From c1f6010aa58954878c29e44e9a6c3b9c8ecaeaaf Mon Sep 17 00:00:00 2001 From: Lucio Lelii Date: Mon, 13 Mar 2023 17:39:14 +0100 Subject: [PATCH] open file added --- android/app/capacitor.build.gradle | 1 + android/app/src/main/AndroidManifest.xml | 2 + android/capacitor.settings.gradle | 3 + ios/App/App/Info.plist | 4 ++ ios/App/Podfile | 1 + package-lock.json | 28 ++++++++ package.json | 2 + src/app/app.module.ts | 3 +- src/app/d4sauth.service.ts | 18 +++++ src/app/model/actions/copy-item.ts | 2 +- src/app/model/actions/create-folder.ts | 2 +- src/app/model/actions/delete-item.ts | 2 +- src/app/model/actions/move-item.ts | 2 +- src/app/model/actions/rename-item.ts | 2 +- .../show-folder/show-folder.component.html | 2 +- src/app/show-folder/show-folder.component.ts | 70 ++++++++++++++----- src/app/storagehub.service.ts | 11 ++- 17 files changed, 128 insertions(+), 27 deletions(-) diff --git a/android/app/capacitor.build.gradle b/android/app/capacitor.build.gradle index e4a126c..5271fdc 100644 --- a/android/app/capacitor.build.gradle +++ b/android/app/capacitor.build.gradle @@ -10,6 +10,7 @@ android { apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle" dependencies { implementation project(':capacitor-app') + implementation project(':capacitor-filesystem') implementation project(':capacitor-haptics') implementation project(':capacitor-keyboard') implementation project(':capacitor-splash-screen') diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 1bac385..e697f98 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -39,4 +39,6 @@ + + diff --git a/android/capacitor.settings.gradle b/android/capacitor.settings.gradle index c7e1694..de2e069 100644 --- a/android/capacitor.settings.gradle +++ b/android/capacitor.settings.gradle @@ -5,6 +5,9 @@ project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/ include ':capacitor-app' project(':capacitor-app').projectDir = new File('../node_modules/@capacitor/app/android') +include ':capacitor-filesystem' +project(':capacitor-filesystem').projectDir = new File('../node_modules/@capacitor/filesystem/android') + include ':capacitor-haptics' project(':capacitor-haptics').projectDir = new File('../node_modules/@capacitor/haptics/android') diff --git a/ios/App/App/Info.plist b/ios/App/App/Info.plist index f693a0e..a834c35 100644 --- a/ios/App/App/Info.plist +++ b/ios/App/App/Info.plist @@ -33,6 +33,10 @@ $(CURRENT_PROJECT_VERSION) LSRequiresIPhoneOS + UIFileSharingEnabled + + LSSupportsOpeningDocumentsInPlace + NSExtensionAttributes NSExtensionActivationRule diff --git a/ios/App/Podfile b/ios/App/Podfile index 2a45b34..016794b 100644 --- a/ios/App/Podfile +++ b/ios/App/Podfile @@ -12,6 +12,7 @@ def capacitor_pods pod 'Capacitor', :path => '../../node_modules/@capacitor/ios' pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios' pod 'CapacitorApp', :path => '../../node_modules/@capacitor/app' + pod 'CapacitorFilesystem', :path => '../../node_modules/@capacitor/filesystem' pod 'CapacitorHaptics', :path => '../../node_modules/@capacitor/haptics' pod 'CapacitorKeyboard', :path => '../../node_modules/@capacitor/keyboard' pod 'CapacitorSplashScreen', :path => '../../node_modules/@capacitor/splash-screen' diff --git a/package-lock.json b/package-lock.json index 3d77be0..cfaef47 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "@capacitor/android": "4.6.3", "@capacitor/app": "^4.1.1", "@capacitor/core": "4.6.3", + "@capacitor/filesystem": "^4.1.4", "@capacitor/haptics": "4.1.0", "@capacitor/ios": "4.6.3", "@capacitor/keyboard": "4.1.1", @@ -33,6 +34,7 @@ "@ionic/angular": "^6.5.4", "angular-oauth2-oidc": "^15.0.1", "capacitor-share-extension": "^2.0.0", + "cordova-plugin-file-opener2": "^4.0.0", "cordova-plugin-inappbrowser": "^5.0.0", "ionicons": "^6.1.3", "keycloak-angular": "^13.0.0", @@ -2565,6 +2567,14 @@ "tslib": "^2.1.0" } }, + "node_modules/@capacitor/filesystem": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@capacitor/filesystem/-/filesystem-4.1.4.tgz", + "integrity": "sha512-ivko1RNK4hq63xhMacq8D6D97N5/SAafTsrmY/pghYrG6Cl2SEY0+IgRu7V9/VWeN3FSplyUPucjUTAFQxXN5g==", + "peerDependencies": { + "@capacitor/core": "^4.0.0" + } + }, "node_modules/@capacitor/haptics": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@capacitor/haptics/-/haptics-4.1.0.tgz", @@ -6612,6 +6622,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cordova-plugin-file-opener2": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cordova-plugin-file-opener2/-/cordova-plugin-file-opener2-4.0.0.tgz", + "integrity": "sha512-+O+MMldI2k5Kjoa62KXYxnZQIGE4k5VfoCmmOUmaV5XM6U6euiiKMfCkuMrOrdAFb1C5Jsx+SuBHbWx1NEB5lw==", + "engines": { + "cordovaDependencies": { + "2.0.0": { + "cordova": ">=6.0.0" + }, + "3.0.0": { + "cordova": ">=7.0.0" + }, + "4.0.0": { + "cordova-android": ">=10.0.0" + } + } + } + }, "node_modules/cordova-plugin-inappbrowser": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cordova-plugin-inappbrowser/-/cordova-plugin-inappbrowser-5.0.0.tgz", diff --git a/package.json b/package.json index 3bb37e4..32fb08f 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@capacitor/android": "4.6.3", "@capacitor/app": "^4.1.1", "@capacitor/core": "4.6.3", + "@capacitor/filesystem": "^4.1.4", "@capacitor/haptics": "4.1.0", "@capacitor/ios": "4.6.3", "@capacitor/keyboard": "4.1.1", @@ -38,6 +39,7 @@ "@ionic/angular": "^6.5.4", "angular-oauth2-oidc": "^15.0.1", "capacitor-share-extension": "^2.0.0", + "cordova-plugin-file-opener2": "^4.0.0", "cordova-plugin-inappbrowser": "^5.0.0", "ionicons": "^6.1.3", "keycloak-angular": "^13.0.0", diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 49fa746..9976be3 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -11,13 +11,14 @@ import { D4sAuthService } from './d4sauth.service'; import { D4sIntentService } from './d4s-intent.service'; import {initializeIntent} from './_helper/intent-init.factory' import { StoragehubService } from './storagehub.service'; +import { EventService } from './event.service'; @NgModule({ declarations: [AppComponent], imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule, HttpClientModule, HttpClientModule, KeycloakAngularModule], providers: [ - D4sAuthService, ModalController, StoragehubService, ToastController, + D4sAuthService, ModalController, StoragehubService, ToastController, EventService, { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }, { provide: APP_INITIALIZER, diff --git a/src/app/d4sauth.service.ts b/src/app/d4sauth.service.ts index fb1709a..432e89c 100644 --- a/src/app/d4sauth.service.ts +++ b/src/app/d4sauth.service.ts @@ -63,12 +63,30 @@ export class D4sAuthService { mustUpdate = true; console.log("token refreshed"); } + + if (this.uma && this.isExpired(35)){ + console.log("uma expires in 35, must update") + mustUpdate = true; + } + if (mustUpdate || !this.uma) this.uma = await this.entitlement(this.audience); return "Bearer " + this.uma.access_token; } + isExpired(minValidityInSeconds : number ) { + if (!this.uma) return false; + const expires: number = this.parseJwt(this.uma.access_token).exp; + + const time: number = Date.now()/1000; + + console.log(`exiperes in ${expires} and now is ${time}`); + + return time+minValidityInSeconds > expires; + + } + entitlement(resourceServerId: any): Promise { return new Promise((resolve, reject) => { const keycloak = this.keycloak.getKeycloakInstance(); diff --git a/src/app/model/actions/copy-item.ts b/src/app/model/actions/copy-item.ts index 84f6289..56ae525 100644 --- a/src/app/model/actions/copy-item.ts +++ b/src/app/model/actions/copy-item.ts @@ -19,7 +19,7 @@ export class CopyAction extends Action { onSelected: (destinationItem: WSItem) => { this.actionHandler({ item: item, destinationItem: destinationItem }, storagehub).then( (obs) => obs.subscribe( () => { - reload(); + reload(destinationItem.item.id) notify(`${item.getTitle()} copied`); } )) diff --git a/src/app/model/actions/create-folder.ts b/src/app/model/actions/create-folder.ts index 76bec39..97eedd9 100644 --- a/src/app/model/actions/create-folder.ts +++ b/src/app/model/actions/create-folder.ts @@ -27,7 +27,7 @@ export class CreateFolderAction extends Action { text: 'Create', handler: (data) => { this.actionHandler({ wsitem: item, name: data.name }, storagehub).then((obs) => obs.subscribe( - () => reload() + () => reload(item.item.id) ) ); } diff --git a/src/app/model/actions/delete-item.ts b/src/app/model/actions/delete-item.ts index e866044..ca4ccf0 100644 --- a/src/app/model/actions/delete-item.ts +++ b/src/app/model/actions/delete-item.ts @@ -18,7 +18,7 @@ export class DeleteAction extends Action { text: 'Yes', handler: () => { this.actionHandler(item, storagehub).then((obs) => obs.subscribe( - () => reload() + () => reload(item.item.parentId) )); } }] diff --git a/src/app/model/actions/move-item.ts b/src/app/model/actions/move-item.ts index c6dce87..1dee197 100644 --- a/src/app/model/actions/move-item.ts +++ b/src/app/model/actions/move-item.ts @@ -18,7 +18,7 @@ export class MoveAction extends Action { onSelected: (destinationItem:WSItem) =>{ this.actionHandler({item: item, destinationItem: destinationItem}, storagehub).then( (obs) => obs.subscribe( () => { - reload(); + reload(destinationItem.item.id); notify(`${item.getTitle()} moved`); } )) diff --git a/src/app/model/actions/rename-item.ts b/src/app/model/actions/rename-item.ts index 5487283..31ec6da 100644 --- a/src/app/model/actions/rename-item.ts +++ b/src/app/model/actions/rename-item.ts @@ -27,7 +27,7 @@ export class RenameAction extends Action { text: 'Rename', handler: (data) => { this.actionHandler({item: item, newName: data.name}, storagehub).then( (obs) => obs.subscribe( - () => reload() + () => reload(item.item.parentId) )); } } diff --git a/src/app/show-folder/show-folder.component.html b/src/app/show-folder/show-folder.component.html index f9eeb23..304c895 100644 --- a/src/app/show-folder/show-folder.component.html +++ b/src/app/show-folder/show-folder.component.html @@ -38,6 +38,6 @@ - \ No newline at end of file diff --git a/src/app/show-folder/show-folder.component.ts b/src/app/show-folder/show-folder.component.ts index af2a5d0..ac72b96 100644 --- a/src/app/show-folder/show-folder.component.ts +++ b/src/app/show-folder/show-folder.component.ts @@ -12,11 +12,14 @@ import { CreateFolderAction } from '../model/actions/create-folder'; import { Actions } from '../model/actions/actions'; import { ItemsListComponent } from '../items-list/items-list.component'; import { Sorting, SortName, SortType } from '../model/sorting'; +import { UploaderInfoService } from '../uploader-info.service'; +import { EventService } from '../event.service'; +import { OpenFile } from '../model/actions/open-file'; @Component({ standalone: true, selector: 'show-folder', - providers: [FileOpener, StoragehubService], + providers: [FileOpener, StoragehubService, UploaderInfoService], templateUrl: './show-folder.component.html', styleUrls: ['./show-folder.component.scss'], schemas: [CUSTOM_ELEMENTS_SCHEMA], @@ -27,7 +30,7 @@ import { Sorting, SortName, SortType } from '../model/sorting'; export class ShowFolderComponent implements OnInit { @Output() folderClickedEvent = new EventEmitter(); - + filteredItems: WSItem[] | undefined; _items: WSItem[] | undefined; @@ -45,14 +48,14 @@ export class ShowFolderComponent implements OnInit { @Input() root: boolean = false; - underUploadItem: string[] = []; - selectedSegment = "all"; public get sortName(): typeof SortName { - return SortName; + return SortName; } + underUpload: string[]; + @ViewChild('filepicker') uploader!: ElementRef; customSortAlertOptions = { @@ -65,18 +68,31 @@ export class ShowFolderComponent implements OnInit { private storagehub: StoragehubService, private alertCtrl: AlertController, private modalCtrl: ModalController, - private toastController: ToastController + private toastController: ToastController, + private uploaderInfo: UploaderInfoService, + private event: EventService, + private fileOpener: FileOpener ) { + this.underUpload = this.getUnderUploadItems(); } - ngOnInit(): void { + this.event.ReloadEvent.subscribe((val) => { + console.log(`event received with value ${val} in item ${this.parentItem?.item.id}`); + if (val === this.parentItem?.item.id) + this.loadDocuments(); + }); + } + + getUnderUploadItems(): string[] { + const parentId = this.parentItem?.item.id; + return parentId ? this.uploaderInfo.getUnderUpload(parentId) : []; } loadDocuments() { this.filteredItems = undefined; if (this.parentItem) { - this.storagehub.getChildren(this.parentItem.item.id).then( (obs) => obs.subscribe( + this.storagehub.getChildren(this.parentItem.item.id).then((obs) => obs.subscribe( (res) => { const tmpItems$: WSItem[] = [] const tmpFiltered$: WSItem[] = [] @@ -89,8 +105,9 @@ export class ShowFolderComponent implements OnInit { }); this._items = tmpItems$.sort(Sorting.getSortFunction(this.currentSortName, this.currentSortType)); this.filteredItems = tmpFiltered$.sort(Sorting.getSortFunction(this.currentSortName, this.currentSortType)); + this.underUpload = this.getUnderUploadItems(); }) - ) + ) } } @@ -133,8 +150,8 @@ export class ShowFolderComponent implements OnInit { } changeSortName($event: any) { - var sort : SortName = $event.target.value; - if (this.currentSortName != sort){ + var sort: SortName = $event.target.value; + if (this.currentSortName != sort) { this.currentSortName = sort; this.updateSort(); } @@ -146,9 +163,13 @@ export class ShowFolderComponent implements OnInit { } itemClicked(item: WSItem) { - if (item.isFolder()) { + if (item.isFolder()) this.folderClickedEvent.emit(item); + else if (item.isFile()){ + new OpenFile().open(this.storagehub, this.fileOpener, item); } + + } addFile() { @@ -159,12 +180,23 @@ export class ShowFolderComponent implements OnInit { const selected = $event.target.files[0]; if (selected && this.parentItem) { - this.underUploadItem = [selected.name]; - const upload$ = this.storagehub.uploadFile(this.parentItem.item.id, selected.name, selected); - upload$.then( (obs) => obs.subscribe({ - next: () => this.loadDocuments(), - error: () => this.underUploadItem = [], - complete: () => this.underUploadItem = [] + const parentId: string = this.parentItem?.item?.id; + this.uploaderInfo.uploadStarted(parentId, selected.name); + const upload$ = this.storagehub.uploadFile(parentId, selected.name, selected); + var first = true; + upload$.then((obs) => obs.subscribe({ + next: () => { + if (first) { + this.loadDocuments(); + first = false; + } + }, + error: () => this.uploaderInfo.uploadFinished(parentId, selected.name), + complete: () => { + console.log + this.uploaderInfo.uploadFinished(parentId, selected.name); + this.loadDocuments(); + } }) ); @@ -233,7 +265,7 @@ export class ShowFolderComponent implements OnInit { await this.presentAlertControl(alertOptions); } else { var modalOptions: undefined | ModalOptions = action.getModalOptions(item, this.storagehub, - () => this.loadDocuments(), (text: string) => this.presentToast(text)); + (itemId: string) => this.event.ReloadEvent.emit(itemId), (text: string) => this.presentToast(text)); if (modalOptions) await this.presentModal(modalOptions); else diff --git a/src/app/storagehub.service.ts b/src/app/storagehub.service.ts index 87bdd02..a8d0472 100644 --- a/src/app/storagehub.service.ts +++ b/src/app/storagehub.service.ts @@ -8,6 +8,7 @@ import { Item } from './model/item.model'; import { ItemList } from './model/itemlist.model'; import { ItemWrapper } from './model/item-wrapper.model'; import { D4sAuthService } from './d4sauth.service'; +import { WSItem } from './model/ws-item'; const shURL ="https://api.dev.d4science.org/workspace" @@ -87,6 +88,14 @@ export class StoragehubService { ); } + async downloadFile(itemId: string){ + const bearer = await this.auth.getSecureHeader(); + let downloadUrl = `${shURL}/items/${itemId}/download`; + return this.http.get(downloadUrl, {headers: {"Authorization": bearer }, responseType: 'blob', observe: 'body'}).pipe( + catchError(this.error) + ); + } + async createFolder(parentId: string, name: string, description: string) { const bearer = await this.auth.getSecureHeader(); let createFolderURL = `${shURL}/items/${parentId}/create/FOLDER`; @@ -100,7 +109,7 @@ export class StoragehubService { async deleteItem(itemId: string) : Promise> { const bearer = await this.auth.getSecureHeader(); let deleteItemUrl = `${shURL}/items/${itemId}`; - return this.http.delete( deleteItemUrl).pipe( + return this.http.delete( deleteItemUrl, {headers: {"Authorization": bearer }}).pipe( catchError(this.error) ); }