forked from CIT/Vmeda.Online
		
	MOBILE-2312 core: Implement local file and site picker components
This commit is contained in:
		
							parent
							
								
									aa3562cfe7
								
							
						
					
					
						commit
						efe4b0f66d
					
				| @ -30,6 +30,8 @@ import { CoreContextMenuComponent } from './context-menu/context-menu'; | |||||||
| import { CoreContextMenuItemComponent } from './context-menu/context-menu-item'; | import { CoreContextMenuItemComponent } from './context-menu/context-menu-item'; | ||||||
| import { CoreContextMenuPopoverComponent } from './context-menu/context-menu-popover'; | import { CoreContextMenuPopoverComponent } from './context-menu/context-menu-popover'; | ||||||
| import { CoreChronoComponent } from './chrono/chrono'; | import { CoreChronoComponent } from './chrono/chrono'; | ||||||
|  | import { CoreLocalFileComponent } from './local-file/local-file'; | ||||||
|  | import { CoreSitePickerComponent } from './site-picker/site-picker'; | ||||||
| 
 | 
 | ||||||
| @NgModule({ | @NgModule({ | ||||||
|     declarations: [ |     declarations: [ | ||||||
| @ -45,7 +47,9 @@ import { CoreChronoComponent } from './chrono/chrono'; | |||||||
|         CoreContextMenuComponent, |         CoreContextMenuComponent, | ||||||
|         CoreContextMenuItemComponent, |         CoreContextMenuItemComponent, | ||||||
|         CoreContextMenuPopoverComponent, |         CoreContextMenuPopoverComponent, | ||||||
|         CoreChronoComponent |         CoreChronoComponent, | ||||||
|  |         CoreLocalFileComponent, | ||||||
|  |         CoreSitePickerComponent | ||||||
|     ], |     ], | ||||||
|     entryComponents: [ |     entryComponents: [ | ||||||
|         CoreContextMenuPopoverComponent |         CoreContextMenuPopoverComponent | ||||||
| @ -68,7 +72,9 @@ import { CoreChronoComponent } from './chrono/chrono'; | |||||||
|         CoreFileComponent, |         CoreFileComponent, | ||||||
|         CoreContextMenuComponent, |         CoreContextMenuComponent, | ||||||
|         CoreContextMenuItemComponent, |         CoreContextMenuItemComponent, | ||||||
|         CoreChronoComponent |         CoreChronoComponent, | ||||||
|  |         CoreLocalFileComponent, | ||||||
|  |         CoreSitePickerComponent | ||||||
|     ] |     ] | ||||||
| }) | }) | ||||||
| export class CoreComponentsModule {} | export class CoreComponentsModule {} | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| <a ion-item text-wrap class="item-media" (click)="download($event, true)" [class.item-2-button-right]="canDelete"> | <a ion-item text-wrap class="item-media" (click)="download($event, true)" [class.item-2-button-right]="canDelete" detail-none> | ||||||
|     <img [src]="fileIcon" alt="" role="presentation" item-start /> |     <img [src]="fileIcon" alt="" role="presentation" item-start /> | ||||||
|     <p>{{fileName}}</p> |     <p>{{fileName}}</p> | ||||||
|     <div class="buttons" item-end> |     <div class="buttons" item-end> | ||||||
|  | |||||||
							
								
								
									
										29
									
								
								src/components/local-file/local-file.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/components/local-file/local-file.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | |||||||
|  | <a ion-item text-wrap class="item-media" (click)="fileClicked($event)" detail-none> | ||||||
|  |     <img [src]="fileIcon" alt="{{fileExtension}}" role="presentation" item-start /> | ||||||
|  | 
 | ||||||
|  |     <!-- File name and edit button (if editable). --> | ||||||
|  |     <p *ngIf="!editMode" class="core-text-with-icon-right"> | ||||||
|  |         {{fileName}} | ||||||
|  |         <a ion-button icon-only clear *ngIf="manage" (click)="activateEdit($event)" [attr.aria-label]="'core.edit' | translate"> | ||||||
|  |             <ion-icon name="create" ios="md-create"></ion-icon> | ||||||
|  |         </a> | ||||||
|  |     </p> | ||||||
|  | 
 | ||||||
|  |     <!-- Form to edit the file's name. --> | ||||||
|  |     <form *ngIf="editMode" (ngSubmit)="changeName(newFileName)"> | ||||||
|  |         <ion-input type="text" name="filename" [(ngModel)]="newFileName" [placeholder]="'core.filename' | translate" autocapitalize="none" autocorrect="off" (click)="$event.stopPropagation()" [core-auto-focus]></ion-input> | ||||||
|  |         <button type="submit" ion-button icon-only clear class="core-button-icon-small" [attr.aria-label]="'core.save' | translate"> | ||||||
|  |             <ion-icon name="checkmark"></ion-icon> | ||||||
|  |         </button> | ||||||
|  |     </form> | ||||||
|  | 
 | ||||||
|  |     <!-- More data about the file. --> | ||||||
|  |     <p *ngIf="size">{{ size }}</p> | ||||||
|  |     <p *ngIf="timemodified">{{ timemodified }}</p> | ||||||
|  | 
 | ||||||
|  |     <div class="buttons" item-end *ngIf="manage"> | ||||||
|  |         <button ion-button clear icon-only (click)="deleteFile($event)" [attr.aria-label]="'core.delete' | translate" color="danger"> | ||||||
|  |             <ion-icon name="trash"></ion-icon> | ||||||
|  |         </button> | ||||||
|  |     </div> | ||||||
|  | </a> | ||||||
							
								
								
									
										187
									
								
								src/components/local-file/local-file.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								src/components/local-file/local-file.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,187 @@ | |||||||
|  | // (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, Input, Output, OnInit, EventEmitter } from '@angular/core'; | ||||||
|  | import { TranslateService } from '@ngx-translate/core'; | ||||||
|  | import { CoreFileProvider } from '../../providers/file'; | ||||||
|  | import { CoreDomUtilsProvider } from '../../providers/utils/dom'; | ||||||
|  | import { CoreMimetypeUtilsProvider } from '../../providers/utils/mimetype'; | ||||||
|  | import { CoreTextUtilsProvider } from '../../providers/utils/text'; | ||||||
|  | import { CoreUtilsProvider } from '../../providers/utils/utils'; | ||||||
|  | import * as moment from 'moment'; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Component to handle a local file. Only files inside the app folder can be managed. | ||||||
|  |  * | ||||||
|  |  * Shows the file name, icon (depending on extension), size and time modified. | ||||||
|  |  * Also, if managing is enabled it will also show buttons to rename and delete the file. | ||||||
|  |  */ | ||||||
|  | @Component({ | ||||||
|  |     selector: 'core-local-file', | ||||||
|  |     templateUrl: 'local-file.html' | ||||||
|  | }) | ||||||
|  | export class CoreLocalFileComponent implements OnInit { | ||||||
|  |     @Input() file: any; // A fileEntry retrieved using CoreFileProvider.getFile or similar.
 | ||||||
|  |     @Input() manage?: boolean|string; // Whether the user can manage the file (edit and delete).
 | ||||||
|  |     @Input() overrideClick?: boolean|string; // Whether the default item click should be overridden.
 | ||||||
|  |     @Output() onDelete?: EventEmitter<void>; // Will notify when the file is deleted.
 | ||||||
|  |     @Output() onRename?: EventEmitter<any>; // Will notify when the file is renamed. Receives the FileEntry as the param.
 | ||||||
|  |     @Output() onClick?: EventEmitter<void>; // Will notify when the file is clicked. Only if overrideClick is true.
 | ||||||
|  | 
 | ||||||
|  |     fileName: string; | ||||||
|  |     fileIcon: string; | ||||||
|  |     fileExtension: string; | ||||||
|  |     size: string; | ||||||
|  |     timemodified: string; | ||||||
|  |     newFileName: string = ''; | ||||||
|  |     editMode: boolean; | ||||||
|  |     relativePath: string; | ||||||
|  | 
 | ||||||
|  |     constructor(private mimeUtils: CoreMimetypeUtilsProvider, private utils: CoreUtilsProvider, private translate: TranslateService, | ||||||
|  |             private textUtils: CoreTextUtilsProvider, private fileProvider: CoreFileProvider, | ||||||
|  |             private domUtils: CoreDomUtilsProvider) { | ||||||
|  |         this.onDelete = new EventEmitter(); | ||||||
|  |         this.onRename = new EventEmitter(); | ||||||
|  |         this.onClick = new EventEmitter(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Component being initialized. | ||||||
|  |      */ | ||||||
|  |     ngOnInit() { | ||||||
|  |         this.manage = this.utils.isTrueOrOne(this.manage); | ||||||
|  | 
 | ||||||
|  |         // Let's calculate the relative path for the file.
 | ||||||
|  |         this.relativePath = this.fileProvider.removeBasePath(this.file.toURL()); | ||||||
|  |         if (!this.relativePath) { | ||||||
|  |             // Didn't find basePath, use fullPath but if the user tries to manage the file it'll probably fail.
 | ||||||
|  |             this.relativePath = this.file.fullPath; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         this.loadFileBasicData(); | ||||||
|  | 
 | ||||||
|  |         // Get the size and timemodified.
 | ||||||
|  |         this.fileProvider.getMetadata(this.file).then((metadata) => { | ||||||
|  |             if (metadata.size >= 0) { | ||||||
|  |                 this.size = this.textUtils.bytesToSize(metadata.size, 2); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             this.timemodified = moment(metadata.modificationTime).format('LLL'); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Load the basic data for the file. | ||||||
|  |      * | ||||||
|  |      * @param {[type]} scope [description] | ||||||
|  |      * @param {[type]} file  [description] | ||||||
|  |      */ | ||||||
|  |     protected loadFileBasicData() { | ||||||
|  |         this.fileName = this.file.name; | ||||||
|  |         this.fileIcon = this.mimeUtils.getFileIcon(this.file.name); | ||||||
|  |         this.fileExtension = this.mimeUtils.getFileExtension(this.file.name); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * File clicked. | ||||||
|  |      * | ||||||
|  |      * @param {Event} e Click event. | ||||||
|  |      */ | ||||||
|  |     fileClicked(e: Event) : void { | ||||||
|  |         e.preventDefault(); | ||||||
|  |         e.stopPropagation(); | ||||||
|  | 
 | ||||||
|  |         if (this.utils.isTrueOrOne(this.overrideClick) && this.onClick.observers.length) { | ||||||
|  |             this.onClick.emit(); | ||||||
|  |         } else { | ||||||
|  |             this.utils.openFile(this.file.toURL()); | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Activate the edit mode. | ||||||
|  |      * | ||||||
|  |      * @param {Event} e Click event. | ||||||
|  |      */ | ||||||
|  |     activateEdit(e: Event) : void { | ||||||
|  |         e.preventDefault(); | ||||||
|  |         e.stopPropagation(); | ||||||
|  |         this.editMode = true; | ||||||
|  |         this.newFileName = this.file.name; | ||||||
|  | 
 | ||||||
|  |         // @todo For some reason core-auto-focus isn't working right. Focus the input manually.
 | ||||||
|  |         // $timeout(function() {
 | ||||||
|  |         //     $mmUtil.focusElement(element[0].querySelector('input'));
 | ||||||
|  |         // });
 | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Rename the file. | ||||||
|  |      * | ||||||
|  |      * @param {string} newName New name. | ||||||
|  |      */ | ||||||
|  |     changeName(newName: string) : void { | ||||||
|  |         if (newName == this.file.name) { | ||||||
|  |             // Name hasn't changed, stop.
 | ||||||
|  |             this.editMode = false; | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         let modal = this.domUtils.showModalLoading(), | ||||||
|  |             fileAndDir = this.fileProvider.getFileAndDirectoryFromPath(this.relativePath), | ||||||
|  |             newPath = this.textUtils.concatenatePaths(fileAndDir.directory, newName); | ||||||
|  | 
 | ||||||
|  |         // Check if there's a file with this name.
 | ||||||
|  |         this.fileProvider.getFile(newPath).then(() => { | ||||||
|  |             // There's a file with this name, show error and stop.
 | ||||||
|  |             this.domUtils.showErrorModal('core.errorfileexistssamename', true); | ||||||
|  |         }).catch(() => { | ||||||
|  |             // File doesn't exist, move it.
 | ||||||
|  |             return this.fileProvider.moveFile(this.relativePath, newPath).then((fileEntry) => { | ||||||
|  |                 this.editMode = false; | ||||||
|  |                 this.file = fileEntry; | ||||||
|  |                 this.loadFileBasicData(); | ||||||
|  |                 this.onRename.emit({file: this.file}); | ||||||
|  |             }).catch(() => { | ||||||
|  |                 this.domUtils.showErrorModal('core.errorrenamefile', true); | ||||||
|  |             }); | ||||||
|  |         }).finally(() => { | ||||||
|  |             modal.dismiss(); | ||||||
|  |         }); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Delete the file. | ||||||
|  |      * | ||||||
|  |      * @param {Event} e Click event. | ||||||
|  |      */ | ||||||
|  |     deleteFile(e: Event) : void { | ||||||
|  |         e.preventDefault(); | ||||||
|  |         e.stopPropagation(); | ||||||
|  | 
 | ||||||
|  |         // Ask confirmation.
 | ||||||
|  |         this.domUtils.showConfirm(this.translate.instant('core.confirmdeletefile')).then(() => { | ||||||
|  |             let modal = this.domUtils.showModalLoading(); | ||||||
|  |             this.fileProvider.removeFile(this.relativePath).then(() => { | ||||||
|  |                 this.onDelete.emit(); | ||||||
|  |             }).catch(() => { | ||||||
|  |                 this.domUtils.showErrorModal('core.errordeletefile', true); | ||||||
|  |             }).finally(() => { | ||||||
|  |                 modal.dismiss(); | ||||||
|  |             }); | ||||||
|  |         }).catch(() => { | ||||||
|  |             // User cancelled.
 | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										6
									
								
								src/components/site-picker/site-picker.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/components/site-picker/site-picker.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | |||||||
|  | <ion-item> | ||||||
|  |     <ion-label>{{ 'core.site' | translate }}</ion-label> | ||||||
|  |     <ion-select [(ngModel)]="selectedSite" (ngModelChange)="siteSelected.emit(selectedSite)"> | ||||||
|  |         <ion-option *ngFor="let site of sites" [value]="site.id">{{ site.fullNameAndSiteName }}</ion-option> | ||||||
|  |     </ion-select> | ||||||
|  | </ion-item> | ||||||
							
								
								
									
										66
									
								
								src/components/site-picker/site-picker.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/components/site-picker/site-picker.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,66 @@ | |||||||
|  | // (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, Input, Output, EventEmitter, OnInit } from '@angular/core'; | ||||||
|  | import { TranslateService } from '@ngx-translate/core'; | ||||||
|  | import { CoreSitesProvider } from '../../providers/sites'; | ||||||
|  | import { CoreTextUtilsProvider } from '../../providers/utils/text'; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 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: 'site-picker.html' | ||||||
|  | }) | ||||||
|  | export class CoreSitePickerComponent implements OnInit { | ||||||
|  |     @Input() initialSite?: string; // Initial site. If not provided, current site.
 | ||||||
|  |     @Output() siteSelected: EventEmitter<string>; // Emit an event when a site is selected. Sends the siteId as parameter.
 | ||||||
|  | 
 | ||||||
|  |     selectedSite: string; | ||||||
|  |     sites: any[]; | ||||||
|  | 
 | ||||||
|  |     constructor(private translate: TranslateService, private sitesProvider: CoreSitesProvider, | ||||||
|  |             private textUtils: CoreTextUtilsProvider) { | ||||||
|  |         this.siteSelected = new EventEmitter(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ngOnInit() { | ||||||
|  |         this.selectedSite = this.initialSite || this.sitesProvider.getCurrentSiteId(); | ||||||
|  | 
 | ||||||
|  |         // Load the sites.
 | ||||||
|  |         this.sitesProvider.getSites().then((sites) => { | ||||||
|  |             let promises = []; | ||||||
|  | 
 | ||||||
|  |             sites.forEach((site: any) => { | ||||||
|  |                 // Format the site name.
 | ||||||
|  |                 promises.push(this.textUtils.formatText(site.siteName, true, true).catch(() => { | ||||||
|  |                     return site.siteName; | ||||||
|  |                 }).then((formatted) => { | ||||||
|  |                     site.fullNameAndSiteName = this.translate.instant('core.fullnameandsitename', | ||||||
|  |                             {fullname: site.fullName, sitename: formatted}); | ||||||
|  |                 })); | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |             return Promise.all(promises).then(() => { | ||||||
|  |                 this.sites = sites; | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user