MOBILE-3630 sharedfiled: Implement components and pages
parent
92f5742351
commit
2302638702
|
@ -137,7 +137,7 @@ export abstract class CorePageItemsListManager<Item> {
|
|||
// If this item is already selected, do nothing.
|
||||
const itemRoute = this.getItemRoute(route);
|
||||
const itemPath = this.getItemPath(item);
|
||||
const selectedItemPath = itemRoute ? this.getSelectedItemPath(itemRoute.snapshot) : null;
|
||||
const selectedItemPath = itemRoute?.snapshot ? this.getSelectedItemPath(itemRoute.snapshot) : null;
|
||||
|
||||
if (selectedItemPath === itemPath) {
|
||||
return;
|
||||
|
|
|
@ -52,6 +52,7 @@ import { CoreAttachmentsComponent } from './attachments/attachments';
|
|||
import { CoreFilesComponent } from './files/files';
|
||||
import { CoreLocalFileComponent } from './local-file/local-file';
|
||||
import { CoreBSTooltipComponent } from './bs-tooltip/bs-tooltip';
|
||||
import { CoreSitePickerComponent } from './site-picker/site-picker';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
@ -86,6 +87,7 @@ import { CoreBSTooltipComponent } from './bs-tooltip/bs-tooltip';
|
|||
CoreFilesComponent,
|
||||
CoreLocalFileComponent,
|
||||
CoreBSTooltipComponent,
|
||||
CoreSitePickerComponent,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
|
@ -127,6 +129,7 @@ import { CoreBSTooltipComponent } from './bs-tooltip/bs-tooltip';
|
|||
CoreFilesComponent,
|
||||
CoreLocalFileComponent,
|
||||
CoreBSTooltipComponent,
|
||||
CoreSitePickerComponent,
|
||||
],
|
||||
})
|
||||
export class CoreComponentsModule {}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<ion-item *ngIf="sites && sites.length">
|
||||
<ion-label>{{ 'core.site' | translate }}</ion-label>
|
||||
<ion-select [(ngModel)]="selectedSite" (ngModelChange)="siteSelected.emit(selectedSite)" interface="action-sheet">
|
||||
<ion-select-option *ngFor="let site of sites" [value]="site.id">{{ site.fullNameAndSiteName }}</ion-select-option>
|
||||
</ion-select>
|
||||
</ion-item>
|
|
@ -0,0 +1,77 @@
|
|||
// (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, Input, Output, EventEmitter, OnInit } from '@angular/core';
|
||||
|
||||
import { CoreFilter } from '@features/filter/services/filter';
|
||||
import { CoreSiteBasicInfo, CoreSites } from '@services/sites';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
import { Translate } from '@singletons';
|
||||
|
||||
/**
|
||||
* Component to display a site selector. It will display a select with the list of sites. If the selected site changes,
|
||||
* an output will be emitted with the site ID.
|
||||
*
|
||||
* Example usage:
|
||||
* <core-site-picker (siteSelected)="changeSite($event)"></core-site-picker>
|
||||
*/
|
||||
@Component({
|
||||
selector: 'core-site-picker',
|
||||
templateUrl: 'core-site-picker.html',
|
||||
})
|
||||
export class CoreSitePickerComponent implements OnInit {
|
||||
|
||||
@Input() initialSite?: string; // Initial site. If not provided, current site.
|
||||
@Output() siteSelected = new EventEmitter<string>(); // Emit an event when a site is selected. Sends the siteId as parameter.
|
||||
|
||||
selectedSite?: string;
|
||||
sites?: SiteInfo[];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async ngOnInit(): Promise<void> {
|
||||
this.selectedSite = this.initialSite || CoreSites.getCurrentSiteId();
|
||||
|
||||
// Load the sites.
|
||||
const sites = await CoreSites.getSites();
|
||||
|
||||
if (!this.selectedSite && sites.length) {
|
||||
// There is no current site, select the first one.
|
||||
this.selectedSite = sites[0].id;
|
||||
this.siteSelected.emit(this.selectedSite);
|
||||
}
|
||||
|
||||
await Promise.all(sites.map(async (site: SiteInfo) => {
|
||||
// Format the site name.
|
||||
const options = { clean: true, singleLine: true, filter: false };
|
||||
const siteName = await CoreUtils.ignoreErrors(
|
||||
CoreFilter.formatText(site.siteName || '', options, [], site.id),
|
||||
site.siteName || '',
|
||||
);
|
||||
|
||||
site.fullNameAndSiteName = Translate.instant(
|
||||
'core.fullnameandsitename',
|
||||
{ fullname: site.fullName, sitename: siteName },
|
||||
);
|
||||
}));
|
||||
|
||||
this.sites = sites;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type SiteInfo = CoreSiteBasicInfo & {
|
||||
fullNameAndSiteName?: string;
|
||||
};
|
|
@ -376,16 +376,14 @@ export class CoreFileUploaderHelperProvider {
|
|||
try {
|
||||
const data = await handler.action(maxSize, upload, allowOffline, handler.mimetypes);
|
||||
|
||||
let result: CoreWSUploadFileResult | FileEntry | undefined;
|
||||
|
||||
if (data.treated) {
|
||||
// The handler already treated the file. Return the result.
|
||||
this.fileUploaded(data.result!);
|
||||
|
||||
return true;
|
||||
result = data.result;
|
||||
} else if (data.fileEntry) {
|
||||
// The handler provided us a fileEntry, use it.
|
||||
await this.uploadFileEntry(data.fileEntry, !!data.delete, maxSize, upload, allowOffline);
|
||||
|
||||
return true;
|
||||
result = await this.uploadFileEntry(data.fileEntry, !!data.delete, maxSize, upload, allowOffline);
|
||||
} else if (data.path) {
|
||||
let fileEntry: FileEntry;
|
||||
|
||||
|
@ -398,13 +396,17 @@ export class CoreFileUploaderHelperProvider {
|
|||
}
|
||||
|
||||
// File found, treat it.
|
||||
await this.uploadFileEntry(fileEntry, !!data.delete, maxSize, upload, allowOffline);
|
||||
|
||||
return true;
|
||||
result = await this.uploadFileEntry(fileEntry, !!data.delete, maxSize, upload, allowOffline);
|
||||
}
|
||||
|
||||
// Nothing received, fail.
|
||||
throw new CoreError('No file received');
|
||||
if (!result) {
|
||||
// Nothing received, fail.
|
||||
throw new CoreError('No file received');
|
||||
}
|
||||
|
||||
this.fileUploaded(result);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
CoreDomUtils.showErrorModalDefault(
|
||||
error,
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
// (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 { CoreConstants } from '@/core/constants';
|
||||
|
||||
/**
|
||||
* Settings section.
|
||||
*/
|
||||
export type CoreSettingsSection = {
|
||||
name: string;
|
||||
path: string;
|
||||
icon: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Settings constants.
|
||||
*/
|
||||
export class CoreSettingsConstants {
|
||||
|
||||
static readonly SECTIONS: CoreSettingsSection[] = [
|
||||
{
|
||||
name: 'general',
|
||||
path: 'general',
|
||||
icon: 'fas-wrench',
|
||||
},
|
||||
{
|
||||
name: 'spaceusage',
|
||||
path: 'spaceusage',
|
||||
icon: 'fas-tasks',
|
||||
},
|
||||
{
|
||||
name: 'synchronization',
|
||||
path: 'sync',
|
||||
icon: CoreConstants.ICON_SYNC,
|
||||
},
|
||||
// @TODO sharedfiles
|
||||
{
|
||||
name: 'about',
|
||||
path: 'about',
|
||||
icon: 'fas-id-card',
|
||||
},
|
||||
];
|
||||
|
||||
}
|
|
@ -10,15 +10,10 @@
|
|||
<ion-content>
|
||||
<core-split-view>
|
||||
<ion-list>
|
||||
<ion-item
|
||||
*ngFor="let section of sections.items"
|
||||
[class.core-selected-item]="sections.isSelected(section)"
|
||||
button
|
||||
detail
|
||||
(click)="sections.select(section)"
|
||||
>
|
||||
<ion-item *ngFor="let section of sections.items" [class.core-selected-item]="sections.isSelected(section)" button
|
||||
detail="true" (click)="sections.select(section)">
|
||||
<ion-icon [name]="section.icon" slot="start"></ion-icon>
|
||||
<ion-label>{{ 'core.settings.' + section.name | translate }}</ion-label>
|
||||
<ion-label>{{ section.name | translate }}</ion-label>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</core-split-view>
|
||||
|
|
|
@ -13,10 +13,14 @@
|
|||
// limitations under the License.
|
||||
|
||||
import { AfterViewInit, Component, OnDestroy, ViewChild } from '@angular/core';
|
||||
import { CoreSettingsConstants, CoreSettingsSection } from '@features/settings/constants';
|
||||
import { ActivatedRouteSnapshot, Params } from '@angular/router';
|
||||
|
||||
import { CorePageItemsListManager } from '@classes/page-items-list-manager';
|
||||
import { ActivatedRouteSnapshot } from '@angular/router';
|
||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||
import { CoreSettingsHelper } from '@features/settings/services/settings-helper';
|
||||
import { CoreConstants } from '@/core/constants';
|
||||
import { SHAREDFILES_PAGE_NAME } from '@features/sharedfiles/sharedfiles.module';
|
||||
import { CoreApp } from '@services/app';
|
||||
|
||||
@Component({
|
||||
selector: 'page-core-settings-index',
|
||||
|
@ -32,7 +36,7 @@ export class CoreSettingsIndexPage implements AfterViewInit, OnDestroy {
|
|||
* @inheritdoc
|
||||
*/
|
||||
ngAfterViewInit(): void {
|
||||
this.sections.setItems(CoreSettingsConstants.SECTIONS);
|
||||
this.sections.setItems(this.getSections());
|
||||
this.sections.start(this.splitView);
|
||||
}
|
||||
|
||||
|
@ -43,6 +47,48 @@ export class CoreSettingsIndexPage implements AfterViewInit, OnDestroy {
|
|||
this.sections.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sections.
|
||||
*
|
||||
* @returns Sections.
|
||||
*/
|
||||
protected getSections(): CoreSettingsSection[] {
|
||||
const sections: CoreSettingsSection[] = [
|
||||
{
|
||||
name: 'core.settings.general',
|
||||
path: 'general',
|
||||
icon: 'fas-wrench',
|
||||
},
|
||||
{
|
||||
name: 'core.settings.spaceusage',
|
||||
path: 'spaceusage',
|
||||
icon: 'fas-tasks',
|
||||
},
|
||||
{
|
||||
name: 'core.settings.synchronization',
|
||||
path: 'sync',
|
||||
icon: CoreConstants.ICON_SYNC,
|
||||
},
|
||||
];
|
||||
|
||||
if (CoreApp.isIOS()) {
|
||||
sections.push({
|
||||
name: 'core.sharedfiles.sharedfiles',
|
||||
path: SHAREDFILES_PAGE_NAME + '/list/root',
|
||||
icon: 'fas-folder',
|
||||
params: { manage: true },
|
||||
});
|
||||
}
|
||||
|
||||
sections.push({
|
||||
name: 'core.settings.about',
|
||||
path: 'about',
|
||||
icon: 'fas-id-card',
|
||||
});
|
||||
|
||||
return sections;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -57,11 +103,28 @@ class CoreSettingsSectionsManager extends CorePageItemsListManager<CoreSettingsS
|
|||
return section.path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected getItemQueryParams(section: CoreSettingsSection): Params {
|
||||
return section.params || {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected getSelectedItemPath(route: ActivatedRouteSnapshot): string | null {
|
||||
return route.parent?.routeConfig?.path ?? null;
|
||||
return CoreSettingsHelper.getSelectedItemPath(route);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Settings section.
|
||||
*/
|
||||
export type CoreSettingsSection = {
|
||||
name: string;
|
||||
path: string;
|
||||
icon: string;
|
||||
params?: Params;
|
||||
};
|
||||
|
|
|
@ -26,19 +26,9 @@
|
|||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item-divider><ion-label></ion-label></ion-item-divider>
|
||||
<!-- <ion-item *ngIf="isIOS"
|
||||
(click)="openHandler('CoreSharedFilesListPage', {manage: true, siteId: siteId, hideSitePicker: true})"
|
||||
[title]="'core.sharedfiles.sharedfiles' | translate"
|
||||
[class.core-selected-item]="'CoreSharedFilesListPage' == selectedPage" detail>
|
||||
<ion-icon name="fas-folder" slot="start"></ion-icon>
|
||||
<ion-label>
|
||||
<h2>{{ 'core.sharedfiles.sharedfiles' | translate }}</h2>
|
||||
</ion-label>
|
||||
<ion-badge slot="end">{{ iosSharedFiles }}</ion-badge>
|
||||
</ion-item> -->
|
||||
|
||||
<ion-item *ngFor="let handler of handlers.items" [ngClass]="['core-settings-handler', handler.class]"
|
||||
[title]="handler.title | translate" detail="true" (click)="handlers.select(handler)"
|
||||
[title]="handler.title | translate" detail="true" (click)="handlers.select(handler)" button
|
||||
[class.core-selected-item]="handlers.isSelected(handler)">
|
||||
<ion-icon [name]="handler.icon" slot="start" *ngIf="handler.icon">
|
||||
</ion-icon>
|
||||
|
|
|
@ -20,7 +20,6 @@ import { CoreSettingsDelegate, CoreSettingsHandlerToDisplay } from '../../servic
|
|||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
// import { CoreSharedFiles } from '@features/sharedfiles/services/sharedfiles';
|
||||
import { CoreSettingsHelper, CoreSiteSpaceUsage } from '../../services/settings-helper';
|
||||
import { CoreApp } from '@services/app';
|
||||
import { CoreSiteInfo } from '@classes/site';
|
||||
|
@ -52,7 +51,6 @@ export class CoreSitePreferencesPage implements AfterViewInit, OnDestroy {
|
|||
spaceUsage: 0,
|
||||
};
|
||||
|
||||
iosSharedFiles = 0;
|
||||
protected sitesObserver: CoreEventObserver;
|
||||
protected isDestroyed = false;
|
||||
|
||||
|
@ -101,25 +99,7 @@ export class CoreSitePreferencesPage implements AfterViewInit, OnDestroy {
|
|||
this.siteName = currentSite!.getSiteName();
|
||||
this.siteUrl = currentSite!.getURL();
|
||||
|
||||
const promises: Promise<void>[] = [];
|
||||
|
||||
promises.push(CoreSettingsHelper.getSiteSpaceUsage(this.siteId)
|
||||
.then((spaceUsage) => {
|
||||
this.spaceUsage = spaceUsage;
|
||||
|
||||
return;
|
||||
}));
|
||||
|
||||
/* if (this.isIOS) {
|
||||
promises.push(CoreSharedFiles.getSiteSharedFiles(this.siteId)
|
||||
.then((files) => {
|
||||
this.iosSharedFiles = files.length;
|
||||
|
||||
return;
|
||||
}));
|
||||
}*/
|
||||
|
||||
await Promise.all(promises);
|
||||
this.spaceUsage = await CoreSettingsHelper.getSiteSpaceUsage(this.siteId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -224,9 +204,7 @@ class CoreSettingsSitePreferencesManager extends CorePageItemsListManager<CoreSe
|
|||
* @inheritdoc
|
||||
*/
|
||||
protected getSelectedItemPath(route: ActivatedRouteSnapshot): string | null {
|
||||
// @todo: routeConfig doesn't have a path after refreshing the app.
|
||||
// route.component is null too, and route.parent.url is empty.
|
||||
return route.parent?.routeConfig?.path ?? null;
|
||||
return CoreSettingsHelper.getSelectedItemPath(route);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,6 +27,9 @@ import { CoreDomUtils } from '@services/utils/dom';
|
|||
import { CoreCourse } from '@features/course/services/course';
|
||||
import { makeSingleton, Translate } from '@singletons';
|
||||
import { CoreError } from '@classes/errors/error';
|
||||
import { ActivatedRouteSnapshot } from '@angular/router';
|
||||
import { CoreNavigator } from '@services/navigator';
|
||||
import { CoreTextUtils } from '@services/utils/text';
|
||||
|
||||
/**
|
||||
* Object with space usage and cache entries that can be erased.
|
||||
|
@ -438,6 +441,29 @@ export class CoreSettingsHelperProvider {
|
|||
document.body.classList.toggle('dark', enable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of getSelectedItemPath for settings items managers.
|
||||
*
|
||||
* @param route Current route.
|
||||
* @return Path of the selected item in the given route.
|
||||
*/
|
||||
getSelectedItemPath(route: ActivatedRouteSnapshot): string | null {
|
||||
// @todo: routeConfig doesn't have a path after refreshing the app.
|
||||
// route.component is null too, and route.parent.url is empty.
|
||||
let routePath = route.routeConfig?.path;
|
||||
const parentPath = route.parent?.routeConfig?.path;
|
||||
|
||||
if (!routePath && !parentPath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (routePath) {
|
||||
routePath = CoreNavigator.replaceRoutePathParams(routePath, route.params);
|
||||
}
|
||||
|
||||
return CoreTextUtils.concatenatePaths(parentPath || '', routePath || '');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const CoreSettingsHelper = makeSingleton(CoreSettingsHelperProvider);
|
||||
|
|
|
@ -20,6 +20,7 @@ import { CoreSharedModule } from '@/core/shared.module';
|
|||
import { CoreScreen } from '@services/screen';
|
||||
|
||||
import { CoreSettingsIndexPage } from './pages/index';
|
||||
import { SHAREDFILES_PAGE_NAME } from '@features/sharedfiles/sharedfiles.module';
|
||||
|
||||
const sectionRoutes: Routes = [
|
||||
{
|
||||
|
@ -36,7 +37,10 @@ const sectionRoutes: Routes = [
|
|||
import('./pages/synchronization/synchronization.module')
|
||||
.then(m => m.CoreSettingsSynchronizationPageModule),
|
||||
},
|
||||
// @todo sharedfiles
|
||||
{
|
||||
path: SHAREDFILES_PAGE_NAME,
|
||||
loadChildren: () => import('@features/sharedfiles/sharedfiles-lazy.module').then(m => m.CoreSharedFilesLazyModule),
|
||||
},
|
||||
{
|
||||
path: 'about',
|
||||
loadChildren: () => import('./pages/about/about.module').then(m => m.CoreSettingsAboutPageModule),
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
// (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 { CoreSharedModule } from '@/core/shared.module';
|
||||
import { CoreSharedFilesListComponent } from './list/list';
|
||||
import { CoreSharedFilesListModalComponent } from './list-modal/list-modal';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
CoreSharedFilesListComponent,
|
||||
CoreSharedFilesListModalComponent,
|
||||
],
|
||||
imports: [
|
||||
CoreSharedModule,
|
||||
],
|
||||
exports: [
|
||||
CoreSharedFilesListComponent,
|
||||
CoreSharedFilesListModalComponent,
|
||||
],
|
||||
})
|
||||
export class CoreSharedFilesComponentsModule {}
|
|
@ -0,0 +1,19 @@
|
|||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title>{{ title }}</ion-title>
|
||||
|
||||
<ion-buttons slot="end">
|
||||
<ion-button (click)="closeModal()" [attr.aria-label]="'core.close' | translate">
|
||||
<ion-icon name="fas-times" slot="icon-only"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<core-shared-files-list [siteId]="siteId" [mimetypes]="mimetypes" [isModal]="true" [manage]="manage" [pick]="pick"
|
||||
[path]="path" [showSitePicker]="showSitePicker" (onPathChanged)="calculateTitle($event)" (onFilePicked)="filePicked($event)">
|
||||
</core-shared-files-list>
|
||||
</ion-content>
|
|
@ -0,0 +1,75 @@
|
|||
// (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, OnInit, Input } from '@angular/core';
|
||||
import { FileEntry } from '@ionic-native/file';
|
||||
|
||||
import { CoreFile } from '@services/file';
|
||||
import { ModalController, Translate } from '@singletons';
|
||||
|
||||
/**
|
||||
* Modal to display the list of shared files.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'core-shared-files-list-modal',
|
||||
templateUrl: 'list-modal.html',
|
||||
})
|
||||
export class CoreSharedFilesListModalComponent implements OnInit {
|
||||
|
||||
@Input() siteId?: string;
|
||||
@Input() mimetypes?: string[];
|
||||
@Input() manage?: boolean;
|
||||
@Input() pick?: boolean; // To pick a file you MUST use a modal.
|
||||
@Input() path?: string;
|
||||
@Input() showSitePicker?: boolean;
|
||||
|
||||
title?: string;
|
||||
|
||||
/**
|
||||
* Component being initialized.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
this.calculateTitle(this.path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the title.
|
||||
*
|
||||
* @param path Path to use.
|
||||
*/
|
||||
calculateTitle(path?: string): void {
|
||||
if (path) {
|
||||
this.title = CoreFile.getFileAndDirectoryFromPath(path).name;
|
||||
} else {
|
||||
this.title = Translate.instant('core.sharedfiles.sharedfiles');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close modal.
|
||||
*/
|
||||
closeModal(): void {
|
||||
ModalController.dismiss();
|
||||
}
|
||||
|
||||
/**
|
||||
* A file was picked.
|
||||
*
|
||||
* @param file Picked file.
|
||||
*/
|
||||
filePicked(file: FileEntry): void {
|
||||
ModalController.dismiss(file);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<ion-refresher slot="fixed" [disabled]="!filesLoaded" (ionRefresh)="refreshFiles($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
|
||||
<!-- Allow selecting the site to view. -->
|
||||
<core-site-picker *ngIf="showSitePicker" [hidden]="!filesLoaded" [initialSite]="siteId" (siteSelected)="changeSite($event)">
|
||||
</core-site-picker>
|
||||
|
||||
<core-loading [hideUntil]="filesLoaded" class="core-loading-center">
|
||||
<ion-list *ngIf="files && files.length > 0">
|
||||
<ng-container *ngFor="let file of files; let idx = index">
|
||||
<core-local-file *ngIf="file.isFile" [file]="file" [manage]="manage" [overrideClick]="pick"
|
||||
(onClick)="filePicked(file)" (onDelete)="fileDeleted(idx)" (onRename)="fileRenamed(idx, $event)">
|
||||
</core-local-file>
|
||||
|
||||
<ion-item button *ngIf="!file.isFile" class="ion-text-wrap item-file" (click)="openFolder(file)">
|
||||
<ion-thumbnail slot="start" aria-hidden="true">
|
||||
<img src="assets/img/files/folder-64.png" alt="">
|
||||
</ion-thumbnail>
|
||||
<ion-label>{{ file.name }}</ion-label>
|
||||
</ion-item>
|
||||
</ng-container>
|
||||
</ion-list>
|
||||
|
||||
<core-empty-box *ngIf="files && !files.length && manage" icon="fas-folder"
|
||||
[message]="'core.sharedfiles.nosharedfiles' | translate">
|
||||
</core-empty-box>
|
||||
|
||||
<core-empty-box *ngIf="files && !files.length && !manage" icon="fas-folder"
|
||||
[message]="'core.sharedfiles.nosharedfilestoupload' | translate">
|
||||
</core-empty-box>
|
||||
</core-loading>
|
|
@ -0,0 +1,170 @@
|
|||
// (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, OnInit, OnDestroy, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { FileEntry, DirectoryEntry } from '@ionic-native/file';
|
||||
import { IonRefresher } from '@ionic/angular';
|
||||
import { Md5 } from 'ts-md5';
|
||||
|
||||
import { CoreSharedFiles } from '@features/sharedfiles/services/sharedfiles';
|
||||
import { CoreNavigator } from '@services/navigator';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { CoreTextUtils } from '@services/utils/text';
|
||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
|
||||
|
||||
/**
|
||||
* Component to display the list of shared files, either as a modal or inside a page.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'core-shared-files-list',
|
||||
templateUrl: 'list.html',
|
||||
})
|
||||
export class CoreSharedFilesListComponent implements OnInit, OnDestroy {
|
||||
|
||||
@Input() siteId?: string;
|
||||
@Input() mimetypes?: string[];
|
||||
@Input() isModal?: boolean; // Whether the component is loaded in a modal.
|
||||
@Input() manage?: boolean;
|
||||
@Input() pick?: boolean; // To pick a file you MUST use a modal.
|
||||
@Input() path?: string;
|
||||
@Input() showSitePicker?: boolean;
|
||||
@Output() onPathChanged = new EventEmitter<string>();
|
||||
@Output() onFilePicked = new EventEmitter<FileEntry>();
|
||||
|
||||
filesLoaded = false;
|
||||
files?: (FileEntry | DirectoryEntry)[];
|
||||
|
||||
protected shareObserver?: CoreEventObserver;
|
||||
|
||||
/**
|
||||
* Component being initialized.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
this.siteId = this.siteId || CoreSites.getCurrentSiteId();
|
||||
|
||||
this.loadFiles();
|
||||
|
||||
// Listen for new files shared with the app.
|
||||
this.shareObserver = CoreEvents.on(CoreEvents.FILE_SHARED, (data) => {
|
||||
if (data.siteId == this.siteId) {
|
||||
// File was stored in current site, refresh the list.
|
||||
this.filesLoaded = false;
|
||||
this.loadFiles().finally(() => {
|
||||
this.filesLoaded = true;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the files.
|
||||
*
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected async loadFiles(): Promise<void> {
|
||||
this.files = await CoreSharedFiles.getSiteSharedFiles(this.siteId, this.path, this.mimetypes);
|
||||
this.filesLoaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the list of files.
|
||||
*
|
||||
* @param refresher Refresher.
|
||||
*/
|
||||
refreshFiles(refresher: IonRefresher): void {
|
||||
this.loadFiles().finally(() => {
|
||||
refresher.complete();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a file is deleted. Remove the file from the list.
|
||||
*
|
||||
* @param index Position of the file.
|
||||
*/
|
||||
fileDeleted(index: number): void {
|
||||
this.files!.splice(index, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a file is renamed. Update the list.
|
||||
*
|
||||
* @param index Position of the file.
|
||||
* @param data Data containing the new FileEntry.
|
||||
*/
|
||||
fileRenamed(index: number, data: { file: FileEntry }): void {
|
||||
this.files![index] = data.file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a subfolder.
|
||||
*
|
||||
* @param folder The folder to open.
|
||||
*/
|
||||
openFolder(folder: DirectoryEntry): void {
|
||||
const path = CoreTextUtils.concatenatePaths(this.path || '', folder.name);
|
||||
|
||||
if (this.isModal) {
|
||||
this.path = path;
|
||||
this.filesLoaded = false;
|
||||
this.loadFiles();
|
||||
this.onPathChanged.emit(path);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const hash = <string> Md5.hashAsciiStr(path);
|
||||
|
||||
CoreNavigator.navigate(`../${hash}`, {
|
||||
params: {
|
||||
path,
|
||||
manage: this.manage,
|
||||
pick: this.pick,
|
||||
siteId: this.siteId,
|
||||
mimetypes: this.mimetypes,
|
||||
isModal: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Change site loaded.
|
||||
*
|
||||
* @param id Site to load.
|
||||
*/
|
||||
changeSite(id: string): void {
|
||||
this.siteId = id;
|
||||
this.path = '';
|
||||
this.filesLoaded = false;
|
||||
this.loadFiles();
|
||||
this.onPathChanged.emit('');
|
||||
}
|
||||
|
||||
/**
|
||||
* A file was picked.
|
||||
*
|
||||
* @param file Picked file.
|
||||
*/
|
||||
filePicked(file: FileEntry): void {
|
||||
this.onFilePicked.emit(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Component destroyed.
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.shareObserver?.off();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title>{{ 'core.sharedfiles.sharedfiles' | translate }}</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<core-loading [hideUntil]="loaded">
|
||||
<ion-list>
|
||||
<ion-item class="ion-text-wrap">
|
||||
<ion-label>
|
||||
<h2>{{ 'core.sharedfiles.chooseaccountstorefile' | translate }}</h2>
|
||||
<p>{{fileName}}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item *ngFor="let site of sites" (click)="storeInSite(site.id)" detail="false">
|
||||
<ion-avatar slot="start" aria-hidden="true">
|
||||
<img [src]="site.avatar" core-external-content [siteId]="site.id"
|
||||
alt="{{ 'core.pictureof' | translate:{$a: site.fullname} }}" role="presentation"
|
||||
onError="this.src='assets/img/user-avatar.png'">
|
||||
</ion-avatar>
|
||||
<ion-label>
|
||||
<h2>{{site.fullName}}</h2>
|
||||
<p><core-format-text clean="true" [text]="site.siteName" [siteId]="site.id"></core-format-text></p>
|
||||
<p>{{site.siteUrl}}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</core-loading>
|
||||
</ion-content>
|
|
@ -0,0 +1,108 @@
|
|||
// (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, OnInit } from '@angular/core';
|
||||
import { CoreSharedFilesHelper } from '@features/sharedfiles/services/sharedfiles-helper';
|
||||
import { FileEntry } from '@ionic-native/file';
|
||||
import { CoreFile } from '@services/file';
|
||||
import { CoreNavigator } from '@services/navigator';
|
||||
import { CoreSiteBasicInfo, CoreSites } from '@services/sites';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
|
||||
/**
|
||||
* Page to display the list of sites to choose one to store a shared file.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'page-core-shared-files-choose-site',
|
||||
templateUrl: 'choose-site.html',
|
||||
})
|
||||
export class CoreSharedFilesChooseSitePage implements OnInit {
|
||||
|
||||
fileName?: string;
|
||||
sites?: CoreSiteBasicInfo[];
|
||||
loaded = false;
|
||||
|
||||
protected filePath?: string;
|
||||
protected fileEntry?: FileEntry;
|
||||
protected isInbox = false; // Whether the file is in the Inbox folder.
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async ngOnInit(): Promise<void> {
|
||||
this.filePath = CoreNavigator.getRouteParam('filePath');
|
||||
this.isInbox = !!CoreNavigator.getRouteBooleanParam('isInbox');
|
||||
|
||||
if (!this.filePath) {
|
||||
CoreDomUtils.showErrorModal('Error reading file.');
|
||||
await CoreUtils.nextTick();
|
||||
CoreNavigator.back();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const fileAndDir = CoreFile.getFileAndDirectoryFromPath(this.filePath);
|
||||
this.fileName = fileAndDir.name;
|
||||
|
||||
try {
|
||||
await Promise.all([
|
||||
this.loadFile(),
|
||||
this.loadSites(),
|
||||
]);
|
||||
} catch {
|
||||
CoreDomUtils.showErrorModal('Error reading file.');
|
||||
CoreNavigator.back();
|
||||
} finally {
|
||||
this.loaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the file data.
|
||||
*
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected async loadFile(): Promise<void> {
|
||||
this.fileEntry = await CoreFile.getExternalFile(this.filePath!);
|
||||
this.fileName = this.fileEntry.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load sites.
|
||||
*
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected async loadSites(): Promise<void> {
|
||||
this.sites = await CoreSites.getSites();
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the file in a certain site.
|
||||
*
|
||||
* @param siteId Site ID.
|
||||
*/
|
||||
async storeInSite(siteId: string): Promise<void> {
|
||||
this.loaded = false;
|
||||
|
||||
try {
|
||||
await CoreSharedFilesHelper.storeSharedFileInSite(this.fileEntry!, siteId, this.isInbox);
|
||||
|
||||
CoreNavigator.back();
|
||||
} finally {
|
||||
this.loaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title>{{ title }}</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<core-shared-files-list [siteId]="siteId" [mimetypes]="mimetypes" [isModal]="false" [manage]="manage" [pick]="false"
|
||||
[path]="path" [showSitePicker]="showSitePicker" (onPathChanged)="calculateTitle($event)">
|
||||
</core-shared-files-list>
|
||||
</ion-content>
|
|
@ -0,0 +1,63 @@
|
|||
// (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, OnInit } from '@angular/core';
|
||||
|
||||
import { CoreFile } from '@services/file';
|
||||
import { CoreNavigator } from '@services/navigator';
|
||||
import { Translate } from '@singletons';
|
||||
|
||||
/**
|
||||
* Page to display the list of shared files.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'page-core-shared-files-list',
|
||||
templateUrl: 'list.html',
|
||||
})
|
||||
export class CoreSharedFilesListPage implements OnInit {
|
||||
|
||||
siteId?: string;
|
||||
mimetypes?: string[];
|
||||
manage = false;
|
||||
showSitePicker = false;
|
||||
path = '';
|
||||
title?: string;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
this.siteId = CoreNavigator.getRouteParam('siteId');
|
||||
this.mimetypes = CoreNavigator.getRouteParam('mimetypes');
|
||||
this.manage = !!CoreNavigator.getRouteBooleanParam('manage');
|
||||
this.path = CoreNavigator.getRouteParam('path') || '';
|
||||
this.showSitePicker = !CoreNavigator.getRouteParam('hideSitePicker');
|
||||
|
||||
this.calculateTitle(this.path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the title.
|
||||
*
|
||||
* @param path Path to use.
|
||||
*/
|
||||
calculateTitle(path?: string): void {
|
||||
if (path) {
|
||||
this.title = CoreFile.getFileAndDirectoryFromPath(path).name;
|
||||
} else {
|
||||
this.title = Translate.instant('core.sharedfiles.sharedfiles');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
// (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 { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { CoreSharedModule } from '@/core/shared.module';
|
||||
import { CoreSharedFilesComponentsModule } from './components/components.module';
|
||||
import { CoreSharedFilesListPage } from './pages/list/list';
|
||||
import { CoreSharedFilesChooseSitePage } from './pages/choose-site/choose-site';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: 'choosesite',
|
||||
component: CoreSharedFilesChooseSitePage,
|
||||
},
|
||||
{
|
||||
path: 'list/:hash',
|
||||
component: CoreSharedFilesListPage,
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forChild(routes),
|
||||
CoreSharedModule,
|
||||
CoreSharedFilesComponentsModule,
|
||||
],
|
||||
declarations: [
|
||||
CoreSharedFilesListPage,
|
||||
CoreSharedFilesChooseSitePage,
|
||||
],
|
||||
})
|
||||
export class CoreSharedFilesLazyModule {}
|
|
@ -465,6 +465,21 @@ export class CoreNavigatorService {
|
|||
return 'param-' + (++this.lastParamId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the route params in a path with the params values.
|
||||
*
|
||||
* @param path Path.
|
||||
* @param params Params.
|
||||
* @returns Path with params replaced.
|
||||
*/
|
||||
replaceRoutePathParams(path: string, params?: Params): string {
|
||||
for (const name in params) {
|
||||
path = path.replace(`:${name}`, params[name]);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const CoreNavigator = makeSingleton(CoreNavigatorService);
|
||||
|
|
|
@ -373,6 +373,6 @@ export type CoreEventFileSharedData = {
|
|||
/**
|
||||
* Data passed to APP_LAUNCHED_URL event.
|
||||
*/
|
||||
export type CoreEventAppLaunchedData = {
|
||||
export type CoreEventAppLaunchedData = {
|
||||
url: string;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue