MOBILE-3305: Add page to manage downloaded courses storage
parent
16ebd1c72e
commit
671c01a660
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"deletecourse": "Offload all course data",
|
"deletecourse": "Offload all course data",
|
||||||
|
"deletecourses": "Offload all courses data",
|
||||||
"deletedatafrom": "Offload data from {{name}}",
|
"deletedatafrom": "Offload data from {{name}}",
|
||||||
"info": "Files stored on your device make the app work faster and enable the app to be used offline. You can safely offload files if you need to free up storage space.",
|
"info": "Files stored on your device make the app work faster and enable the app to be used offline. You can safely offload files if you need to free up storage space.",
|
||||||
"managestorage": "Manage storage",
|
"managestorage": "Manage storage",
|
||||||
|
|
|
@ -98,7 +98,17 @@ export class AddonStorageManagerCourseStoragePage {
|
||||||
*
|
*
|
||||||
* (This works by deleting data for each module on the course that has data.)
|
* (This works by deleting data for each module on the course that has data.)
|
||||||
*/
|
*/
|
||||||
deleteForCourse(): void {
|
async deleteForCourse(): Promise<void> {
|
||||||
|
try {
|
||||||
|
await this.domUtils.showDeleteConfirm('core.course.confirmdeletemodulefiles');
|
||||||
|
} catch (error) {
|
||||||
|
if (!error.coreCanceled) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const modules = [];
|
const modules = [];
|
||||||
this.sections.forEach((section) => {
|
this.sections.forEach((section) => {
|
||||||
section.modules.forEach((module) => {
|
section.modules.forEach((module) => {
|
||||||
|
@ -118,7 +128,17 @@ export class AddonStorageManagerCourseStoragePage {
|
||||||
*
|
*
|
||||||
* @param section Section object with information about section and modules
|
* @param section Section object with information about section and modules
|
||||||
*/
|
*/
|
||||||
deleteForSection(section: any): void {
|
async deleteForSection(section: any): Promise<void> {
|
||||||
|
try {
|
||||||
|
await this.domUtils.showDeleteConfirm('core.course.confirmdeletemodulefiles');
|
||||||
|
} catch (error) {
|
||||||
|
if (!error.coreCanceled) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const modules = [];
|
const modules = [];
|
||||||
section.modules.forEach((module) => {
|
section.modules.forEach((module) => {
|
||||||
if (module.totalSize > 0) {
|
if (module.totalSize > 0) {
|
||||||
|
@ -134,10 +154,22 @@ export class AddonStorageManagerCourseStoragePage {
|
||||||
*
|
*
|
||||||
* @param module Module details
|
* @param module Module details
|
||||||
*/
|
*/
|
||||||
deleteForModule(module: any): void {
|
async deleteForModule(module: any): Promise<void> {
|
||||||
if (module.totalSize > 0) {
|
if (module.totalSize === 0) {
|
||||||
this.deleteModules([module]);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.domUtils.showDeleteConfirm('core.course.confirmdeletemodulefiles');
|
||||||
|
} catch (error) {
|
||||||
|
if (!error.coreCanceled) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.deleteModules([module]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
<ion-header>
|
||||||
|
<ion-navbar core-back-button>
|
||||||
|
<ion-title>{{ 'addon.storagemanager.managestorage' | translate }}</ion-title>
|
||||||
|
</ion-navbar>
|
||||||
|
</ion-header>
|
||||||
|
<ion-content>
|
||||||
|
<core-loading [hideUntil]="loaded">
|
||||||
|
<ion-card>
|
||||||
|
<ion-card-header>
|
||||||
|
<h1 text-wrap>{{ 'core.courses.courses' | translate }}</h1>
|
||||||
|
<p text-wrap>{{ 'addon.storagemanager.info' | translate }}</p>
|
||||||
|
<ion-item no-padding padding-top class="size" text-wrap>
|
||||||
|
<ion-icon name="cube" item-start></ion-icon>
|
||||||
|
<h2 text-wrap>{{ 'addon.storagemanager.storageused' | translate }}</h2>
|
||||||
|
<div item-end>
|
||||||
|
<p text-end>{{ totalSize | coreBytesToSize }}</p>
|
||||||
|
</div>
|
||||||
|
<button ion-button icon-only item-end no-padding (click)="deleteCompletelyDownloadedCourses()" [disabled]="completelyDownloadedCourses.length === 0">
|
||||||
|
<core-icon name="trash" label="{{ 'addon.storagemanager.deletecourses' | translate }}"></core-icon>
|
||||||
|
</button>
|
||||||
|
</ion-item>
|
||||||
|
</ion-card-header>
|
||||||
|
</ion-card>
|
||||||
|
<ion-card>
|
||||||
|
<ion-list>
|
||||||
|
<ion-item *ngFor="let course of downloadedCourses" class="course">
|
||||||
|
<h2 text-wrap>{{ course.displayname }}</h2>
|
||||||
|
<h3 *ngIf="course.isDownloading">{{ 'core.downloading' | translate }}</h3>
|
||||||
|
<p>
|
||||||
|
<ion-icon name="cube" item-start></ion-icon>
|
||||||
|
{{ course.totalSize | coreBytesToSize }}
|
||||||
|
</p>
|
||||||
|
<button ion-button icon-only item-end (click)="deleteCourse(course)" [disabled]="course.isDownloading">
|
||||||
|
<core-icon name="trash" label="{{ 'addon.storagemanager.deletedatafrom' | translate: { name: course.name } }}"></core-icon>
|
||||||
|
</button>
|
||||||
|
</ion-item>
|
||||||
|
</ion-list>
|
||||||
|
</ion-card>
|
||||||
|
</core-loading>
|
||||||
|
</ion-content>
|
|
@ -0,0 +1,36 @@
|
||||||
|
// (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 { NgModule } from '@angular/core';
|
||||||
|
import { IonicPageModule } from 'ionic-angular';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { CoreComponentsModule } from '@components/components.module';
|
||||||
|
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
|
import { CorePipesModule } from '@pipes/pipes.module';
|
||||||
|
import { AddonStorageManagerCoursesStoragePage } from './courses-storage';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
AddonStorageManagerCoursesStoragePage,
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CoreComponentsModule,
|
||||||
|
CoreDirectivesModule,
|
||||||
|
CorePipesModule,
|
||||||
|
IonicPageModule.forChild(AddonStorageManagerCoursesStoragePage),
|
||||||
|
TranslateModule.forChild()
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonStorageManagerCoursesStoragePageModule {
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
ion-app.app-root page-addon-storagemanager-courses-storage {
|
||||||
|
|
||||||
|
.item-md.item-block .item-inner {
|
||||||
|
padding-right: 0;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ion-item.course {
|
||||||
|
border-bottom: 1px solid $list-border-color;
|
||||||
|
padding-right: 16px;
|
||||||
|
padding-left: 16px;
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
color: $subdued-text-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,218 @@
|
||||||
|
// (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 { Component } from '@angular/core';
|
||||||
|
import { IonicPage } from 'ionic-angular';
|
||||||
|
import { CoreCourse, CoreCourseProvider } from '@core/course/providers/course';
|
||||||
|
import { CoreCourses } from '@core/courses/providers/courses';
|
||||||
|
import { CoreArray } from '@singletons/array';
|
||||||
|
import { CoreCourseModulePrefetch } from '@core/course/providers/module-prefetch-delegate';
|
||||||
|
import { CoreConstants } from '@core/constants';
|
||||||
|
import { CoreDomUtils } from '@providers/utils/dom';
|
||||||
|
import { Translate } from '@singletons/core.singletons';
|
||||||
|
import { CoreEvents, CoreEventsProvider, CoreEventObserver } from '@providers/events';
|
||||||
|
import { CoreCourseHelper } from '@core/course/providers/helper';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Core course data.
|
||||||
|
*/
|
||||||
|
interface Course {
|
||||||
|
id: number;
|
||||||
|
displayname: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Downloaded course data.
|
||||||
|
*/
|
||||||
|
interface DownloadedCourse extends Course {
|
||||||
|
totalSize: number;
|
||||||
|
isDownloading: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page that displays downloaded courses and allows the user to delete them.
|
||||||
|
*/
|
||||||
|
@IonicPage({ segment: 'addon-storagemanager-courses-storage' })
|
||||||
|
@Component({
|
||||||
|
selector: 'page-addon-storagemanager-courses-storage',
|
||||||
|
templateUrl: 'courses-storage.html',
|
||||||
|
})
|
||||||
|
export class AddonStorageManagerCoursesStoragePage {
|
||||||
|
|
||||||
|
userCourses: Course[] = [];
|
||||||
|
downloadedCourses: DownloadedCourse[] = [];
|
||||||
|
completelyDownloadedCourses: DownloadedCourse[] = [];
|
||||||
|
totalSize = 0;
|
||||||
|
loaded = false;
|
||||||
|
|
||||||
|
courseStatusObserver: CoreEventObserver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View loaded.
|
||||||
|
*/
|
||||||
|
async ionViewDidLoad(): Promise<void> {
|
||||||
|
this.userCourses = await CoreCourses.instance.getUserCourses();
|
||||||
|
this.courseStatusObserver = CoreEvents.instance.on(
|
||||||
|
CoreEventsProvider.COURSE_STATUS_CHANGED,
|
||||||
|
({ courseId, status }) => this.onCourseUpdated(courseId, status),
|
||||||
|
);
|
||||||
|
|
||||||
|
const downloadedCourseIds = await CoreCourse.instance.getDownloadedCourseIds();
|
||||||
|
const downloadedCourses = await Promise.all(
|
||||||
|
this.userCourses
|
||||||
|
.filter((course) => downloadedCourseIds.indexOf(course.id) !== -1)
|
||||||
|
.map((course) => this.getDownloadedCourse(course)),
|
||||||
|
);
|
||||||
|
|
||||||
|
this.setDownloadedCourses(downloadedCourses);
|
||||||
|
|
||||||
|
this.loaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component destroyed.
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.courseStatusObserver && this.courseStatusObserver.off();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete all courses that have been downloaded.
|
||||||
|
*/
|
||||||
|
async deleteCompletelyDownloadedCourses(): Promise<void> {
|
||||||
|
try {
|
||||||
|
await CoreDomUtils.instance.showDeleteConfirm('core.course.confirmdeletemodulefiles');
|
||||||
|
} catch (error) {
|
||||||
|
if (!error.coreCanceled) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const modal = CoreDomUtils.instance.showModalLoading();
|
||||||
|
const deletedCourseIds = this.completelyDownloadedCourses.map((course) => course.id);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Promise.all(deletedCourseIds.map((courseId) => CoreCourseHelper.instance.deleteCourseFiles(courseId)));
|
||||||
|
|
||||||
|
this.setDownloadedCourses(this.downloadedCourses.filter((course) => !CoreArray.contains(deletedCourseIds, course.id)));
|
||||||
|
} catch (error) {
|
||||||
|
CoreDomUtils.instance.showErrorModalDefault(error, Translate.instance.instant('core.errordeletefile'));
|
||||||
|
} finally {
|
||||||
|
modal.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete course.
|
||||||
|
*
|
||||||
|
* @param course Course to delete.
|
||||||
|
*/
|
||||||
|
async deleteCourse(course: DownloadedCourse): Promise<void> {
|
||||||
|
try {
|
||||||
|
await CoreDomUtils.instance.showDeleteConfirm('core.course.confirmdeletemodulefiles');
|
||||||
|
} catch (error) {
|
||||||
|
if (!error.coreCanceled) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const modal = CoreDomUtils.instance.showModalLoading();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await CoreCourseHelper.instance.deleteCourseFiles(course.id);
|
||||||
|
|
||||||
|
this.setDownloadedCourses(CoreArray.withoutItem(this.downloadedCourses, course));
|
||||||
|
} catch (error) {
|
||||||
|
CoreDomUtils.instance.showErrorModalDefault(error, Translate.instance.instant('core.errordeletefile'));
|
||||||
|
} finally {
|
||||||
|
modal.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle course updated event.
|
||||||
|
*
|
||||||
|
* @param courseId Updated course id.
|
||||||
|
*/
|
||||||
|
private async onCourseUpdated(courseId: number, status: string): Promise<void> {
|
||||||
|
if (courseId == CoreCourseProvider.ALL_COURSES_CLEARED) {
|
||||||
|
this.setDownloadedCourses([]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const course = this.downloadedCourses.find((course) => course.id === courseId);
|
||||||
|
|
||||||
|
if (!course) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
course.isDownloading = status === CoreConstants.DOWNLOADING;
|
||||||
|
course.totalSize = await this.calculateDownloadedCourseSize(course.id);
|
||||||
|
|
||||||
|
this.setDownloadedCourses(this.downloadedCourses);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set downloaded courses data.
|
||||||
|
*
|
||||||
|
* @param courses Courses info.
|
||||||
|
*/
|
||||||
|
private setDownloadedCourses(courses: DownloadedCourse[]): void {
|
||||||
|
this.downloadedCourses = courses;
|
||||||
|
this.completelyDownloadedCourses = courses.filter((course) => !course.isDownloading);
|
||||||
|
this.totalSize = this.downloadedCourses.reduce((totalSize, course) => totalSize + course.totalSize, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get downloaded course data.
|
||||||
|
*
|
||||||
|
* @param course Course.
|
||||||
|
* @return Course info.
|
||||||
|
*/
|
||||||
|
private async getDownloadedCourse(course: Course): Promise<DownloadedCourse> {
|
||||||
|
const totalSize = await this.calculateDownloadedCourseSize(course.id);
|
||||||
|
const status = await CoreCourse.instance.getCourseStatus(course.id);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...course,
|
||||||
|
totalSize,
|
||||||
|
isDownloading: status === CoreConstants.DOWNLOADING,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the size of a downloaded course.
|
||||||
|
*
|
||||||
|
* @param courseId Downloaded course id.
|
||||||
|
* @return Promise to be resolved with the course size.
|
||||||
|
*/
|
||||||
|
private async calculateDownloadedCourseSize(courseId: number): Promise<number> {
|
||||||
|
const sections = await CoreCourse.instance.getSections(courseId);
|
||||||
|
const modules = CoreArray.flatten(sections.map((section) => section.modules));
|
||||||
|
const promisedModuleSizes = modules.map(async (module) => {
|
||||||
|
const size = await CoreCourseModulePrefetch.instance.getModuleDownloadedSize(module, courseId);
|
||||||
|
|
||||||
|
return isNaN(size) ? 0 : size;
|
||||||
|
});
|
||||||
|
const moduleSizes = await Promise.all(promisedModuleSizes);
|
||||||
|
|
||||||
|
return moduleSizes.reduce((totalSize, moduleSize) => totalSize + moduleSize, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1014,6 +1014,7 @@
|
||||||
"addon.notifications.playsound": "Play sound",
|
"addon.notifications.playsound": "Play sound",
|
||||||
"addon.notifications.therearentnotificationsyet": "There are no notifications.",
|
"addon.notifications.therearentnotificationsyet": "There are no notifications.",
|
||||||
"addon.storagemanager.deletecourse": "Offload all course data",
|
"addon.storagemanager.deletecourse": "Offload all course data",
|
||||||
|
"addon.storagemanager.deletecourses": "Offload all courses data",
|
||||||
"addon.storagemanager.deletedatafrom": "Offload data from {{name}}",
|
"addon.storagemanager.deletedatafrom": "Offload data from {{name}}",
|
||||||
"addon.storagemanager.info": "Files stored on your device make the app work faster and enable the app to be used offline. You can safely offload files if you need to free up storage space.",
|
"addon.storagemanager.info": "Files stored on your device make the app work faster and enable the app to be used offline. You can safely offload files if you need to free up storage space.",
|
||||||
"addon.storagemanager.managestorage": "Manage storage",
|
"addon.storagemanager.managestorage": "Manage storage",
|
||||||
|
|
|
@ -29,6 +29,7 @@ import { CoreSitePluginsProvider } from '@core/siteplugins/providers/siteplugins
|
||||||
import { CoreCourseFormatDelegate } from './format-delegate';
|
import { CoreCourseFormatDelegate } from './format-delegate';
|
||||||
import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications';
|
import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications';
|
||||||
import { CoreCoursesProvider } from '@core/courses/providers/courses';
|
import { CoreCoursesProvider } from '@core/courses/providers/courses';
|
||||||
|
import { makeSingleton } from '@singletons/core.singletons';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service that provides some features regarding a course.
|
* Service that provides some features regarding a course.
|
||||||
|
@ -334,6 +335,23 @@ export class CoreCourseProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain ids of downloaded courses.
|
||||||
|
*
|
||||||
|
* @param siteId Site id.
|
||||||
|
* @return Resolves with an array containing downloaded course ids.
|
||||||
|
*/
|
||||||
|
async getDownloadedCourseIds(siteId?: string): Promise<number[]> {
|
||||||
|
const site = await this.sitesProvider.getSite(siteId);
|
||||||
|
const entries = await site.getDb().getRecordsList(this.COURSE_STATUS_TABLE, 'status', [
|
||||||
|
CoreConstants.DOWNLOADED,
|
||||||
|
CoreConstants.DOWNLOADING,
|
||||||
|
CoreConstants.OUTDATED,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return entries.map((entry) => entry.id);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a module from Moodle.
|
* Get a module from Moodle.
|
||||||
*
|
*
|
||||||
|
@ -1178,3 +1196,5 @@ export type CoreCourseModuleSummary = {
|
||||||
url?: string; // Url.
|
url?: string; // Url.
|
||||||
iconurl: string; // Iconurl.
|
iconurl: string; // Iconurl.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export class CoreCourse extends makeSingleton(CoreCourseProvider) {}
|
||||||
|
|
|
@ -41,6 +41,7 @@ import { CoreLoggerProvider } from '@providers/logger';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
import { CoreFilterHelperProvider } from '@core/filter/providers/helper';
|
import { CoreFilterHelperProvider } from '@core/filter/providers/helper';
|
||||||
import { CoreArray } from '@singletons/array';
|
import { CoreArray } from '@singletons/array';
|
||||||
|
import { makeSingleton } from '@singletons/core.singletons';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prefetch info of a module.
|
* Prefetch info of a module.
|
||||||
|
@ -1627,3 +1628,5 @@ export class CoreCourseHelperProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class CoreCourseHelper extends makeSingleton(CoreCourseHelperProvider) {}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import { Md5 } from 'ts-md5/dist/md5';
|
||||||
import { Subject, BehaviorSubject, Subscription } from 'rxjs';
|
import { Subject, BehaviorSubject, Subscription } from 'rxjs';
|
||||||
import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate';
|
import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate';
|
||||||
import { CoreFileHelperProvider } from '@providers/file-helper';
|
import { CoreFileHelperProvider } from '@providers/file-helper';
|
||||||
|
import { makeSingleton } from '@singletons/core.singletons';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Progress of downloading a list of modules.
|
* Progress of downloading a list of modules.
|
||||||
|
@ -1467,3 +1468,5 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class CoreCourseModulePrefetch extends makeSingleton(CoreCourseModulePrefetchDelegate) {}
|
||||||
|
|
|
@ -162,6 +162,16 @@ export class CoreCoursesCourseProgressComponent implements OnInit, OnDestroy {
|
||||||
* Delete the course.
|
* Delete the course.
|
||||||
*/
|
*/
|
||||||
async deleteCourse(): Promise<void> {
|
async deleteCourse(): Promise<void> {
|
||||||
|
try {
|
||||||
|
await this.domUtils.showDeleteConfirm('core.course.confirmdeletemodulefiles');
|
||||||
|
} catch (error) {
|
||||||
|
if (!error.coreCanceled) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const modal = this.domUtils.showModalLoading();
|
const modal = this.domUtils.showModalLoading();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
<ion-icon name="search"></ion-icon>
|
<ion-icon name="search"></ion-icon>
|
||||||
</button>
|
</button>
|
||||||
<core-context-menu>
|
<core-context-menu>
|
||||||
|
<core-context-menu-item *ngIf="(downloadCourseEnabled || downloadCoursesEnabled)" [priority]="500" [content]="'addon.storagemanager.managestorage' | translate" (action)="manageCoursesStorage()" iconAction="cube"></core-context-menu-item>
|
||||||
|
|
||||||
<!-- Action for dashboard and site home. -->
|
<!-- Action for dashboard and site home. -->
|
||||||
<core-context-menu-item *ngIf="(siteHomeEnabled || dashboardEnabled) && (downloadCourseEnabled || downloadCoursesEnabled)" [priority]="1000" [content]="'core.settings.showdownloadoptions' | translate" (action)="toggleDownload()" [iconAction]="downloadEnabledIcon"></core-context-menu-item>
|
<core-context-menu-item *ngIf="(siteHomeEnabled || dashboardEnabled) && (downloadCourseEnabled || downloadCoursesEnabled)" [priority]="1000" [content]="'core.settings.showdownloadoptions' | translate" (action)="toggleDownload()" [iconAction]="downloadEnabledIcon"></core-context-menu-item>
|
||||||
|
|
||||||
|
|
|
@ -122,6 +122,13 @@ export class CoreCoursesDashboardPage implements OnDestroy {
|
||||||
this.tabsComponent && this.tabsComponent.ionViewDidLeave();
|
this.tabsComponent && this.tabsComponent.ionViewDidLeave();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open page to manage courses storage.
|
||||||
|
*/
|
||||||
|
manageCoursesStorage(): void {
|
||||||
|
this.navCtrl.push('AddonStorageManagerCoursesStoragePage');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Go to search courses.
|
* Go to search courses.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -17,6 +17,7 @@ import { CoreEventsProvider } from '@providers/events';
|
||||||
import { CoreLoggerProvider } from '@providers/logger';
|
import { CoreLoggerProvider } from '@providers/logger';
|
||||||
import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites';
|
import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites';
|
||||||
import { CoreSite } from '@classes/site';
|
import { CoreSite } from '@classes/site';
|
||||||
|
import { makeSingleton } from '@singletons/core.singletons';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data sent to the EVENT_MY_COURSES_UPDATED.
|
* Data sent to the EVENT_MY_COURSES_UPDATED.
|
||||||
|
@ -1152,3 +1153,5 @@ export class CoreCoursesProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class CoreCourses extends makeSingleton(CoreCoursesProvider) {}
|
||||||
|
|
|
@ -17,6 +17,17 @@
|
||||||
*/
|
*/
|
||||||
export class CoreArray {
|
export class CoreArray {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether an array contains an item.
|
||||||
|
*
|
||||||
|
* @param arr Array.
|
||||||
|
* @param item Item.
|
||||||
|
* @return Whether item is within the array.
|
||||||
|
*/
|
||||||
|
static contains<T>(arr: T[], item: T): boolean {
|
||||||
|
return arr.indexOf(item) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flatten the first dimension of a multi-dimensional array.
|
* Flatten the first dimension of a multi-dimensional array.
|
||||||
*
|
*
|
||||||
|
@ -33,4 +44,22 @@ export class CoreArray {
|
||||||
return [].concat(...arr);
|
return [].concat(...arr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain a new array without the specified item.
|
||||||
|
*
|
||||||
|
* @param arr Array.
|
||||||
|
* @param item Item to remove.
|
||||||
|
* @return Array without the specified item.
|
||||||
|
*/
|
||||||
|
static withoutItem<T>(arr: T[], item: T): T[] {
|
||||||
|
const newArray = [...arr];
|
||||||
|
const index = arr.indexOf(item);
|
||||||
|
|
||||||
|
if (index !== -1) {
|
||||||
|
newArray.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newArray;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue