MOBILE-2322 files: Implement list page

main
Dani Palou 2018-01-24 11:15:54 +01:00
parent 55393cd1ee
commit 052208dac7
4 changed files with 293 additions and 1 deletions

View File

@ -0,0 +1,44 @@
<ion-header>
<ion-navbar>
<ion-title><core-format-text [text]="title"></core-format-text></ion-title>
</ion-navbar>
</ion-header>
<ion-content>
<ion-refresher [enabled]="filesLoaded && (showPrivateFiles || showSiteFiles)" (ionRefresh)="refreshData($event)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
</ion-refresher>
<core-loading [hideUntil]="filesLoaded" *ngIf="showPrivateFiles || showSiteFiles">
<!-- Allow selecting the files to see: private or site. -->
<div no-padding *ngIf="showPrivateFiles && showSiteFiles && !path">
<ion-select [(ngModel)]="root" (ngModelChange)="rootChanged()" interface="popover">
<ion-option value="my">{{ 'addon.files.privatefiles' | translate }}</ion-option>
<ion-option value="site">{{ 'addon.files.sitefiles' | translate }}</ion-option>
</ion-select>
</div>
<!-- Display info about space used and space left. -->
<p class="core-info-card" *ngIf="userQuota && filesInfo && filesInfo.filecount > 0">{{ 'core.quotausage' | translate:{$a: {used: spaceUsed, total: userQuotaReadable} } }}</p>
<!-- List of files. -->
<ion-list *ngIf="files && files.length > 0">
<div *ngFor="let file of files">
<a *ngIf="file.isdir" ion-item class="item-media" [navPush]="'AddonFilesListPage'" [navParams]="{path: file.link, title: file.filename}">
<img [src]="file.imgPath" alt="" role="presentation" item-start>
<p>{{file.filename}}</p>
</a>
<core-file *ngIf="!file.isdir" [file]="file" [component]="component" [componentId]="file.contextid"></core-file>
</div>
</ion-list>
<!-- Message telling there are no files. -->
<core-empty-box *ngIf="!files || !files.length" icon="folder" [message]="'addon.files.emptyfilelist' | translate"></core-empty-box>
</core-loading>
<!-- Upload a private file. -->
<ion-fab bottom right *ngIf="showUpload && root != 'site' && !path">
<button ion-fab (click)="uploadFile()" [attr.aria-label]="'core.fileuploader.uploadafile' | translate">
<ion-icon name="add"></ion-icon>
</button>
</ion-fab>
</ion-content>

View File

@ -0,0 +1,33 @@
// (C) Copyright 2015 Martin Dougiamas
//
// 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 { AddonFilesListPage } from './list';
@NgModule({
declarations: [
AddonFilesListPage,
],
imports: [
CoreComponentsModule,
CoreDirectivesModule,
IonicPageModule.forChild(AddonFilesListPage),
TranslateModule.forChild()
],
})
export class AddonFilesListPageModule {}

View File

@ -0,0 +1,215 @@
// (C) Copyright 2015 Martin Dougiamas
//
// 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, ViewChild, OnDestroy } from '@angular/core';
import { IonicPage, NavParams, NavController } from 'ionic-angular';
import { TranslateService } from '@ngx-translate/core';
import { CoreAppProvider } from '../../../../providers/app';
import { CoreEventsProvider } from '../../../../providers/events';
import { CoreSitesProvider } from '../../../../providers/sites';
import { CoreDomUtilsProvider } from '../../../../providers/utils/dom';
import { CoreTextUtilsProvider } from '../../../../providers/utils/text';
import { AddonFilesProvider } from '../../providers/files';
import { AddonFilesHelperProvider } from '../../providers/helper';
/**
* Page that displays the list of files.
*/
@IonicPage({ segment: 'addon-files-list' })
@Component({
selector: 'page-addon-files-list',
templateUrl: 'list.html',
})
export class AddonFilesListPage implements OnDestroy {
title: string; // Page title.
showPrivateFiles: boolean; // Whether the user can view private files.
showSiteFiles: boolean; // Whether the user can view site files.
showUpload: boolean; // Whether the user can upload files.
root: string; // The root of the files loaded: 'my' or 'site'.
path: string; // The path of the directory being loaded. If empty path it means the root is being loaded.
userQuota: number; // The user quota (in bytes).
filesInfo: any; // Info about private files (size, number of files, etc.).
spaceUsed: string; // Space used in a readable format.
userQuotaReadable: string; // User quota in a readable format.
files: any[]; // List of files.
component: string; // Component to link the file downloads to.
filesLoaded: boolean; // Whether the files are loaded.
protected updateSiteObserver;
constructor(navParams: NavParams, eventsProvider: CoreEventsProvider, private sitesProvider: CoreSitesProvider,
private domUtils: CoreDomUtilsProvider, private translate: TranslateService, private appProvider: CoreAppProvider,
private filesProvider: AddonFilesProvider, private filesHelper: AddonFilesHelperProvider,
private textUtils: CoreTextUtilsProvider) {
this.title = navParams.get('title') || this.translate.instant('addon.files.files');
this.root = navParams.get('root');
this.path = navParams.get('path');
// Update visibility if current site info is updated.
this.updateSiteObserver = eventsProvider.on(CoreEventsProvider.SITE_UPDATED, () => {
this.setVisibility();
}, sitesProvider.getCurrentSiteId());
}
/**
* View loaded.
*/
ionViewDidLoad(): void {
this.setVisibility();
this.userQuota = this.sitesProvider.getCurrentSite().getInfo().userquota;
if (!this.root) {
// Load private files by default.
if (this.showPrivateFiles) {
this.root = 'my';
} else if (this.showSiteFiles) {
this.root = 'site';
}
}
if (this.root) {
this.rootChanged();
} else {
this.filesLoaded = true;
}
}
/**
* Refresh the data.
*
* @param {any} refresher Refresher.
*/
refreshData(refresher: any): void {
this.refreshFiles().finally(() => {
refresher.complete();
});
}
/**
* Function called when the root has changed.
*/
rootChanged(): void {
this.filesLoaded = false;
this.component = this.root == 'my' ? AddonFilesProvider.PRIVATE_FILES_COMPONENT : AddonFilesProvider.SITE_FILES_COMPONENT;
this.fetchFiles().finally(() => {
this.filesLoaded = true;
});
}
/**
* Upload a new file.
*/
uploadFile(): void {
this.filesProvider.versionCanUploadFiles().then((canUpload) => {
if (!canUpload) {
this.domUtils.showAlertTranslated('core.notice', 'addon.files.erroruploadnotworking');
} else if (!this.appProvider.isOnline()) {
this.domUtils.showErrorModal('core.fileuploader.errormustbeonlinetoupload', true);
} else {
this.filesHelper.uploadPrivateFile(this.filesInfo).then(() => {
// File uploaded, refresh the list.
this.filesLoaded = false;
this.refreshFiles().finally(() => {
this.filesLoaded = true;
});
}).catch(() => {
// Ignore errors, they're handled inside the function.
});
}
});
}
/**
* Set visibility of some items based on site data.
*/
protected setVisibility(): void {
this.showPrivateFiles = this.filesProvider.canViewPrivateFiles();
this.showSiteFiles = this.filesProvider.canViewSiteFiles();
this.showUpload = this.filesProvider.canUploadFiles();
}
/**
* Fetch the files.
*
* @return {Promise<any>} Promise resolved when done.
*/
protected fetchFiles(): Promise<any> {
let promise;
if (!this.path) {
// The path is unknown, the user must be requesting a root.
if (this.root == 'site') {
this.title = this.translate.instant('addon.files.sitefiles');
promise = this.filesProvider.getSiteFiles();
} else if (this.root == 'my') {
this.title = this.translate.instant('addon.files.files');
promise = this.filesProvider.getPrivateFiles().then((files) => {
if (this.showUpload && this.filesProvider.canGetPrivateFilesInfo() && this.userQuota > 0) {
// Get the info to calculate the available size.
return this.filesProvider.getPrivateFilesInfo().then((info) => {
this.filesInfo = info;
this.spaceUsed = this.textUtils.bytesToSize(info.filesizewithoutreferences, 1);
this.userQuotaReadable = this.textUtils.bytesToSize(this.userQuota, 1);
return files;
});
} else {
// User quota isn't useful, delete it.
delete this.userQuota;
}
return files;
});
} else {
// Unknown root.
promise = Promise.reject(null);
}
} else {
// Path is set, serve the files the user requested.
promise = this.filesProvider.getFiles(this.path);
}
return promise.then((files) => {
this.files = files;
}).catch((error) => {
this.domUtils.showErrorModalDefault(error, 'addon.files.couldnotloadfiles', true);
});
}
/**
* Refresh the displayed files.
*
* @return {Promise<any>} Promise resolved when done.
*/
protected refreshFiles(): Promise<any> {
const promises = [];
promises.push(this.filesProvider.invalidateDirectory(this.root, this.path));
promises.push(this.filesProvider.invalidatePrivateFilesInfoForUser());
return Promise.all(promises).finally(() => {
return this.fetchFiles();
});
}
/**
* Page destroyed.
*/
ngOnDestroy(): void {
this.updateSiteObserver && this.updateSiteObserver.off();
}
}

View File

@ -204,7 +204,7 @@ export class CoreUtilsProvider {
}
return newArray;
} else if (typeof source == 'object') {
} else if (typeof source == 'object' && source !== null) {
// Clone the object and all the subproperties.
const newObject = {};
for (const name in source) {