273 lines
9.8 KiB
TypeScript
273 lines
9.8 KiB
TypeScript
// (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 { Injectable } from '@angular/core';
|
|
import { FileEntry } from '@ionic-native/file/ngx';
|
|
|
|
import { CoreCanceledError } from '@classes/errors/cancelederror';
|
|
import { CoreFileUploader } from '@features/fileuploader/services/fileuploader';
|
|
import { CoreFileUploaderHandlerResult } from '@features/fileuploader/services/fileuploader-delegate';
|
|
import { CoreFile } from '@services/file';
|
|
import { CoreNavigator } from '@services/navigator';
|
|
import { CoreSites } from '@services/sites';
|
|
import { CoreDomUtils } from '@services/utils/dom';
|
|
import { AlertController, ApplicationInit, makeSingleton, Translate } from '@singletons';
|
|
import { CoreEvents } from '@singletons/events';
|
|
import { CoreLogger } from '@singletons/logger';
|
|
import { CoreSharedFilesListModalComponent } from '../components/list-modal/list-modal';
|
|
import { CoreSharedFiles } from './sharedfiles';
|
|
import { SHAREDFILES_PAGE_NAME } from '../sharedfiles.module';
|
|
import { CoreSharedFilesChooseSitePage } from '../pages/choose-site/choose-site';
|
|
import { CoreError } from '@classes/errors/error';
|
|
import { CorePlatform } from '@services/platform';
|
|
|
|
/**
|
|
* Helper service to share files with the app.
|
|
*/
|
|
@Injectable({ providedIn: 'root' })
|
|
export class CoreSharedFilesHelperProvider {
|
|
|
|
protected logger: CoreLogger;
|
|
|
|
constructor() {
|
|
this.logger = CoreLogger.getInstance('CoreSharedFilesHelperProvider');
|
|
}
|
|
|
|
/**
|
|
* Initialize.
|
|
*/
|
|
initialize(): void {
|
|
if (!CorePlatform.isIOS()) {
|
|
return;
|
|
}
|
|
|
|
let lastCheck = Date.now();
|
|
|
|
// Check if there are new files at app start and when the app is resumed.
|
|
this.searchIOSNewSharedFiles();
|
|
|
|
CorePlatform.resume.subscribe(() => {
|
|
// Wait a bit to make sure that APP_LAUNCHED_URL is treated before this callback.
|
|
setTimeout(() => {
|
|
if (Date.now() - lastCheck < 1000) {
|
|
// Last check less than 1s ago, don't do anything.
|
|
return;
|
|
}
|
|
|
|
lastCheck = Date.now();
|
|
this.searchIOSNewSharedFiles();
|
|
}, 200);
|
|
});
|
|
|
|
CoreEvents.on(CoreEvents.APP_LAUNCHED_URL, (data) => {
|
|
if (data.url.indexOf('file://') === 0) {
|
|
// We received a file in iOS, it's probably a shared file. Treat it.
|
|
lastCheck = Date.now();
|
|
this.searchIOSNewSharedFiles(data.url);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Ask a user if he wants to replace a file (using originalName) or rename it (using newName).
|
|
*
|
|
* @param originalName Original name.
|
|
* @param newName New name.
|
|
* @returns Promise resolved with the name to use when the user chooses. Rejected if user cancels.
|
|
*/
|
|
async askRenameReplace(originalName: string, newName: string): Promise<string> {
|
|
const alert = await AlertController.create({
|
|
header: Translate.instant('core.sharedfiles.sharedfiles'),
|
|
message: Translate.instant('core.sharedfiles.chooseactionrepeatedfile', { $a: newName }),
|
|
buttons: [
|
|
{
|
|
text: Translate.instant('core.sharedfiles.rename'),
|
|
role: 'rename',
|
|
},
|
|
{
|
|
text: Translate.instant('core.sharedfiles.replace'),
|
|
role: 'replace',
|
|
},
|
|
],
|
|
});
|
|
|
|
await alert.present();
|
|
|
|
const result = await alert.onDidDismiss();
|
|
|
|
if (result.role == 'rename') {
|
|
return newName;
|
|
} else if (result.role == 'replace') {
|
|
return originalName;
|
|
} else {
|
|
// Canceled.
|
|
throw new CoreCanceledError();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Go to the choose site view.
|
|
*
|
|
* @param filePath File path to send to the view.
|
|
* @param isInbox Whether the file is in the Inbox folder.
|
|
*/
|
|
goToChooseSite(filePath: string, isInbox?: boolean): void {
|
|
if (CoreSites.isLoggedIn()) {
|
|
CoreNavigator.navigateToSitePath(`/${SHAREDFILES_PAGE_NAME}/choosesite`, {
|
|
params: { filePath, isInbox },
|
|
});
|
|
} else {
|
|
CoreNavigator.navigate(`/${SHAREDFILES_PAGE_NAME}/choosesite`, {
|
|
params: { filePath, isInbox },
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Whether the user is already choosing a site to store a shared file.
|
|
*
|
|
* @returns Whether the user is already choosing a site to store a shared file.
|
|
*/
|
|
protected isChoosingSite(): boolean {
|
|
return CoreNavigator.getCurrentRoute({ pageComponent: CoreSharedFilesChooseSitePage }) !== null;
|
|
}
|
|
|
|
/**
|
|
* Open the view to select a shared file.
|
|
*
|
|
* @param mimetypes List of supported mimetypes. If undefined, all mimetypes supported.
|
|
* @returns Promise resolved when a file is picked, rejected if file picker is closed without selecting a file.
|
|
*/
|
|
async pickSharedFile(mimetypes?: string[]): Promise<CoreFileUploaderHandlerResult> {
|
|
const file = await CoreDomUtils.openModal<FileEntry>({
|
|
component: CoreSharedFilesListModalComponent,
|
|
cssClass: 'core-modal-fullscreen',
|
|
componentProps: { mimetypes, pick: true },
|
|
});
|
|
|
|
if (!file) {
|
|
// User cancelled.
|
|
throw new CoreCanceledError();
|
|
}
|
|
|
|
const error = CoreFileUploader.isInvalidMimetype(mimetypes, file.fullPath);
|
|
if (error) {
|
|
throw new CoreError(error);
|
|
}
|
|
|
|
return {
|
|
path: file.fullPath,
|
|
treated: false,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Delete a shared file.
|
|
*
|
|
* @param fileEntry The file entry to delete.
|
|
* @param isInbox Whether the file is in the Inbox folder.
|
|
* @returns Promise resolved when done.
|
|
*/
|
|
protected removeSharedFile(fileEntry: FileEntry, isInbox?: boolean): Promise<void> {
|
|
if (isInbox) {
|
|
return CoreSharedFiles.deleteInboxFile(fileEntry);
|
|
} else {
|
|
return CoreFile.removeFileByFileEntry(fileEntry);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if there is a new file received in iOS and move it to the shared folder of current site.
|
|
* If more than one site is found, the user will have to choose the site where to store it in.
|
|
* If more than one file is found, treat only the first one.
|
|
*
|
|
* @param path Path to a file received when launching the app.
|
|
* @returns Promise resolved when done.
|
|
*/
|
|
async searchIOSNewSharedFiles(path?: string): Promise<void> {
|
|
try {
|
|
await ApplicationInit.donePromise;
|
|
|
|
if (this.isChoosingSite()) {
|
|
// We're already treating a shared file. Abort.
|
|
return;
|
|
}
|
|
|
|
let fileEntry: FileEntry | undefined;
|
|
if (path) {
|
|
// The app was launched with the path to the file, get the file.
|
|
fileEntry = await CoreFile.getExternalFile(path);
|
|
} else {
|
|
// No path received, search if there is any file in the Inbox folder.
|
|
fileEntry = await CoreSharedFiles.checkIOSNewFiles();
|
|
}
|
|
|
|
if (!fileEntry) {
|
|
return;
|
|
}
|
|
|
|
const siteIds = await CoreSites.getSitesIds();
|
|
|
|
if (!siteIds.length) {
|
|
// No sites stored, show error and delete the file.
|
|
CoreDomUtils.showErrorModal('core.sharedfiles.errorreceivefilenosites', true);
|
|
|
|
return this.removeSharedFile(fileEntry, !path);
|
|
} else if (siteIds.length == 1) {
|
|
return this.storeSharedFileInSite(fileEntry, siteIds[0], !path);
|
|
} else if (!this.isChoosingSite()) {
|
|
this.goToChooseSite(fileEntry.toURL(), !path);
|
|
}
|
|
} catch (error) {
|
|
if (error) {
|
|
this.logger.error('Error searching iOS new shared files', error, path);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Store a shared file in a site's shared files folder.
|
|
*
|
|
* @param fileEntry Shared file entry.
|
|
* @param siteId Site ID. If not defined, current site.
|
|
* @param isInbox Whether the file is in the Inbox folder.
|
|
* @returns Promise resolved when done.
|
|
*/
|
|
async storeSharedFileInSite(fileEntry: FileEntry, siteId?: string, isInbox?: boolean): Promise<void> {
|
|
siteId = siteId || CoreSites.getCurrentSiteId();
|
|
|
|
// First of all check if there's already a file with the same name in the shared files folder.
|
|
const sharedFilesDirPath = CoreSharedFiles.getSiteSharedFilesDirPath(siteId);
|
|
|
|
let newName = await CoreFile.getUniqueNameInFolder(sharedFilesDirPath, fileEntry.name);
|
|
|
|
if (newName.toLowerCase() != fileEntry.name.toLowerCase()) {
|
|
// Repeated name. Ask the user what he wants to do.
|
|
newName = await this.askRenameReplace(fileEntry.name, newName);
|
|
}
|
|
|
|
try {
|
|
await CoreSharedFiles.storeFileInSite(fileEntry, newName, siteId);
|
|
} catch (error) {
|
|
CoreDomUtils.showErrorModal(error || 'Error moving file.');
|
|
} finally {
|
|
this.removeSharedFile(fileEntry, isInbox);
|
|
CoreDomUtils.showAlertTranslated('core.success', 'core.sharedfiles.successstorefile');
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
export const CoreSharedFilesHelper = makeSingleton(CoreSharedFilesHelperProvider);
|