You've already forked joplin
							
							
				mirror of
				https://github.com/laurent22/joplin.git
				synced 2025-10-31 00:07:48 +02:00 
			
		
		
		
	| @@ -27,6 +27,21 @@ export const initCodeMirror = ( | ||||
| 		initialText, | ||||
| 		settings, | ||||
|  | ||||
| 		onPasteFile: async (data) => { | ||||
| 			const reader = new FileReader(); | ||||
| 			return new Promise<void>((resolve, reject) => { | ||||
| 				reader.onload = async () => { | ||||
| 					const dataUrl = reader.result as string; | ||||
| 					const base64 = dataUrl.replace(/^data:.*;base64,/, ''); | ||||
| 					await messenger.remoteApi.onPasteFile(data.type, base64); | ||||
| 					resolve(); | ||||
| 				}; | ||||
| 				reader.onerror = () => reject(new Error('Failed to load file.')); | ||||
|  | ||||
| 				reader.readAsDataURL(data); | ||||
| 			}); | ||||
| 		}, | ||||
|  | ||||
| 		onLogMessage: message => { | ||||
| 			void messenger.remoteApi.logMessage(message); | ||||
| 		}, | ||||
|   | ||||
| @@ -38,7 +38,7 @@ describe('NoteEditor', () => { | ||||
| 					onChange={()=>{}} | ||||
| 					onSelectionChange={()=>{}} | ||||
| 					onUndoRedoDepthChange={()=>{}} | ||||
| 					onAttach={()=>{}} | ||||
| 					onAttach={async ()=>{}} | ||||
| 					plugins={{}} | ||||
| 				/> | ||||
| 			</MenuProvider>, | ||||
|   | ||||
| @@ -26,11 +26,14 @@ import { WebViewErrorEvent } from 'react-native-webview/lib/RNCWebViewNativeComp | ||||
| import Logger from '@joplin/utils/Logger'; | ||||
| import { PluginStates } from '@joplin/lib/services/plugins/reducer'; | ||||
| import useEditorCommandHandler from './hooks/useEditorCommandHandler'; | ||||
| import { join, dirname } from 'path'; | ||||
| import * as mimeUtils from '@joplin/lib/mime-utils'; | ||||
| import uuid from '@joplin/lib/uuid'; | ||||
|  | ||||
| type ChangeEventHandler = (event: ChangeEvent)=> void; | ||||
| type UndoRedoDepthChangeHandler = (event: UndoRedoDepthChangeEvent)=> void; | ||||
| type SelectionChangeEventHandler = (event: SelectionRangeChangeEvent)=> void; | ||||
| type OnAttachCallback = ()=> void; | ||||
| type OnAttachCallback = (filePath?: string)=> Promise<void>; | ||||
|  | ||||
| const logger = Logger.create('NoteEditor'); | ||||
|  | ||||
| @@ -373,6 +376,9 @@ function NoteEditor(props: Props, ref: any) { | ||||
|  | ||||
| 	const onEditorEvent = useRef((_event: EditorEvent) => {}); | ||||
|  | ||||
| 	const onAttachRef = useRef(props.onAttach); | ||||
| 	onAttachRef.current = props.onAttach; | ||||
|  | ||||
| 	const editorMessenger = useMemo(() => { | ||||
| 		const localApi: WebViewToEditorApi = { | ||||
| 			async onEditorEvent(event) { | ||||
| @@ -381,6 +387,16 @@ function NoteEditor(props: Props, ref: any) { | ||||
| 			async logMessage(message) { | ||||
| 				logger.debug('CodeMirror:', message); | ||||
| 			}, | ||||
| 			async onPasteFile(type, data) { | ||||
| 				const tempFilePath = join(Setting.value('tempDir'), `paste.${uuid.createNano()}.${mimeUtils.toFileExtension(type)}`); | ||||
| 				await shim.fsDriver().mkdir(dirname(tempFilePath)); | ||||
| 				try { | ||||
| 					await shim.fsDriver().writeFile(tempFilePath, data, 'base64'); | ||||
| 					await onAttachRef.current(tempFilePath); | ||||
| 				} finally { | ||||
| 					await shim.fsDriver().remove(tempFilePath); | ||||
| 				} | ||||
| 			}, | ||||
| 		}; | ||||
| 		const messenger = new RNToWebViewMessenger<WebViewToEditorApi, EditorBodyControl>( | ||||
| 			'editor', webviewRef, localApi, | ||||
|   | ||||
| @@ -57,4 +57,5 @@ export interface SelectionRange { | ||||
| export interface WebViewToEditorApi { | ||||
| 	onEditorEvent(event: EditorEvent): Promise<void>; | ||||
| 	logMessage(message: string): Promise<void>; | ||||
| 	onPasteFile(type: string, dataBase64: string): Promise<void>; | ||||
| } | ||||
|   | ||||
| @@ -6,10 +6,9 @@ import UndoRedoService from '@joplin/lib/services/UndoRedoService'; | ||||
| import NoteBodyViewer from '../NoteBodyViewer/NoteBodyViewer'; | ||||
| import checkPermissions from '../../utils/checkPermissions'; | ||||
| import NoteEditor from '../NoteEditor/NoteEditor'; | ||||
| import { Size } from '@joplin/utils/types'; | ||||
| const FileViewer = require('react-native-file-viewer').default; | ||||
| const React = require('react'); | ||||
| import { Keyboard, View, TextInput, StyleSheet, Linking, Image, Share, NativeSyntheticEvent } from 'react-native'; | ||||
| import { Keyboard, View, TextInput, StyleSheet, Linking, Share, NativeSyntheticEvent } from 'react-native'; | ||||
| import { Platform, PermissionsAndroid } from 'react-native'; | ||||
| const { connect } = require('react-redux'); | ||||
| // const { MarkdownEditor } = require('@joplin/lib/../MarkdownEditor/index.js'); | ||||
| @@ -36,7 +35,6 @@ import { BaseScreenComponent } from '../base-screen'; | ||||
| import { themeStyle, editorFont } from '../global-style'; | ||||
| const { dialogs } = require('../../utils/dialogs.js'); | ||||
| const DialogBox = require('react-native-dialogbox').default; | ||||
| import ImageResizer from '@bam.tech/react-native-image-resizer'; | ||||
| import shared, { BaseNoteScreenComponent } from '@joplin/lib/components/shared/note-screen-shared'; | ||||
| import { Asset, ImagePickerResponse, launchImageLibrary } from 'react-native-image-picker'; | ||||
| import SelectDateTimeDialog from '../SelectDateTimeDialog'; | ||||
| @@ -65,6 +63,8 @@ import debounce from '../../utils/debounce'; | ||||
| import { focus } from '@joplin/lib/utils/focusHandler'; | ||||
| import CommandService from '@joplin/lib/services/CommandService'; | ||||
| import * as urlUtils from '@joplin/lib/urlUtils'; | ||||
| import getImageDimensions from '../../utils/image/getImageDimensions'; | ||||
| import resizeImage from '../../utils/image/resizeImage'; | ||||
|  | ||||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied | ||||
| const emptyArray: any[] = []; | ||||
| @@ -682,24 +682,9 @@ class NoteScreenComponent extends BaseScreenComponent<Props, State> implements B | ||||
| 		return result; | ||||
| 	} | ||||
|  | ||||
| 	public async imageDimensions(uri: string): Promise<Size> { | ||||
| 		return new Promise((resolve, reject) => { | ||||
| 			Image.getSize( | ||||
| 				uri, | ||||
| 				(width: number, height: number) => { | ||||
| 					resolve({ width: width, height: height }); | ||||
| 				}, | ||||
| 				// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied | ||||
| 				(error: any) => { | ||||
| 					reject(error); | ||||
| 				}, | ||||
| 			); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	public async resizeImage(localFilePath: string, targetPath: string, mimeType: string) { | ||||
| 		const maxSize = Resource.IMAGE_MAX_DIMENSION; | ||||
| 		const dimensions = await this.imageDimensions(localFilePath); | ||||
| 		const dimensions = await getImageDimensions(localFilePath); | ||||
| 		reg.logger().info('Original dimensions ', dimensions); | ||||
|  | ||||
| 		const saveOriginalImage = async () => { | ||||
| @@ -711,30 +696,14 @@ class NoteScreenComponent extends BaseScreenComponent<Props, State> implements B | ||||
| 			dimensions.height = maxSize; | ||||
| 			reg.logger().info('New dimensions ', dimensions); | ||||
|  | ||||
| 			const format = mimeType === 'image/png' ? 'PNG' : 'JPEG'; | ||||
| 			reg.logger().info(`Resizing image ${localFilePath}`); | ||||
| 			const resizedImage = await ImageResizer.createResizedImage( | ||||
| 				localFilePath, | ||||
| 				dimensions.width, | ||||
| 				dimensions.height, | ||||
| 				format, | ||||
| 				85, // quality | ||||
| 				undefined, // rotation | ||||
| 				undefined, // outputPath | ||||
| 				true, // keep metadata | ||||
| 			); | ||||
|  | ||||
| 			const resizedImagePath = resizedImage.uri; | ||||
| 			reg.logger().info('Resized image ', resizedImagePath); | ||||
| 			reg.logger().info(`Moving ${resizedImagePath} => ${targetPath}`); | ||||
|  | ||||
| 			await shim.fsDriver().copy(resizedImagePath, targetPath); | ||||
|  | ||||
| 			try { | ||||
| 				await shim.fsDriver().unlink(resizedImagePath); | ||||
| 			} catch (error) { | ||||
| 				reg.logger().warn('Error when unlinking cached file: ', error); | ||||
| 			} | ||||
| 			await resizeImage({ | ||||
| 				inputPath: localFilePath, | ||||
| 				outputPath: targetPath, | ||||
| 				maxWidth: dimensions.width, | ||||
| 				maxHeight: dimensions.height, | ||||
| 				quality: 85, | ||||
| 				format: mimeType === 'image/png' ? 'PNG' : 'JPEG', | ||||
| 			}); | ||||
| 			return true; | ||||
| 		}; | ||||
|  | ||||
| @@ -1140,11 +1109,19 @@ class NoteScreenComponent extends BaseScreenComponent<Props, State> implements B | ||||
|  | ||||
| 		const buttonId = await dialogs.pop(this, _('Choose an option'), buttons); | ||||
|  | ||||
| 		if (buttonId === 'takePhoto') this.takePhoto_onPress(); | ||||
| 		if (buttonId === 'attachFile') void this.attachFile_onPress(); | ||||
| 		if (buttonId === 'attachPhoto') void this.attachPhoto_onPress(); | ||||
| 		if (buttonId === 'takePhoto') await this.takePhoto_onPress(); | ||||
| 		if (buttonId === 'attachFile') await this.attachFile_onPress(); | ||||
| 		if (buttonId === 'attachPhoto') await this.attachPhoto_onPress(); | ||||
| 	} | ||||
|  | ||||
| 	public onAttach = async (filePath?: string) => { | ||||
| 		if (filePath) { | ||||
| 			await this.attachFile({ uri: filePath }, 'all'); | ||||
| 		} else { | ||||
| 			await this.showAttachMenu(); | ||||
| 		} | ||||
| 	}; | ||||
|  | ||||
| 	// private vosk_:Vosk; | ||||
|  | ||||
| 	// private async getVosk() { | ||||
| @@ -1585,7 +1562,7 @@ class NoteScreenComponent extends BaseScreenComponent<Props, State> implements B | ||||
| 					onChange={this.onMarkdownEditorTextChange} | ||||
| 					onSelectionChange={this.onMarkdownEditorSelectionChange} | ||||
| 					onUndoRedoDepthChange={this.onUndoRedoDepthChange} | ||||
| 					onAttach={() => this.showAttachMenu()} | ||||
| 					onAttach={this.onAttach} | ||||
| 					readOnly={this.state.readOnly} | ||||
| 					plugins={this.props.plugins} | ||||
| 					style={{ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user