1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-07-06 23:56:13 +02:00
Files
joplin/packages/app-mobile/components/screens/Note/commands/attachFile.ts

131 lines
4.1 KiB
TypeScript
Raw Normal View History

import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService';
import { _ } from '@joplin/lib/locale';
import { CommandRuntimeProps } from '../types';
import { Platform } from 'react-native';
import pickDocument from '../../../../utils/pickDocument';
import { ImagePickerResponse, launchImageLibrary } from 'react-native-image-picker';
import Logger from '@joplin/utils/Logger';
import { msleep } from '@joplin/utils/time';
const logger = Logger.create('attachFile');
export enum AttachFileAction {
TakePhoto = 'takePhoto',
AttachFile = 'attachFile',
AttachPhoto = 'attachPhoto',
AttachDrawing = 'attachDrawing',
RecordAudio = 'attachRecording',
}
export interface AttachFileOptions {
action?: AttachFileAction | null;
}
export const declaration: CommandDeclaration = {
name: 'attachFile',
label: () => _('Attach file'),
iconName: 'material attachment',
};
export const runtime = (props: CommandRuntimeProps): CommandRuntime => {
const takePhoto = async () => {
if (Platform.OS === 'web') {
const response = await pickDocument({ multiple: true, preferCamera: true });
for (const asset of response) {
await props.attachFile(asset, 'image');
}
} else {
props.setCameraVisible(true);
}
};
const attachFile = async () => {
const response = await pickDocument({ multiple: true });
for (const asset of response) {
await props.attachFile(asset, 'all');
}
};
const attachPhoto = async () => {
// the selection Limit should be specified. I think 200 is enough?
const response: ImagePickerResponse = await launchImageLibrary({
mediaType: 'mixed',
videoQuality: 'low',
formatAsMp4: true,
includeBase64: false,
selectionLimit: 200,
});
if (response.errorCode) {
logger.warn('Got error from picker', response.errorCode);
return;
}
if (response.didCancel) {
logger.info('User cancelled picker');
return;
}
for (const asset of response.assets) {
await props.attachFile(asset, asset.type);
}
};
const recordAudio = async () => {
props.setAudioRecorderVisible(true);
};
const showAttachMenu = async (action: AttachFileAction = null) => {
props.hideKeyboard();
let buttonId: AttachFileAction = null;
if (action) {
buttonId = action;
} else {
const buttons = [];
// On iOS, it will show "local files", which means certain files saved from the browser
// and the iCloud files, but it doesn't include photos and images from the CameraRoll
//
// On Android, it will depend on the phone, but usually it will allow browsing all files and photos.
buttons.push({ text: _('Attach file'), id: AttachFileAction.AttachFile });
buttons.push({ text: _('Record audio'), id: AttachFileAction.RecordAudio });
// Disabled on Android because it doesn't work due to permission issues, but enabled on iOS
// because that's only way to browse photos from the camera roll.
if (Platform.OS === 'ios') buttons.push({ text: _('Attach photo'), id: AttachFileAction.AttachPhoto });
buttons.push({ text: _('Take photo'), id: AttachFileAction.TakePhoto });
buttonId = await props.dialogs.showMenu(_('Choose an option'), buttons) as AttachFileAction;
if (Platform.OS === 'ios') {
// Fixes an issue: The first time "attach file" or "attach photo" is chosen after starting Joplin
// on iOS, no attach dialog was shown. Adding a brief delay after the "choose an option" dialog is
// dismissed seems to fix the issue.
await msleep(1);
}
}
if (buttonId === AttachFileAction.TakePhoto) await takePhoto();
if (buttonId === AttachFileAction.AttachFile) await attachFile();
if (buttonId === AttachFileAction.AttachPhoto) await attachPhoto();
if (buttonId === AttachFileAction.RecordAudio) await recordAudio();
};
return {
execute: async (_context: CommandContext, filePath?: string, options: AttachFileOptions = null) => {
options = {
action: null,
...options,
};
if (filePath) {
await props.attachFile({ uri: filePath }, 'all');
} else {
await showAttachMenu(options.action);
}
},
enabledCondition: '!noteIsReadOnly',
};
};