React Native のアプリでチャット機能を実装する際に「react-native-gifted-chat」というライブラリを使用しているのですが、今回は、このライブラリを使って返信(リプライ)機能を実装する手順についてです。
今後も使いそうなので、備忘録かつ、コピー&ペースト元としてまとめ。
実装には、下記の記事を参考にさせていただきました。
Reply for Particular Message · Issue #750 · FaridSafi/react-native-gifted-chat · GitHub
https://github.com/FaridSafi/react-native-gifted-chat/issues/750
実装手順は長くなるので…まず、必要な部分のコードはこちら!
最初に必要なライブラリ等をインポートします。
import React from 'react';
import { StyleSheet, Text, View, TouchableOpacity } from 'react-native';
import { GiftedChat } from 'react-native-gifted-chat';
import moment from 'moment';
import UUID from 'uuidjs';
import Ionicons from 'react-native-vector-icons/Ionicons';
次に、追加する関数がこちら。
/**
* メッセージをロングタップして返信する
*/
this.onLongPress = (context, message) => {
this.setState({ replyMessage: message });
}
/**
* メッセージを送信する
*/
this.onSend = (rawData) => {
const createdAt = moment().format('YYYY-MM-DD HH:mm:ss');
const message = {
_id: UUID.generate(),
text: rawData[0]['text'],
memberNo: 1,
createdAt: createdAt,
reply: this.state.replyMessage,
};
/* ここでメッセージの送信処理を実行する */
this.setState({ replyMessage: null }); // ←メッセージの送信処理が完了したら実行
}
/**
* 吹き出し内に返信元メッセージを表示する
*/
this.renderCustomView = props => (
<View>
{ props.currentMessage.reply ?
<View style={{...styles.replyContainer, ...styles.replyBubbleContainer}}>
<View style={{...styles.replyBorder, backgroundColor: '#0A2B47'}} />
<View>
<Text style={{...styles.replyName, color: '#E0F0FE' }}>{props.currentMessage.reply.user.name}</Text>
<Text numberOfLines={2} ellipsizeMode="tail" style={{...styles.replyText, color: "#E0F0FE" }}>{props.currentMessage.reply.text}</Text>
</View>
</View> : null }
</View>
)
/**
* 返信する対象のメッセージをフッターに表示
*/
this.renderFooter = props => (
<View>
{ this.state.replyMessage ?
<View style={styles.replyContainer}>
<View style={styles.replyBorder} />
<View>
<Text style={styles.replyName}>{this.state.replyMessage.user.name}</Text>
<Text numberOfLines={1} ellipsizeMode="tail" style={styles.replyText}>{this.state.replyMessage.text}</Text>
</View>
<TouchableOpacity onPress={() => this.setState({ replyMessage: null })} style={styles.replyClose}>
<Ionicons name='close-outline' color='#999' size={20} />
</TouchableOpacity>
</View> : null }
</View>
)
最後に、render() の return 内に記述する <GiftedChat /> がこちら。
なお、追加するのは最後の 3行、renderCustomView、renderFooter、onLongPress です。
<GiftedChat
messages={this.state.messages}
onSend={messages => this.onSend(messages)}
user={{
_id: [送信者ID],
name: [送信者名],
avatar: [送信者アイコンの画像パス],
}}
placeholder='メッセージを入力'
locale='ja'
timeFormat='H:mm'
dateFormat='M月D日'
keyboardShouldPersistTaps='never'
renderSend={this.renderSend}
renderCustomView={this.renderCustomView}
renderFooter={this.renderFooter}
onLongPress={this.onLongPress} />
StyleSheet は下記のとおりです。
なお、アプリのメインカラーが青色なので、配色も青系統になっています。
このあたりは、適宜変更してください。
const styles = StyleSheet.create({
replyContainer: {
margin: 5,
backgroundColor: '#E0F0FE',
minHeight: 50,
flexDirection: 'row',
alignItems: 'center',
},
replyBorder: {
backgroundColor: '#2699FB',
width: 5,
height: '100%',
},
replyName: {
paddingTop: 5,
paddingHorizontal: 10,
fontWeight: 'bold',
color: '#666',
},
replyText: {
color: '#999',
paddingHorizontal: 10,
paddingBottom: 5,
},
replyClose: {
position: 'absolute',
padding: 5,
top: 0,
right: 0,
},
replyBubbleContainer: {
marginTop: 10,
borderRadius: 5,
maxWidth: 200,
backgroundColor: '#15578F',
overflow: 'hidden',
},
});
さて、一つずつ説明しますと、まず <GiftedChat /> の onLongPress に、メッセージの吹き出しが長押しされた時の処理を指定します。
これは上記サンプルコードの this.onLongPress で、今回は長押しされたメッセージの情報を state の replyMessage に保存しているだけです。
なお、もしメッセージを長押しした際に実行する処理の候補が複数ある場合は、「react-native-action-sheet」ライブラリで ActionSheet を表示させるなどしてください。
で、state の replyMessage にデータが保存されると、renderFooter で指定されたコンポーネントが描画されます。
サンプルコードでは this.renderFooter で指定されたコンポーネントの内容が描画され、これは state の replyMessage にデータがある場合にのみ描画されます。
実際の画面のスクリーンショットは下記のとおりです。

右上の閉じるアイコンボタンがタップされると、state の replyMessage が削除されるので消えます。
何かメッセージを打ち送信すると、onSend で指定した関数が実行されますが、この時、送信データに reply という項目で返信元のメッセージデータも併せて送信するように修正します。
こちらの処理に関しては this.onSend をご参照ください。
最後は reply データが含まれているメッセージデータを表示する処理で、この処理は renderCustomView を使用します。
this.renderCustomView 内で返信元メッセージを描画する処理を実行しています。
実際の画面は下記のとおりです。

renderCustomView を使うと、自分が入力したメッセージの上に CustomView を描画することができます。
こちらは今回初めて使ったので知りませんでした!
なお、renderCustomView で表示するコンポーネントを、自分が送信したメッセージや画像の下に表示させたい場合は、isCustomViewBottom という Props に true を追加すればOKです。
なお、デフォルトの値は false です。
個人的には、返信元メッセージが自分のメッセージの上に表示されている方がしっくりくるので、今回は指定しませんでした。
実装するコードは以上です。
返信元メッセージをフッターに表示する機能は意外と簡単に実装できたのですが、吹き出し内に返信元メッセージを表示するところで若干苦戦しました…。
最初、renderCustomView を使用することを思いつかなかったので、renderBubble で吹き出しのコンポーネントをカスタマイズすることで対応しようとしてしまいました…。
その後、参考サイトを読み返し、renderCustomView に気がついたので事なきを得ました。
参考サイトがとにかく参考になったので、実装の際は主にこちらを確認しながら作業することをお勧めします。
以上、React Native の「react-native-gifted-chat」ライブラリで返信機能を実装する方法についてでした。
ご参考になれば幸いです。