refactor file & image picker

This commit is contained in:
ammarahm-ed
2021-10-30 14:02:58 +05:00
parent 767d37ec5d
commit ce191536bf
2 changed files with 307 additions and 296 deletions

View File

@@ -1,23 +1,4 @@
import React from 'react'; import picker from './picker';
import {Platform, View} from 'react-native';
import DocumentPicker from 'react-native-document-picker';
import Sodium from 'react-native-sodium';
import {launchCamera, launchImageLibrary} from 'react-native-image-picker';
import RNFetchBlob from 'rn-fetch-blob';
import {Attachment} from '../../../../components/AttachmentDialog';
import {eSendEvent, ToastEvent} from '../../../../services/EventManager';
import {editing} from '../../../../utils';
import {db} from '../../../../utils/database';
import {
eCloseProgressDialog,
eOpenProgressDialog
} from '../../../../utils/Events';
import {sleep} from '../../../../utils/TimeUtils';
import {EditorWebView, getNote} from '../../Functions';
import tiny, {safeKeyboardDismiss} from '../tiny';
const FILE_SIZE_LIMIT = 500 * 1024 * 1024;
const IMAGE_SIZE_LIMIT = 50 * 1024 * 1024;
export const execCommands = { export const execCommands = {
bold: `tinymce.activeEditor.execCommand('Bold');`, bold: `tinymce.activeEditor.execCommand('Bold');`,
@@ -89,185 +70,8 @@ export const execCommands = {
})();`, })();`,
cl: `tinymce.activeEditor.execCommand('insertCheckList')`, cl: `tinymce.activeEditor.execCommand('insertCheckList')`,
filepicker: async () => { filepicker: picker.file,
try { image: picker.image,
let options = {
mode: 'import',
allowMultiSelection: false
};
if (Platform.OS == 'ios') {
options.copyTo = 'cachesDirectory';
}
let key = await db.attachments.generateKey();
console.log('generated key for attachments: ', key);
let file = await DocumentPicker.pick(options);
file = file[0];
if (file.size > FILE_SIZE_LIMIT) {
ToastEvent.show({
title: 'File too large',
message: 'The maximum allowed size per file is 512 MB',
type:'error'
});
return;
}
if (file.copyError) {
ToastEvent.show({
heading: 'Failed to open file',
message: file.copyError,
type: 'error',
context: 'global'
});
return;
}
let uri =
Platform.OS === 'ios'
? file.fileCopyUri.replace('file:///', '/')
: file.uri;
eSendEvent(eOpenProgressDialog, {
title: 'Encrypting attachment',
paragraph: 'Please wait while we encrypt file for upload',
nowarn: true,
icon: 'attachment',
component: (
<View
style={{
paddingHorizontal: 12
}}>
<Attachment
attachment={{
metadata: {
filename: file.name
},
length: file.size
}}
encryption
/>
</View>
)
});
let hash = await Sodium.hashFile({
uri: uri,
type: 'url'
});
console.log(hash);
let result = await attachFile(uri, hash, file.type, file.name);
console.log('attach file: ', result);
if (Platform.OS === 'ios') {
await RNFetchBlob.fs.unlink(uri);
}
setTimeout(() => {
eSendEvent(eCloseProgressDialog);
}, 1000);
if (!result) return;
tiny.call(
EditorWebView,
`
(function() {
let file = ${JSON.stringify({
hash: hash,
filename: file.name,
type: file.type,
size: file.size
})}
tinymce.activeEditor.execCommand('mceAttachFile',file);
setTimeout(function() {
tinymce.activeEditor.nodeChanged({selectionChange:true})
},100)
})();
`
);
} catch (e) {
ToastEvent.show({
heading: e.message,
message: 'You need internet access to attach a file',
type: 'error',
context: 'global'
});
console.log('attachment error: ', e);
}
},
image: async () => {
if (editing.isFocused) {
safeKeyboardDismiss();
await sleep(500);
editing.isFocused = true;
}
eSendEvent(eOpenProgressDialog, {
noProgress: true,
noIcon: true,
actionsArray: [
{
action: async () => {
try {
let key = await db.attachments.generateKey();
eSendEvent(eCloseProgressDialog);
await sleep(400);
launchCamera(
{
includeBase64: true,
maxWidth: 4000,
maxHeight: 4000,
quality: 0.8,
mediaType: 'photo'
},
handleImageResponse
);
} catch (e) {
ToastEvent.show({
heading: e.message,
message: 'You need internet access to attach a file',
type: 'error',
context: 'global'
});
console.log('attachment error:', e);
}
},
actionText: 'Take photo',
icon: 'camera'
},
{
action: async () => {
try {
let key = await db.attachments.generateKey();
eSendEvent(eCloseProgressDialog);
await sleep(400);
launchImageLibrary(
{
includeBase64: true,
maxWidth: 4000,
maxHeight: 4000,
quality: 0.8,
mediaType: 'photo',
selectionLimit: 1
},
handleImageResponse
);
} catch (e) {
ToastEvent.show({
heading: e.message,
message: 'You need internet access to attach a file',
type: 'error',
context: 'global'
});
console.log('attachment error:', e);
}
},
actionText: 'Select from gallery',
icon: 'image-multiple'
}
]
});
return;
},
video: `tinymce.activeEditor.execCommand('mceMedia')`, video: `tinymce.activeEditor.execCommand('mceMedia')`,
pre: ` pre: `
tinymce.activeEditor.execCommand('mceInsertCodeBlock') tinymce.activeEditor.execCommand('mceInsertCodeBlock')
@@ -331,103 +135,6 @@ export const execCommands = {
});` });`
}; };
const handleImageResponse = async response => {
if (
response.didCancel ||
response.errorMessage ||
!response.assets ||
response.assets?.length === 0
) {
return;
}
let image = response.assets[0];
if (image.fileSize > IMAGE_SIZE_LIMIT) {
ToastEvent.show({
title: 'File too large',
message: 'The maximum allowed size per image is 50 MB',
type:'error'
});
return;
}
let b64 = `data:${image.type};base64, ` + image.base64;
let options;
if (Platform.OS === 'android') {
options = {
uri: image.uri,
type: 'url'
};
} else {
options = {
data: image.base64,
type: 'base64'
};
}
let hash = await Sodium.hashFile(options);
console.log(hash);
tiny.call(
EditorWebView,
`
(function(){
let image = ${JSON.stringify({
hash: hash,
type: image.type,
filename: image.fileName,
dataurl: b64,
size: image.fileSize
})}
tinymce.activeEditor.execCommand('mceAttachImage',image);
setTimeout(function() {
tinymce.activeEditor.nodeChanged({selectionChange:true})
},100)
})();
`
);
attachFile(
image.uri,
hash,
image.type,
image.fileName,
Platform.OS === 'ios' ? image.base64 : null
);
};
async function attachFile(uri, hash, type, filename, b64) {
try {
let exists = db.attachments.exists(hash);
let encryptionInfo;
if (!exists) {
let key = await db.attachments.generateKey();
let options = {
hash: hash
};
if (Platform.OS === 'ios' && b64) {
options.data = b64;
options.type = 'base64';
} else {
options.uri = uri;
options.type = 'url';
}
encryptionInfo = await Sodium.encryptFile(key, options);
encryptionInfo.type = type;
encryptionInfo.filename = filename;
encryptionInfo.alg = `xcha-argon2i13-s`;
encryptionInfo.size = encryptionInfo.length;
encryptionInfo.key = key;
} else {
encryptionInfo = {hash: hash};
}
console.log(encryptionInfo);
await db.attachments.add(encryptionInfo, getNote()?.id);
return true;
} catch (e) {
console.log('attach file error: ', e);
return false;
}
}
const setFloat = float => `(function () { const setFloat = float => `(function () {
let node = tinymce.activeEditor.selection.getNode(); let node = tinymce.activeEditor.selection.getNode();
if (node.tagName === 'IMG') { if (node.tagName === 'IMG') {

View File

@@ -0,0 +1,304 @@
import React from 'react';
import {Platform, View} from 'react-native';
import DocumentPicker from 'react-native-document-picker';
import Sodium from 'react-native-sodium';
import {launchCamera, launchImageLibrary} from 'react-native-image-picker';
import RNFetchBlob from 'rn-fetch-blob';
import {Attachment} from '../../../../components/AttachmentDialog';
import {eSendEvent, ToastEvent} from '../../../../services/EventManager';
import {editing} from '../../../../utils';
import {db} from '../../../../utils/database';
import {
eCloseProgressDialog,
eOpenProgressDialog
} from '../../../../utils/Events';
import {sleep} from '../../../../utils/TimeUtils';
import {EditorWebView, getNote} from '../../Functions';
import tiny, {safeKeyboardDismiss} from '../tiny';
const FILE_SIZE_LIMIT = 500 * 1024 * 1024;
const IMAGE_SIZE_LIMIT = 50 * 1024 * 1024;
const showEncryptionSheet = () => {
eSendEvent(eOpenProgressDialog, {
title: 'Encrypting attachment',
paragraph: 'Please wait while we encrypt file for upload',
nowarn: true,
icon: 'attachment',
component: (
<View
style={{
paddingHorizontal: 12
}}>
<Attachment
attachment={{
metadata: {
filename: file.name
},
length: file.size
}}
encryption
/>
</View>
)
});
};
const santizeUri = uri => {
uri = decodeURI(uri);
uri = Platform.OS === 'ios' ? uri.replace('file:///', '/') : uri;
return uri;
};
const file = async () => {
try {
let options = {
mode: 'import',
allowMultiSelection: false
};
if (Platform.OS == 'ios') {
options.copyTo = 'cachesDirectory';
}
let key = await db.attachments.generateKey();
console.log('generated key for attachments: ', key);
let file;
try {
file = await DocumentPicker.pick(options);
} catch (e) {
return;
}
file = file[0];
if (file.type === 'image/') {
ToastEvent.show({
title: 'Type not supported',
message: 'Please add images from gallery or camera picker.',
type: 'error',
});
return;
}
if (file.size > FILE_SIZE_LIMIT) {
ToastEvent.show({
title: 'File too large',
message: 'The maximum allowed size per file is 500 MB',
type: 'error'
});
return;
}
if (file.copyError) {
ToastEvent.show({
heading: 'Failed to open file',
message: file.copyError,
type: 'error',
context: 'global'
});
return;
}
let uri = Platform.OS === 'ios' ? file.fileCopyUri : file.uri;
uri = santizeUri(uri);
showEncryptionSheet();
let hash = await Sodium.hashFile({
uri: uri,
type: 'url'
});
console.log(uri);
let result = await attachFile(uri, hash, file.type, file.name, 'file');
console.log('attach file: ', result);
setTimeout(() => {
eSendEvent(eCloseProgressDialog);
}, 1000);
if (!result) return;
tiny.call(
EditorWebView,
`
(function() {
let file = ${JSON.stringify({
hash: hash,
filename: file.name,
type: file.type,
size: file.size
})}
tinymce.activeEditor.execCommand('mceAttachFile',file);
setTimeout(function() {
tinymce.activeEditor.nodeChanged({selectionChange:true})
},100)
})();
`
);
} catch (e) {
ToastEvent.show({
heading: e.message,
message: 'You need internet access to attach a file',
type: 'error',
context: 'global'
});
console.log('attachment error: ', e);
}
};
const image = async () => {
if (editing.isFocused) {
safeKeyboardDismiss();
await sleep(500);
editing.isFocused = true;
}
eSendEvent(eOpenProgressDialog, {
noProgress: true,
noIcon: true,
actionsArray: [
{
action: async () => {
try {
await db.attachments.generateKey();
eSendEvent(eCloseProgressDialog);
await sleep(400);
launchCamera(
{
includeBase64: true,
maxWidth: 4000,
maxHeight: 4000,
quality: 0.8,
mediaType: 'photo'
},
handleImageResponse
);
} catch (e) {
ToastEvent.show({
heading: e.message,
message: 'You need internet access to attach a file',
type: 'error',
context: 'global'
});
console.log('attachment error:', e);
}
},
actionText: 'Take photo',
icon: 'camera'
},
{
action: async () => {
try {
await db.attachments.generateKey();
eSendEvent(eCloseProgressDialog);
await sleep(400);
launchImageLibrary(
{
includeBase64: true,
maxWidth: 4000,
maxHeight: 4000,
quality: 0.8,
mediaType: 'photo',
selectionLimit: 1
},
handleImageResponse
);
} catch (e) {
ToastEvent.show({
heading: e.message,
message: 'You need internet access to attach a file',
type: 'error',
context: 'global'
});
console.log('attachment error:', e);
}
},
actionText: 'Select from gallery',
icon: 'image-multiple'
}
]
});
return;
};
const handleImageResponse = async response => {
if (
response.didCancel ||
response.errorMessage ||
!response.assets ||
response.assets?.length === 0
) {
return;
}
let image = response.assets[0];
if (image.fileSize > IMAGE_SIZE_LIMIT) {
ToastEvent.show({
title: 'File too large',
message: 'The maximum allowed size per image is 50 MB',
type: 'error'
});
return;
}
let b64 = `data:${image.type};base64, ` + image.base64;
let uri = image.uri;
uri = decodeURI(uri);
console.log(uri);
let hash = await Sodium.hashFile({
uri: uri,
type: 'url'
});
tiny.call(
EditorWebView,
`
(function(){
let image = ${JSON.stringify({
hash: hash,
type: image.type,
filename: image.fileName,
dataurl: b64,
size: image.fileSize
})}
tinymce.activeEditor.execCommand('mceAttachImage',image);
setTimeout(function() {
tinymce.activeEditor.nodeChanged({selectionChange:true})
},100)
})();
`
);
attachFile(uri, hash, image.type, image.fileName, 'image');
};
async function attachFile(uri, hash, type, filename) {
try {
let exists = db.attachments.exists(hash);
let encryptionInfo;
if (!exists) {
let key = await db.attachments.generateKey();
encryptionInfo = await Sodium.encryptFile(key, {
uri: uri,
type: 'url',
hash: hash
});
encryptionInfo.type = type;
encryptionInfo.filename = filename;
encryptionInfo.alg = `xcha-stream`;
encryptionInfo.size = encryptionInfo.length;
encryptionInfo.key = key;
} else {
encryptionInfo = {hash: hash};
}
console.log(encryptionInfo);
await db.attachments.add(encryptionInfo, getNote()?.id);
if (Platform.OS === 'ios') await RNFetchBlob.fs.unlink(uri);
return true;
} catch (e) {
console.log('attach file error: ', e);
if (Platform.OS === 'ios') {
await RNFetchBlob.fs.unlink(uri);
}
return false;
}
}
export default {
file,
image
};