can't set response from api to messages array of GiftedChat - react-native

I am new to react native. I am currently developing a messaging app.
I have used npm-giftedChat for UI & functionalities. The problem is I need to get the response from api & set it to the messages array of giftedchat. I receive data from API and while I set it to messages array it loops over data and renders only the last data in that array.
Any help would be appreciated.I have added my code here
Please find where I am going wrong?
componentWillMount() {
var arrMsg = [];
var data = params.data
for(let i = 0; i < data.Replies.length ; i++){
var obj = {
_id: data.To._id,
text: data.Replies[i].Reply,
createdAt: data.Replies[i].CreatedDate,
user: {
_id: data.From._id,
name: 'React Native',
avatar: data.From.Profile.DisplayPicture
},
image: '',
}
arrMsg.push(obj)
}
this.setState({messages: arrMsg})
}
Sample output

My self also facing same issues..
setting is very important in gifted chat..
so try to use following in ur code,i have edited same like your code.if any queries let me know thanks.
for (let i = 0; i < data.Replies.length; i++) {
console.log(data.Replies[i].CreatedDate);
debugger
var id = data.From._id
if (data.To.id == UserID) {
id = this.state.userID
}
const obj = {
_id: Math.round(Math.random() * 1000000),
text: data.Replies[i].Reply,
createdAt: data.Replies[i].CreatedDate,
user: {
_id: id,
name: 'React Native',
avatar: data.From.Profile.DisplayPicture
},
image: '',
}
arrMsg.push(obj);
};
this.setState((previousState) => {
return {
messages: GiftedChat.append(previousState.messages, arrMsg)
};
});

I wrote a gist here on how to add a web socket listening to a rails channel to a react native chat screen + Gifted Chat
// chat.js
import React, { Component } from 'react';
import {
Text,
View,
StyleSheet,
TouchableHighlight,
Dimensions,
AppState,
AsyncStorage,
Alert
} from 'react-native';
import {
GiftedChat,
Actions,
Bubble,
SystemMessage
} from 'react-native-gifted-chat';
import axios from 'axios';
import ActionCable from 'react-native-actioncable';
import { yourRootUrl, websocketUrl } from '../config/constants';
class Chat extends Component {
state = {
messages: [],
client: '',
accessToken: '',
expiry: '',
uid: '',
userId: ''
}
componentDidMount() {
AsyncStorage.multiGet(
['client', 'expiry',
'access_token', 'uid',
'account_balance', 'userId'
]
)
.then((result) => {
this.setState({
client: result[0][1],
expiry: result[1][1],
accessToken: result[2][1],
uid: result[3][1],
userId: result[5][1]
});
})
.then(() => {
this.getPreviousMessages();
})
.then(() => {
this.createSocket();
})
.catch(() => {
//error logic
});
}
getPreviousMessages() {
//when we open the chat page we should load previous messages
const { chatId } = this.props.navigation.state.params;
const { client, accessToken, uid, userId } = this.state;
const url = yourRootUrl + '/chats/' + chatId;
const headers = {
'access-token': accessToken,
client,
expiry,
uid
};
axios.get(url, { headers })
.then((response) => {
/*
lets construct our messages to be in
same format as expected by GiftedChat
*/
const allMessages = [];
response.data.included.forEach((x) => {
if (x.attributes.system) {
const sysMessage = {
_id: x.id,
text: x.attributes['message-text'],
createdAt: new Date(x.attributes['created-at']),
system: true
};
allMessages.push(sysMessage);
} else {
const userMessage = {
_id: x.id,
text: x.attributes['message-text'],
createdAt: new Date(x.attributes['created-at']),
user: {
_id: x.attributes['sender-id'],
avatar: x.attributes['sender-avatar'],
},
image: x.attributes.image,
};
allMessages.push(userMessage);
}
});
if (allMessages.length === response.data.included.length) {
//lets sort messages according to date created
const sortAllMessages = allMessages.sort((a, b) =>
b.createdAt - a.createdAt
);
this.setState((previousState) => {
return {
messages: GiftedChat.append(previousState.messages, sortAllMessages)
};
});
}
})
}
createSocket() {
//assuming you have set up your chatchannel in your rails backend
const { client, accessToken, uid, userId } = this.state;
const { chatId } = this.props.navigation.state.params; //using react-navigation
const WEBSOCKET_HOST = websocketUrl +
'access-token=' + accessToken + '&client=' +
client + '&uid=' + uid;
const cable = ActionCable.createConsumer(WEBSOCKET_HOST);
this.channel = cable.subscriptions.create(
{
channel: 'ChatChannel',
id: chatId
}, {
received: (data) => {
console.log('Received Data:', data);
if ((data.message.sender_id !== parseInt(userId))
|| (data.message.image !== null)) {
//ensuring you do not pick up your own messages
if (data.message.system === true) {
const sysMessage = {
_id: data.message.id,
text: data.message.message_text,
createdAt: new Date(data.message.created_at),
system: true
};
this.setState((previousState) => {
return {
messages: GiftedChat.append(previousState.messages, sysMessage)
};
});
} else {
const userMessage = {
_id: data.message.id,
text: data.message.message_text,
createdAt: new Date(data.message.created_at),
user: {
_id: data.message.sender_id,
avatar: data.message.sender_avatar,
},
image: data.message.image,
};
this.setState((previousState) => {
return {
messages: GiftedChat.append(previousState.messages, userMessage)
};
});
}
}
},
connected: () => {
console.log(`Connected ${chatId}`);
},
disconnected: () => {
console.warn(`${chatId} was disconnected.`);
},
rejected: () => {
console.warn('connection rejected');
},
});
}
onSend(messages = []) {
const { chatId } = this.props.navigation.state.params;
const { client, accessToken, uid, userId } = this.state;
this.setState((previousState) => {
return {
messages: GiftedChat.append(previousState.messages, messages)
};
});
messages.forEach((x) => {
const url = yourRootUrl + '/messages';
const headers = {
'access-token': accessToken,
client,
expiry,
uid
};
const data = {
chat_id: chatId,
sender_id: userId,
sender_name: name,
message_text: x.text,
image: x.image
};
/*
send the message to your rails app backend
hopefully you have a callback in your model like
after_create :broadcast_message
then broadcast to the chat channel from your rails backend
*/
axios.post(url, data, { headers })
.then(response => console.log(response));
});
}
renderBubble(props) {
return (
<Bubble
{...props}
wrapperStyle={{
left: {
backgroundColor: '#f9f9f9',
}
}}
/>
);
}
renderSystemMessage(props) {
return (
<SystemMessage
{...props}
containerStyle={{
marginBottom: 15,
}}
textStyle={{
fontSize: 14,
textAlign: 'center'
}}
/>
);
}
render() {
return (
<GiftedChat
messages={this.state.messages}
onSend={message => this.onSend(message)}
user={{
_id: parseInt(userId)
}}
renderBubble={this.renderBubble}
renderSystemMessage={this.renderSystemMessage}
/>
);
}
}

Related

Multiple useEffect in react-native to achieve mentioned functionality

I need help with the async nature of Async storage and axios api. Here's the functionality that I am trying to achieve ->
send request to two separate api to get some data.
display that data on the screen with some additional text
api request are authenticated so a token is passed as Authentication Header
I have attached the current implementation, I am having the a number of errors in this
Errors:
Login_token not set in state after fetching from Async Storage.
Data not set in state after api call
both resulting in either failed api calls or undefined state errors on render
This is my code.
import React, { FunctionComponent, useEffect, useCallback, useState} from 'react';
import { StyleSheet, View} from 'react-native';
// chat
import { GiftedChat } from 'react-native-gifted-chat';
// navigation
import { RootStackParamList } from '../../navigators/RootStack';
import { StackScreenProps } from '#react-navigation/stack';
export type Props = StackScreenProps<RootStackParamList, "Chat">;
// api
import { Convo_details, Send_Msg, List_Msg, Expert_Public_Profile } from '../../api/UserApi';
import Spinner from 'react-native-loading-spinner-overlay';
import AsyncStorage from '#react-native-async-storage/async-storage';
import uuid from 'react-native-uuid';
const Chat: FunctionComponent<Props> = ({ navigation, route, ...props }) => {
// console.log(props.route.params);
const [login_token, setlogin_token] = useState('')
const [conversation_id, setconversation_id] = useState('')
const [conversation_details, setconversation_details] = useState({})
const [currentuser, setcurrentuser] = useState({})
const [loading, setLoading] = useState(false);
const [expertuid, setexpertuid] = useState('')
const [ExpertProfile, setExpertProfile] = useState({})
const [messages, setMessages] = useState([]);
useEffect(() => {
getlogintoken()
console.log("####################################","getlogintoken");
}, [])
/* conversationid */
useEffect(() => {
if (route.params != null) {
setconversation_id(route.params[0])
}
console.log("####################################","conversation id");
}, [])
/* expert uid */
useEffect(() => {
if (route.params != null) {
setexpertuid(route.params[1])
}
console.log("####################################","expert uid");
}, [])
/* expert public profile */
useEffect(() => {
getexpertpublicprofile()
getConvo_details()
console.log("####################################","convo_details");
}, [])
useEffect(() => {
// get current user
AsyncStorage.getItem("currentuser").then(res => {
if (res != null) setcurrentuser(res)
else alert("Current user not found")
})
console.log("####################################","current user");
}, [])
// set welcome msg
useEffect(() => {
if (Object.keys(conversation_details).length != 0 && Object.keys(ExpertProfile).length != 0)
setwelcomemsg()
}, [])
const onSend = useCallback(async (messages = []) => {
// console.log(messages[0].text);
setMessages(previousMessages => GiftedChat.append(previousMessages, messages))
const data = {
conversation_id: "f98d6851-a713-4f58-9118-77a779ff175f",//conversation_id,
message_type: "TEXT",
body: messages[0].text
}
const res: any = await Send_Msg(data, login_token)
.catch(error => {
alert(`Send_Msg -> ${error}`)
console.log(error);
return
})
if (res.status == 200) {
console.log(res.data);
} else console.log(res);
}, [])
const getexpertpublicprofile = async () => {
setLoading(true)
const res: any = await Expert_Public_Profile(expertuid, login_token)
.catch(error => {
setLoading(false)
console.log("Expert public profile ->");
alert(`Expert public profile ->${error.message}`)
console.log(error);
return
})
setLoading(false)
if (res.status === 200) setExpertProfile(res.data)
else {
alert(`get expert public profile${res.data.message}`)
console.log("getexpertpublicprofile -->");
console.log(res.data);
}
}
const getlogintoken = () => {
AsyncStorage.getItem("login_token").then(res => {
if (res != null) {
setLoading(false)
setlogin_token(res)
}
else alert("No login token found")
})
}
const getConvo_details = async () => {
setLoading(true)
const res: any = await Convo_details(conversation_id, login_token)
.catch(error => {
setLoading(false)
alert(`Convo_details-->${error.message}`)
console.log("Convo_details -->");
console.log(error);
return
})
setLoading(false)
if (res.status === 200) setconversation_details(res.data)
else {
alert(`get convo details-> ${res.data.message}`)
console.log("getConvo_details -->");
console.log(res.data);
}
}
const setwelcomemsg = () => {
try {
let user = JSON.parse(currentuser)
let messages = [
{
_id: uuid.v4().toString(),
conversation_id: conversation_details.conversation_id,
created_at: new Date(),
from: conversation_details.recipient.user_uid,
type: "TEXT",
text: `About Me - ${ExpertProfile.bio}`,
user: {
_id: conversation_details.recipient.user_uid,
}
},
{
_id: uuid.v4().toString(),
conversation_id: conversation_details.conversation_id,
created_at: new Date(),
from: conversation_details.recipient.user_uid,
type: "TEXT",
text: `My name is ${conversation_details.recipient.name}`,
user: {
_id: conversation_details.recipient.user_uid,
}
},
{
_id: uuid.v4().toString(),
conversation_id: conversation_details.conversation_id,
created_at: new Date(),
from: conversation_details.recipient.user_uid,
type: "TEXT",
text: `Hi ${user.full_name}`,
user: {
_id: conversation_details.recipient.user_uid,
}
}]
setMessages(previousMessages => GiftedChat.append(previousMessages, messages))
} catch (error) {
console.log("try -> set welcome msg");
console.log(error);
return
}
}
return (
<View style={styles.maincontainer}>
<Spinner
visible={loading}
textContent={'Loading...'}
textStyle={{ color: '#FFF' }}
/>
<GiftedChat
messages={messages}
onSend={messages => onSend(messages)}
user={{
_id: currentuser.user_uid,
}}
isTyping={false}
scrollToBottom={true}
showAvatarForEveryMessage={true}
renderAvatar={() => null}
/>
</View>
);
}
export default Chat;
const styles = StyleSheet.create({
maincontainer: {
flex: 1,
},
});
When axios returns, it usually give the response as res.data, so in your case, try either res.data or res.data.yourToken (I'm not sure how it's your object).
Gurav,
As far as your code above, The api call's will trigger even before you get currentuser or loginToken. You have to handle the api call after getting the currentuser and loginToken. This can be gracefully handled with async, await.
example code:
useEffect(() => {
getData()
}, [])
useEffect(() => {
if(login_token && currentuser) {
//The api call goes here after you get the logintoken andcurrentuser.
// The above condition is just an example but will vary based on your requirements
}
}, [login_token, currentuser])
const getData = async () => {
await getlogintoken()
await getcurrentuser()
}
const getlogintoken = async () => {
await AsyncStorage.getItem("login_token").then(res => {
if (res != null) {
setLoading(false)
setlogin_token(res)
}
else alert("No login token found")
})
}
const getcurrentuser = async () => {
await AsyncStorage.getItem("currentuser").then(res => {
if (res != null) setcurrentuser(res)
else alert("Current user not found")
})
}

How to re-run useQuery and FlatList?

I use FlatList with useState.
const [state, setState] = useState(route);
<FlatList
keyboardDismissMode={true}
showsVerticalScrollIndicator={false}
data={state}
keyExtractor={(comment) => "" + comment.id}
renderItem={renderComment}
/>
When I change the datㅁ which is contained in state, I want to re-run Flatlist with new data.
So after I mutate my data, I try to rerun useQuery first in order to change state. I put refetch module here.
1)
const { data: updatePhoto, refetch } = useQuery(SEE_PHOTO_QUERY, {
variables: {
id: route?.params?.photoId,
},
});
If I put button, this onValid function will executed.
<ConfirmButton onPress={handleSubmit(onValid)}>
onValid function changes data and after all finished, as you can see I put refetch().
=> all this process is for that if I add comment and press confirm button, UI (flatlist) should be changed.
const onValid = async ({ comments }) => {
await createCommentMutation({
variables: {
photoId: route?.params?.photoId,
payload: comments,
},
});
await refetch();
console.log(updatePhoto);
};
But when I console.log data after all, it doesnt' contain added data..
what is the problem here?
If you need more explanation, I can answer in real time.
please help me.
add full code
export default function Comments({ route }) {
const { data: userData } = useMe();
const { register, handleSubmit, setValue, getValues } = useForm();
const [state, setState] = useState(route);
const [update, setUpdate] = useState(false);
const navigation = useNavigation();
useEffect(() => {
setState(route?.params?.comments);
}, [state, route]);
const renderComment = ({ item: comments }) => {
return <CommentRow comments={comments} photoId={route?.params?.photoId} />;
};
const { data: updatePhoto, refetch } = useQuery(SEE_PHOTO_QUERY, {
variables: {
id: route?.params?.photoId,
},
});
const createCommentUpdate = (cache, result) => {
const { comments } = getValues();
const {
data: {
createComment: { ok, id, error },
},
} = result;
if (ok) {
const newComment = {
__typename: "Comment",
createdAt: Date.now() + "",
id,
isMine: true,
payload: comments,
user: {
__typename: "User",
avatar: userData?.me?.avatar,
username: userData?.me?.username,
},
};
const newCacheComment = cache.writeFragment({
data: newComment,
fragment: gql`
fragment BSName on Comment {
id
createdAt
isMine
payload
user {
username
avatar
}
}
`,
});
cache.modify({
id: `Photo:${route?.params?.photoId}`,
fields: {
comments(prev) {
return [...prev, newCacheComment];
},
commentNumber(prev) {
return prev + 1;
},
},
});
}
};
const [createCommentMutation] = useMutation(CREATE_COMMENT_MUTATION, {
update: createCommentUpdate,
});
const onValid = async ({ comments }) => {
await createCommentMutation({
variables: {
photoId: route?.params?.photoId,
payload: comments,
},
});
await refetch();
console.log(updatePhoto);
};

Mobile project with expo and hapi.js

Im strugglling with an error that im not capable of get a solution and i dont know if its happening in the front or in the back of the app.
Im trying to follow a tutorial where the guy is setting up an app that records your voice and return it as text from Google API speech to text.
this is the front part, the app is failing in the axios.post, the request is not passing and keeps loading until the error log says it cannot connect to the path.
import React, { Component } from 'react'
import {
StyleSheet, Text, View, TouchableOpacity, ActivityIndicator, Platform,
} from 'react-native'
// import { Audio, Permissions, FileSystem } from 'expo'
import * as Permissions from 'expo-permissions'
import { Audio } from 'expo-av'
import * as FileSystem from 'expo-file-system';
import axios from 'axios'
const styles = StyleSheet.create({
container: {
marginTop: 40,
backgroundColor: '#fff',
alignItems: 'center',
},
button: {
backgroundColor: '#1e88e5',
paddingVertical: 20,
width: '90%',
alignItems: 'center',
borderRadius: 5,
padding: 8,
marginTop: 20,
},
text: {
color: '#fff',
}
})
/*this.recordingSettings = JSON.parse(JSON.stringify(Audio.RECORDING_OPTIONS_PRESET_HIGH_QUALITY = */let recordingOptions = {
android: {
extension: '.m4a',
outputFormat: Audio.RECORDING_OPTION_ANDROID_OUTPUT_FORMAT_MPEG_4,
audioEncoder: Audio.RECORDING_OPTION_ANDROID_AUDIO_ENCODER_AAC,
sampleRate: 44100,
numberOfChannels: 2,
bitRate: 128000,
},
ios: {
extension: '.m4a',
outputFormat: Audio.RECORDING_OPTION_IOS_OUTPUT_FORMAT_MPEG4AAC,
audioQuality: Audio.RECORDING_OPTION_IOS_AUDIO_QUALITY_HIGH,
sampleRate: 44100,
numberOfChannels: 2,
bitRate: 128000,
linearPCMBitDepth: 16,
linearPCMIsBigEndian: false,
linearPCMIsFloat: false,
},
};
export default class SpeechToTextButton extends Component {
constructor(props) {
super(props)
this.recording = null
this.state = {
isRecording: false,
//we would like to know if data fetching is in progress
isFetching: false,
//we will write the transcript result here
transcript: '',
}
}
startRecording = async () => {
// request permissions to record audio
const { status } = await Permissions.askAsync(Permissions.AUDIO_RECORDING)
// if the user doesn't allow us to do so - return as we can't do anything further :(
if (status !== 'granted') return
// when status is granted - setting up our state
this.setState({ isRecording: true })
// basic settings before we start recording,
// you can read more about each of them in expo documentation on Audio
await Audio.setAudioModeAsync({
allowsRecordingIOS: true,
interruptionModeIOS: Audio.INTERRUPTION_MODE_IOS_DO_NOT_MIX,
playsInSilentModeIOS: true,
interruptionModeAndroid: Audio.INTERRUPTION_MODE_ANDROID_DO_NOT_MIX,
playThroughEarpieceAndroid: true,
})
const recording = new Audio.Recording()
try {
// here we pass our recording options
await recording.prepareToRecordAsync(recordingOptions)
// and finally start the record
await recording.startAsync()
console.log('funcionaaa')
} catch (error) {
console.log(error)
// we will take a closer look at stopRecording function further in this article
this.stopRecording()
console.log('no funca')
}
// if recording was successful we store the result in variable,
// so we can refer to it from other functions of our component
this.recording = recording
}
stopRecording = async () => {
// set our state to false, so the UI knows that we've stopped the recording
this.setState({ isRecording: false })
try {
// stop the recording
await this.recording.stopAndUnloadAsync()
console.log('aca tiene que parar')
} catch (error) {
console.log(error)
}
}
getTranscription = async () => {
// set isFetching to true, so the UI knows about it
this.setState({ isFetching: true })
try {
// take the uri of the recorded audio from the file system
const { uri } = await FileSystem.getInfoAsync(this.recording.getURI())
// now we create formData which will be sent to our backend
const formData = new FormData()
formData.append('file', {
uri,
// as different audio types are used for android and ios - we should handle it
type: Platform.OS === 'ios' ? 'audio/x-wav' : 'audio/m4a',
name: Platform.OS === 'ios' ? `${Date.now()}.wav` : `${Date.now()}.m4a`,
})
// post the formData to our backend
const { data } = await axios.post('http://190.19.68.120/api/speech', formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
})
console.log(data)
// set transcript from the data which we received from the api
this.setState({ transcript: data.transcript })
} catch (error) {
console.log('There was an error reading file', error.request)
this.stopRecording()
// we will take a closer look at resetRecording function further down
this.resetRecording()
}
// set isFetching to false so the UI can properly react on that
this.setState({ isFetching: false })
}
deleteRecordingFile = async () => {
// deleting file
try {
const info = await FileSystem.getInfoAsync(this.recording.getURI())
await FileSystem.deleteAsync(info.uri)
} catch (error) {
console.log('There was an error deleting recorded file', error)
}
}
resetRecording = () => {
this.deleteRecordingFile()
this.recording = null
}
handleOnPressOut = () => {
// first we stop the recording
this.stopRecording()
console.log('para en el pressout')
// second we interact with our backend
this.getTranscription()
}
render() {
const {
isRecording, transcript, isFetching,
} = this.state
return (
<View style={styles.container}>
<TouchableOpacity
style={styles.button}
onPressIn={this.startRecording}
onPressOut={this.handleOnPressOut}
>
{isFetching && <ActivityIndicator color="#ffffff" />}
{!isFetching &&
<Text style={styles.text}>
{isRecording ? 'Recording...' : 'Start recording'}
</Text>
}
</TouchableOpacity>
<Text>
{transcript}
</Text>
</View>
)
}
}
Here it is the back part, where i receive the request from the front and send the audio to the google API
'use strict';
const Hapi = require('#hapi/hapi');
const fs = require('fs')
const axios = require('axios')
const speech = require('#google-cloud/speech');
const ffmpeg = require('fluent-ffmpeg');
const client = new speech.SpeechClient();
const init = async () => {
const server = Hapi.server({
port: 3005,
host: 'localhost'
});
server.route({
method: 'POST',
path: '/speech',
config: {
handler: async (request, h) => {
const data = request.payload;
console.log(data)
if (data.file) {
const name = data.file.hapi.filename;
const path = __dirname + "/uploads/" + name;
const encodedPath = __dirname + "/uploads/encoded_" + name;
const file = fs.createWriteStream(path);
file.on('error', (err) => console.error(err));
data.file.pipe(file);
return new Promise(resolve => {
data.file.on('end', async (err) => {
const ret = {
filename: data.name,
headers: data.file.hapi.headers
}
ffmpeg()
.input(path)
.outputOptions([
'-f s16le',
'-acodec pcm_s16le',
'-vn',
'-ac 1',
'-ar 41k',
'-map_metadata -1'
])
.save(encodedPath)
.on('end', async () => {
const savedFile = fs.readFileSync(encodedPath)
const audioBytes = savedFile.toString('base64');
const audio = {
content: audioBytes,
}
const sttConfig = {
enableAutomaticPunctuation: false,
encoding: "LINEAR16",
sampleRateHertz: 41000,
languageCode: "en-US",
model: "default"
}
const request = {
audio: audio,
config: sttConfig,
}
const [response] = await client.recognize(request);
const transcription = response.results
.map(result => result.alternatives[0].transcript)
.join('\n');
fs.unlinkSync(path)
fs.unlinkSync(encodedPath)
resolve(JSON.stringify({ ...ret, transcript: transcription }))
})
})
})
}
},
payload: {
output: 'stream',
parse: true,
}
}
})
await server.start();
console.log('Server running on %s', server.info.uri);
};
process.on('unhandledRejection', (err) => {
console.log(err);
});
init();
i thought i could try migrating the server to express.js but under handler, i got payload key, and i dont know what to do with that in an express server.

Undefined is not a function {near This.state} react Native error

In my react native code I am facing this issue. some time it's work and some time it doesn't.
I am beginner in user of React-redux for state management of component any lead will be appreciated.
import React, { PureComponent } from "react"
import PurchaseInvoiceView from "../Components/Purchaseinvoiceview"
import ReturnInvoiceView from "../Components/Returninvoiceview"
import { connect } from "react-redux"
import { translate } from "../i18n"
import { TabView, SceneMap, TabBar } from "react-native-tab-view"
import styles from "./Styles/InvoiceScreenStyle"
import { Cache } from "react-native-cache"
import AsyncStorage from "#react-native-community/async-storage";
import {
getPurchaseInvoice,
getPurchaseInvoiceFromCache,
getReturnInvoice,
getReturnInvoiceFromCache,
} from "../Redux/Actions/invoiceActions"
class InvoiceScreen extends PureComponent {
state = {
index: 0,
routes: [
{ key: "purchase", title: translate("invoiceScreen.purchase") },
{ key: "return", title: translate("invoiceScreen.return") },
],
token: null,
vansale_id: 0,
user: null,
}
componentDidMount() {
const { dispatch, userData } = this.props
this.setState(
{
token: userData.data.package.token || value.package.token,
vansale_id: userData.data.package.id || value.package.id,
},
() => {
const { token, vansale_id } = this.state
dispatch(getPurchaseInvoice(token, { vansale_id}))
.then(res => {
console.log(res);
if (res.payload.status === true) {
var cache = new Cache({
namespace: "FGMM",
policy: {
maxEntries: 50000,
},
backend: AsyncStorage,
})
cache.setItem("invoiceData", res.payload, function(err) {})
}
})
.catch(err => {
var cache = new Cache({
namespace: "FGMM",
policy: {
maxEntries: 50000,
},
backend: AsyncStorage,
})
cache.getItem("invoiceData", function(err, value) {
if (value) {
if (value.status === true) {
dispatch(getPurchaseInvoiceFromCache(value))
}
}
})
})
dispatch(getReturnInvoice(token, { vansale_id,}))
.then(res => {
console.log(res)
if (res.payload.status === true) {
var cache = new Cache({
namespace: "FGMM",
policy: {
maxEntries: 50000,
},
backend: AsyncStorage,
})
cache.setItem("invoiceReturnData", res.payload, function(err) {})
}
})
.catch(err => {
var cache = new Cache({
namespace: "FGMM",
policy: {
maxEntries: 50000,
},
backend: AsyncStorage,
})
cache.getItem("invoiceReturnData", function(err, value) {
if (value) {
if (value.status === true) {
dispatch(getReturnInvoiceFromCache(value))
}
}
})
})
},
)
var cache = new Cache({
namespace: "FGMM",
policy: {
maxEntries: 50000,
},
backend: AsyncStorage,
})
cache.getItem("userData", function(err, value) {
if (value) {
if (value.status === true) {
}
} else {
this.setState({
token: userData.data.package.token || value.package.token,
vansale_id: userData.data.package.id || value.package.id,
})
}
})
}
render() {
return (
<View style={styles.container}>
<TabView
navigationState={this.state}
activeColor="#777"
inactiveColor="#000"
renderTabBar={props => (
<TabBar
{...props}
indicatorStyle={styles.indicator}
style={styles.tabBar}
labelStyle={styles.labelStyle}
/>
)}
renderScene={SceneMap({
purchase: PurchaseInvoiceView,
return: ReturnInvoiceView,
})}
onIndexChange={index => this.setState({ index })}
initialLayout={{ width: Dimensions.get("window").width }}
/>
</View>
)
}
}
const mapStateToProps = state => {
return {
userData: state.userData,
invoiceData: state.invoiceData,
returnInvoiceData: state.invoiceData.return,
}
}
export default connect(mapStateToProps)(InvoiceScreen)
when I am using this on Android it is working perfectly fine, but for an iOS it's crashing

Pusher chatKit onMessage hook fails in Expo app

I am using React Native with Expo, and I am able to create users + rooms and send messages to them with the following code:
const hooks = {
onMessage: message => {
console.log("message", message);
this.setState({
messages: [...this.state.messages, message]
});
},
onUserStartedTyping: user => {
this.setState({
usersWhoAreTyping: [...this.state.usersWhoAreTyping, user.name]
});
},
onUserStoppedTyping: user => {
this.setState({
usersWhoAreTyping: this.state.usersWhoAreTyping.filter(
username => username !== user.name
)
});
},
onPresenceChange: () => this.forceUpdate()
};
class SetupChatKit extends React.Component {
constructor(props) {
super(props);
this.state = {
chatManager: null,
currentUser: {},
currentRoom: {},
messages: [],
usersWhoAreTyping: []
};
}
componentDidMount() {
const { userId, name } = this.props;
this.instantiateChatManager(userId);
this.createChatKitUser({ userId, name });
}
joinOrCreateChatKitRoom = (mode, chatKitRoomId, title) => {
const { chatManager } = this.state;
return chatManager
.connect()
.then(currentUser => {
this.setState({ currentUser });
if (mode === "join") {
return currentUser.joinRoom({ roomId: chatKitRoomId, hooks });
}
return currentUser.createRoom({
name: title,
private: false,
hooks
});
})
.then(currentRoom => {
this.setState({ currentRoom });
return currentRoom.id;
})
.catch(error => console.error("error", error));
};
instantiateChatManager = userId => {
const chatManager = new Chatkit.ChatManager({
instanceLocator: "v1:us1:9c8d8a28-7103-40cf-bbe4-727eb1a2b598",
userId,
tokenProvider: new Chatkit.TokenProvider({
url: `http://${baseUrl}:3000/api/authenticate`
})
});
this.setState({ chatManager });
};
My problem is that console.log("message", message); never gets called, even when I manually add messages to the room via the online control panel.
I've tried logging from chatManager, and that looks like the following:
As you can see from the documentation, the onMessage hook needs to be attached on subscribeRoom, not when joining a room.
https://docs.pusher.com/chatkit/reference/javascript#connection-hooks
So probably add subscribeToRoom() after the first success promise in your joinOrCreateChatKitRoom() method.
I refactored the code with async/await and used .subscribetoRoom() like so:
joinOrCreateChatKitRoom = async (mode, chatKitRoomId, title) => {
const { chatManager } = this.state;
try {
const currentUser = await chatManager.connect();
this.setState({ currentUser });
let currentRoom;
if (mode === "join") {
currentRoom = await currentUser.joinRoom({
roomId: chatKitRoomId
});
} else {
currentRoom = await currentUser.createRoom({
name: title,
private: false
});
}
this.setState({ currentRoom });
await currentUser.subscribeToRoom({
roomId: currentRoom.id,
hooks: {
onMessage: message => {
this.setState({
messages: [...this.state.messages, message]
});
},
onUserStartedTyping: user => {
this.setState({
usersWhoAreTyping: [...this.state.usersWhoAreTyping, user.name]
});
},
onUserStoppedTyping: user => {
this.setState({
usersWhoAreTyping: this.state.usersWhoAreTyping.filter(
username => username !== user.name
)
});
},
onPresenceChange: () => this.forceUpdate()
}
});
return currentRoom.id;
} catch (error) {
console.error("error creating chatManager", error);
}
};