Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Audio message functionality #247

Merged
merged 23 commits into from
Mar 7, 2018
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
5ea492c
hotfix for ios
ggazzo Mar 5, 2018
54cf7c7
Merge branch 'develop' of github.com:RocketChat/Rocket.Chat.ReactNati…
ggazzo Mar 5, 2018
751013f
[NEW] Add module react-native-audio
kb0304 Mar 5, 2018
188006c
[FIX] Uncomment version in android gradle properties, commit package-…
kb0304 Mar 6, 2018
61f60e3
Merge branch 'develop' of github.com:RocketChat/Rocket.Chat.ReactNati…
ggazzo Mar 6, 2018
2a45056
[WIP] Audio message basic UI
kb0304 Mar 6, 2018
657aa25
[NEW] Record audio message
kb0304 Mar 6, 2018
e4ce3ef
[NEW] [WIP] Audio Message - fileMessage itegration
kb0304 Mar 6, 2018
02722aa
[FIX] Delete single object in Realm
kb0304 Mar 6, 2018
2c30e4a
[FIX] Audio message timer formatting
kb0304 Mar 6, 2018
89a1007
[FIX] Remove duplicate dependency entry
kb0304 Mar 6, 2018
b325177
Merge branch 'develop' into audio-message
ggazzo Mar 6, 2018
c223477
Merge branch 'audio-message' of github.com:kb0304/Rocket.Chat.ReactNa…
ggazzo Mar 6, 2018
c0b2417
[FIX] Fix lint
kb0304 Mar 6, 2018
ab3ccf7
Merge branch 'audio-message' of github.com:kb0304/Rocket.Chat.ReactNa…
kb0304 Mar 6, 2018
b3e45a3
improve audio code
ggazzo Mar 6, 2018
ca3d242
Merge branch 'audio-message' of github.com:kb0304/Rocket.Chat.ReactNa…
ggazzo Mar 6, 2018
73fb94e
fix lint
ggazzo Mar 6, 2018
93a647a
removed version code
ggazzo Mar 6, 2018
d5abd53
[FIX] Cancel recording state fix
kb0304 Mar 6, 2018
8d8ddee
Merge branch 'audio-message' of github.com:kb0304/Rocket.Chat.ReactNa…
kb0304 Mar 6, 2018
1b9893a
Fix rerender due to recordingCanceled
kb0304 Mar 7, 2018
a6fed26
Update Recording.js
ggazzo Mar 7, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ android {
}

dependencies {
compile project(':react-native-audio')
compile project(":reactnativekeyboardinput")
compile project(':react-native-splash-screen')
compile project(':react-native-video')
Expand Down
1 change: 1 addition & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.VIBRATE"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import com.brentvatne.react.ReactVideoPackage;
import com.remobile.toast.RCTToastPackage;
import com.wix.reactnativekeyboardinput.KeyboardInputPackage;
import com.rnim.rn.audio.ReactNativeAudioPackage;

import java.util.Arrays;
import java.util.List;
Expand Down Expand Up @@ -44,6 +45,7 @@ protected List<ReactPackage> getPackages() {
new ReactVideoPackage(),
new SplashScreenReactPackage(),
new RCTToastPackage(),
new ReactNativeAudioPackage(),
new KeyboardInputPackage(MainApplication.this),
new RocketChatNativePackage()
);
Expand Down
2 changes: 1 addition & 1 deletion android/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@
# org.gradle.parallel=true

android.useDeprecatedNdk=true
VERSIONCODE=999999999
# VERSIONCODE=999999999
2 changes: 2 additions & 0 deletions android/settings.gradle
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
rootProject.name = 'RocketChatRN'
include ':react-native-audio'
project(':react-native-audio').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-audio/android')
include ':reactnativekeyboardinput'
project(':reactnativekeyboardinput').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-keyboard-input/lib/android')
include ':react-native-splash-screen'
Expand Down
128 changes: 128 additions & 0 deletions app/containers/MessageBox/Recording.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import React from 'react';
import PropTypes from 'prop-types';
import { View, SafeAreaView, Platform, PermissionsAndroid, Text } from 'react-native';
import { AudioRecorder, AudioUtils } from 'react-native-audio';
import Icon from 'react-native-vector-icons/MaterialIcons';
import styles from './styles';

export const _formatTime = function(seconds) {
let minutes = Math.floor(seconds / 60);
seconds %= 60;
if (minutes < 10) { minutes = `0${ minutes }`; }
if (seconds < 10) { seconds = `0${ seconds }`; }
return `${ minutes }:${ seconds }`;
};


export default class extends React.PureComponent {
static propTypes = {
onFinish: PropTypes.func.isRequired
}

static async permission() {
if (Platform.OS !== 'android') {
return true;
}

const rationale = {
title: 'Microphone Permission',
message: 'Rocket Chat needs access to your microphone so you can send audio message.'
};

const result = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.RECORD_AUDIO, rationale);
return result === true || result === PermissionsAndroid.RESULTS.GRANTED;
}

constructor() {
super();


this.state = {
currentTime: '00:00',
recordingCanceled: false
};
}

componentDidMount() {
const audioPath = `${ AudioUtils.CachesDirectoryPath }/${ Date.now() }.aac`;

AudioRecorder.prepareRecordingAtPath(audioPath, {
SampleRate: 22050,
Channels: 1,
AudioQuality: 'Low',
AudioEncoding: 'aac'
});

AudioRecorder.onProgress = (data) => {
this.setState({
currentTime: _formatTime(Math.floor(data.currentTime))
});
};
//
AudioRecorder.onFinished = (data) => {
if (!this.state.recordingCanceled && Platform.OS === 'ios') {
this._finishRecording(data.status === 'OK', data.audioFileURL);
}
};
AudioRecorder.startRecording();
}

_finishRecording(didSucceed, filePath) {
if (!didSucceed) {
return this.props.onFinish && this.props.onFinish(didSucceed);
}

const path = filePath.startsWith('file://') ? filePath.split('file://')[1] : filePath;
const fileInfo = {
type: 'audio/aac',
store: 'Uploads',
path
};
return this.props.onFinish && this.props.onFinish(fileInfo);
}

finishAudioMessage = async() => {
try {
const filePath = await AudioRecorder.stopRecording();
if (Platform.OS === 'android') {
this._finishRecording(true, filePath);
}
} catch (err) {
this._finishRecording(false);
console.error(err);
}
}

cancelAudioMessage = async() => {
await AudioRecorder.stopRecording();
return this._finishRecording(false);
}

render() {
return (
<SafeAreaView
key='messagebox'
style={styles.textBox}
>
<View style={[styles.textArea, { backgroundColor: '#F6F7F9' }]}>
<Icon
style={[styles.actionButtons, { color: 'red' }]}
name='clear'
key='clear'
accessibilityLabel='Cancel recording'
accessibilityTraits='button'
onPress={this.cancelAudioMessage}
/>
<Text key='currentTime' style={[styles.textBoxInput, { width: 50, height: 60 }]}>{this.state.currentTime}</Text>
<Icon
style={[styles.actionButtons, { color: 'green' }]}
name='check'
key='check'
accessibilityLabel='Finish recording'
accessibilityTraits='button'
onPress={this.finishAudioMessage}
/>
</View>
</SafeAreaView>);
}
}
49 changes: 39 additions & 10 deletions app/containers/MessageBox/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import ImagePicker from 'react-native-image-picker';
import { connect } from 'react-redux';
import { emojify } from 'react-emojione';
import { KeyboardAccessoryView } from 'react-native-keyboard-input';

import { userTyping, layoutAnimation } from '../../actions/room';
import RocketChat from '../../lib/rocketchat';
import { editRequest, editCancel, clearInput } from '../../actions/messages';
Expand All @@ -15,8 +16,10 @@ import database from '../../lib/realm';
import Avatar from '../Avatar';
import CustomEmoji from '../EmojiPicker/CustomEmoji';
import { emojis } from '../../emojis';
import Recording from './Recording';
import './EmojiKeyboard';


const MENTIONS_TRACKING_TYPE_USERS = '@';
const MENTIONS_TRACKING_TYPE_EMOJIS = ':';

Expand Down Expand Up @@ -57,7 +60,7 @@ export default class MessageBox extends React.PureComponent {
mentions: [],
showMentionsContainer: false,
showEmojiKeyboard: false,
trackingType: ''
recording: false
};
this.users = [];
this.rooms = [];
Expand Down Expand Up @@ -138,16 +141,23 @@ export default class MessageBox extends React.PureComponent {
accessibilityTraits='button'
onPress={() => this.submit(this.state.text)}
/>);
} else {
icons.push(<MyIcon
style={[styles.actionButtons, { color: '#2F343D', fontSize: 16 }]}
name='plus'
key='fileIcon'
accessibilityLabel='Message actions'
accessibilityTraits='button'
onPress={() => this.addFile()}
/>);
return icons;
}
icons.push(<Icon
style={[styles.actionButtons, { color: '#1D74F5', paddingHorizontal: 10 }]}
name='mic'
accessibilityLabel='Send audio message'
accessibilityTraits='button'
onPress={() => this.recordAudioMessage()}
/>);
icons.push(<MyIcon
style={[styles.actionButtons, { color: '#2F343D', fontSize: 16 }]}
name='plus'
key='fileIcon'
accessibilityLabel='Message actions'
accessibilityTraits='button'
onPress={() => this.addFile()}
/>);
return icons;
}

Expand Down Expand Up @@ -188,9 +198,25 @@ export default class MessageBox extends React.PureComponent {
showEmojiKeyboard: true
});
}

async recordAudioMessage() {
const recording = await Recording.permission();
this.setState({ recording });
}

finishAudioMessage = async(fileInfo) => {
if (fileInfo) {
RocketChat.sendFileMessage(this.props.rid, fileInfo);
}
this.setState({
recording: false
});
}

closeEmoji() {
this.setState({ showEmojiKeyboard: false });
}

submit(message) {
this.setState({ text: '' });
this.closeEmoji();
Expand Down Expand Up @@ -446,6 +472,9 @@ export default class MessageBox extends React.PureComponent {
);

renderContent() {
if (this.state.recording) {
return (<Recording onFinish={this.finishAudioMessage} />);
}
return (
[
this.renderMentions(),
Expand Down
2 changes: 1 addition & 1 deletion app/containers/message/Audio.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export default class Audio extends React.PureComponent {
}

onLoad(data) {
this.setState({ duration: data.duration });
this.setState({ duration: data.duration > 0 ? data.duration : 0 });
}

onProgress(data) {
Expand Down
3 changes: 3 additions & 0 deletions app/lib/realm.js
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,9 @@ class DB {
deleteAll(...args) {
return this.database.write(() => this.database.deleteAll(...args));
}
delete(...args) {
return this.database.delete(...args);
}
write(...args) {
return this.database.write(...args);
}
Expand Down
22 changes: 16 additions & 6 deletions app/lib/rocketchat.js
Original file line number Diff line number Diff line change
Expand Up @@ -519,10 +519,16 @@ const RocketChat = {
return call('sendFileMessage', rid, null, data, msg);
},
async sendFileMessage(rid, fileInfo, data) {
const placeholder = RocketChat.getMessage(rid, 'Sending an image');
const placeholder = RocketChat.getMessage(rid, 'Sending a file');
try {
const result = await RocketChat._ufsCreate({ ...fileInfo, rid });
if (!data) {
data = await RNFetchBlob.wrap(fileInfo.path);
const fileStat = await RNFetchBlob.fs.stat(fileInfo.path);
fileInfo.size = fileStat.size;
fileInfo.name = fileStat.filename;
}

const result = await RocketChat._ufsCreate({ ...fileInfo, rid });
await RNFetchBlob.fetch('POST', result.url, {
'Content-Type': 'application/octet-stream'
}, data);
Expand All @@ -539,10 +545,14 @@ const RocketChat = {
} catch (e) {
return e;
} finally {
database.write(() => {
const msg = database.objects('messages').filtered('_id = $0', placeholder._id);
database.delete(msg);
});
try {
database.write(() => {
const msg = database.objects('messages').filtered('_id = $0', placeholder._id);
database.delete(msg);
});
} catch (e) {
console.error(e);
}
}
},
async getRooms() {
Expand Down
Loading