You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-11-29 22:48:10 +02:00
Mobile: Improved camera attachment
This commit is contained in:
83
ReactNativeClient/lib/components/CameraView.js
Normal file
83
ReactNativeClient/lib/components/CameraView.js
Normal file
@@ -0,0 +1,83 @@
|
||||
const React = require('react'); const Component = React.Component;
|
||||
const { View, Button, StyleSheet, TouchableOpacity } = require('react-native');
|
||||
const { globalStyle, themeStyle } = require('lib/components/global-style.js');
|
||||
import { RNCamera } from 'react-native-camera';
|
||||
const Icon = require('react-native-vector-icons/Ionicons').default;
|
||||
const { _ } = require('lib/locale.js');
|
||||
|
||||
class CameraView extends Component {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.state = {
|
||||
snapping: false,
|
||||
};
|
||||
|
||||
this.back_onPress = this.back_onPress.bind(this);
|
||||
this.photo_onPress = this.photo_onPress.bind(this);
|
||||
}
|
||||
|
||||
back_onPress() {
|
||||
if (this.props.onCancel) this.props.onCancel();
|
||||
}
|
||||
|
||||
async photo_onPress() {
|
||||
if (!this.camera || !this.props.onPhoto) return;
|
||||
|
||||
this.setState({ snapping: true });
|
||||
|
||||
const result = await this.camera.takePictureAsync({
|
||||
quality: 0.8,
|
||||
exif: true,
|
||||
fixOrientation: true
|
||||
});
|
||||
|
||||
if (this.props.onPhoto) this.props.onPhoto(result);
|
||||
|
||||
this.setState({ snapping: false });
|
||||
}
|
||||
|
||||
render() {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const photoIcon = this.state.snapping ? 'md-checkmark' : 'md-camera';
|
||||
|
||||
return (
|
||||
<View style={this.props.style}>
|
||||
<RNCamera
|
||||
style={{flex:1}}
|
||||
ref={ref => { this.camera = ref; }}
|
||||
type={RNCamera.Constants.Type.back}
|
||||
permissionDialogTitle={_('Permission to use camera')}
|
||||
permissionDialogMessage={_('Your permission to use your camera is required.')}
|
||||
>
|
||||
<View style={{flex:1, justifyContent:'space-between', flexDirection:'column'}}>
|
||||
<View style={{flex:1, justifyContent:'flex-start'}}>
|
||||
<TouchableOpacity onPress={this.back_onPress}>
|
||||
<View style={{ marginLeft:5, marginTop:5, borderRadius:90, width:50,height:50, display:'flex', backgroundColor:'#ffffff55', justifyContent:'center', alignItems:'center'}}>
|
||||
<Icon name={'md-arrow-back'} style={{
|
||||
fontSize: 40,
|
||||
color: 'black',
|
||||
}} />
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<View style={{flex:1, justifyContent:'center', alignItems:'flex-end', flexDirection:'row'}}>
|
||||
<TouchableOpacity onPress={this.photo_onPress}>
|
||||
<View style={{marginBottom:20, borderRadius:90, width:90,height:90,backgroundColor:'#ffffffaa', display:'flex', justifyContent:'center', alignItems:'center'}}>
|
||||
<Icon name={photoIcon} style={{
|
||||
fontSize: 60,
|
||||
color: 'black',
|
||||
}} />
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</RNCamera>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = CameraView;
|
||||
@@ -36,6 +36,7 @@ const ImagePicker = require('react-native-image-picker');
|
||||
const AlarmService = require('lib/services/AlarmService.js');
|
||||
const { SelectDateTimeDialog } = require('lib/components/select-date-time-dialog.js');
|
||||
const ShareExtension = require('react-native-share-extension').default;
|
||||
const CameraView = require('lib/components/CameraView');
|
||||
|
||||
import FileViewer from 'react-native-file-viewer';
|
||||
|
||||
@@ -60,6 +61,7 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
heightBumpView:0,
|
||||
noteTagDialogShown: false,
|
||||
fromShare: false,
|
||||
showCamera: false,
|
||||
};
|
||||
|
||||
// iOS doesn't support multiline text fields properly so disable it
|
||||
@@ -158,6 +160,10 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
this.refs.noteBodyViewer.rebuildMd();
|
||||
}
|
||||
}
|
||||
|
||||
this.attachPhoto_onPress = this.attachPhoto_onPress.bind(this);
|
||||
this.cameraView_onPhoto = this.cameraView_onPhoto.bind(this);
|
||||
this.cameraView_onCancel = this.cameraView_onCancel.bind(this);
|
||||
}
|
||||
|
||||
styles() {
|
||||
@@ -335,7 +341,7 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
}
|
||||
}
|
||||
|
||||
async attachFile(pickerResponse, fileType) {
|
||||
async attachFile(pickerResponse, fileType) {
|
||||
if (!pickerResponse) {
|
||||
reg.logger().warn('Got no response from picker');
|
||||
return;
|
||||
@@ -354,7 +360,6 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
const localFilePath = pickerResponse.uri;
|
||||
let mimeType = pickerResponse.type;
|
||||
|
||||
|
||||
if (!mimeType) {
|
||||
const ext = fileExtension(localFilePath);
|
||||
mimeType = mimeUtils.fromFileExtension(ext);
|
||||
@@ -376,7 +381,7 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
resource.id = uuid.create();
|
||||
resource.mime = mimeType;
|
||||
resource.title = pickerResponse.fileName ? pickerResponse.fileName : _('Untitled');
|
||||
resource.file_extension = safeFileExtension(fileExtension(pickerResponse.fileName));
|
||||
resource.file_extension = safeFileExtension(fileExtension(pickerResponse.fileName ? pickerResponse.fileName : localFilePath));
|
||||
|
||||
if (!resource.mime) resource.mime = 'application/octet-stream';
|
||||
|
||||
@@ -422,6 +427,25 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
await this.attachFile(response, 'image');
|
||||
}
|
||||
|
||||
attachPhoto_onPress() {
|
||||
this.setState({ showCamera: true });
|
||||
}
|
||||
|
||||
cameraView_onPhoto(data) {
|
||||
this.attachFile({
|
||||
uri: data.uri,
|
||||
didCancel: false,
|
||||
error: null,
|
||||
type: 'image/jpg',
|
||||
}, 'image');
|
||||
|
||||
this.setState({ showCamera: false });
|
||||
}
|
||||
|
||||
cameraView_onCancel() {
|
||||
this.setState({ showCamera: false });
|
||||
}
|
||||
|
||||
async attachFile_onPress() {
|
||||
const response = await this.pickDocument();
|
||||
await this.attachFile(response, 'all');
|
||||
@@ -506,7 +530,7 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
let canAttachPicture = true;
|
||||
if (Platform.OS === 'android' && Platform.Version < 21) canAttachPicture = false;
|
||||
if (canAttachPicture) {
|
||||
output.push({ title: _('Attach photo'), onPress: () => { this.attachImage_onPress(); } });
|
||||
output.push({ title: _('Attach photo'), onPress: () => { this.attachPhoto_onPress(); } });
|
||||
output.push({ title: _('Attach any file'), onPress: () => { this.attachFile_onPress(); } });
|
||||
output.push({ isDivider: true });
|
||||
}
|
||||
@@ -555,6 +579,13 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
const folder = this.state.folder;
|
||||
const isNew = !note.id;
|
||||
|
||||
if (this.state.showCamera) {
|
||||
return <CameraView theme={this.props.theme} style={{flex:1}} onPhoto={this.cameraView_onPhoto} onCancel={this.cameraView_onCancel}/>
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
let bodyComponent = null;
|
||||
if (this.state.mode == 'view') {
|
||||
const onCheckboxChange = (newBody) => {
|
||||
|
||||
Reference in New Issue
Block a user