I am trying to get unread messages from PubNub. The process is like this:
Set the grant
Get channel metadata
get unread messages etc...
I am not having the luck running these functions in order. That one is executed after the previous one is done.
async function setGrant(){
await pubnub.grant(
{
channels: [userId.toString() + '.*'],
ttl: 55,
read: true,
write: true,
update: true,
get: true,
},(status) => {
return new Promise(function (resolve) {
resolve(status)
});
});
}
async function getMetadata() {
await pubnub.objects.getAllChannelMetadata(
{
include: {
customFields: true
}
}).then((res: any) => {
return new Promise(function (resolve) {
resolve(setChannelMetadata(res.data));
});
});
}
async function getUnreadedMessages() {
await pubnub.messageCounts({
channels: channels,
channelTimetokens: tokens,
}).then((res: any) => {
console.log(res);
return new Promise(function (resolve) {
resolve(setUnreadMessage(res));
});
});
}
async function setChannelsAndTokens(){
channelMetadata.forEach((value, index) => {
tokens.push(value.custom.lastToken);
channels.push(value.id)
});
return new Promise(function (resolve) {
getUnreadedMessages()
resolve("Chanells and tokens set!");
})
}
function getUnreadMessagesProccess() {
return setGrant().then(getMetadata).then(setChannelsAndTokens).then(getUnreadedMessages)
}
EDIT:
Looks like this is the problem with rendering too. When I get getUnreadedMessages()
then I am editing objects clients:
clients.forEach((value, index) => {
if (res.channels[value.id + '-' + userId + '-chat']) {
value.unreadMessages = res["channels"][value.id + '-' + userId + '-chat'];
} else {
value.unreadMessages = 0;
}
console.log("UNREAD MESSAGE", value.unreadMessages, value.username);
});
based on this I am setting showIcon:
<ClientItem
client={item.item}
isAdmin={isAdmin}
navigation={navigation}
fromAdminTab={fromAdminTab}
showIcon={item.item.unreadMessages>0}
/>
But this icon is not showing corretly.
ClientList:
import Colors from '#helper/Colors';
import { useSelector } from '#redux/index';
import { HealthierLifeOption } from '#redux/types';
import React, { useEffect, useState } from 'react';
import { View, Text, FlatList, ActivityIndicator } from 'react-native';
import ClientItem from './ClientItem';
import {usePubNub} from "pubnub-react";
export type Client = {
id: number;
username: string;
individualPlanPaid: boolean;
healthierLifeOption?: HealthierLifeOption;
company?: {
id: number;
companyName: string;
};
mentor?: {
id: number;
username: string;
};
}
type Props = {
navigation: any;
clients: Client[];
isAdmin?: boolean;
isLoading: boolean;
onEndReached: Function;
fromAdminTab?: boolean
}
const ClientList = ({ navigation, clients, onEndReached, isLoading, isAdmin = false, fromAdminTab= false }: Props) => {
const resultCount = useSelector(state => state.user.menteesResultCount);
const [hasMoreResults, setHasMoreResults] = useState(true);
const userId = useSelector(state => state.user.id);
const pubnub = usePubNub();
useEffect(() => {
getUnreadMessagesProccess
setHasMoreResults(resultCount !== clients?.length);
}, [resultCount, clients]);
async function setGrant() {
return new Promise( async resolve => {
await pubnub.grant({
channels: [
'100-68-chat',
'101-68-chat',
'22-2-chat',
'22-96-chat',
'73-68-chat',
'78-68-chat',
'79-68-chat',
'81-68-chat',
'90-68-chat',
'92-68-chat',
'93-68-chat',
'98-68-chat',
'99-68-chat' ],
ttl: 55,
read: true,
write: true,
update: true,
get: true,
}, response => resolve(response));
});
}
async function getMetadata() {
const options = {include: {customFields: true}};
return await pubnub.objects.getAllChannelMetadata(options);
}
function setChannelsAndTokens(channelMetadata, channels, tokens) {
channelMetadata.data.forEach((value, index) => {
tokens.push(value.custom.lastToken);
channels.push(value.id.toString())
});
}
async function getUnreadedMessages(channels, tokens) {
return new Promise(async resolve => {
pubnub.messageCounts({
channels: null,
channelTimetokens: tokens,
}).then(res => {
clients.forEach((value, index) => {
if (res.channels[value.id + '-' + userId + '-chat']) {
value.unreadMessages = res["channels"][value.id + '-' + userId + '-chat'];
} else {
value.unreadMessages = 0;
}
console.log("UNREAD MESSAGE", value.unreadMessages, value.username);
});
resolve()
})
.catch(error => resolve(error));
});
}
async function getUnreadMessagesProccess() {
const tokens = ['1000'];
const channels = ['1234567'];
const userId = 1234567;
const auth = await setGrant(userId);
log([{statusCode: auth.statusCode}]);
const channelMetadata = await getMetadata();
log([channelMetadata,channels,tokens]);
setChannelsAndTokens(channelMetadata, channels, tokens);
const unread = await getUnreadedMessages(channels, tokens);
log([unread]);
return unread;
}
return (
<View>
<FlatList
keyExtractor={item => item.id.toString()}
data={clients}
onEndReached={() => hasMoreResults ? onEndReached() : null}
onEndReachedThreshold={0.4}
renderItem={item => (
<ClientItem
client={item.item}
isAdmin={isAdmin}
navigation={navigation}
fromAdminTab={fromAdminTab}
showIcon={parseInt(item.item.unreadMessages) > 0 }
/>
)}
ListFooterComponent={isLoading
? () => (
<View style={{ padding: 20 }}>
<ActivityIndicator animating size="large" color={Colors.border_gray} />
</View>
)
: null
}
/>
</View>
);
}
export default ClientList;
ClientItem:
import React, {useEffect, useState} from 'react';
import { Text, View, Image, TouchableOpacity } from 'react-native';
import Images from '#helper/Images';
import FontAwesome5 from 'react-native-vector-icons/FontAwesome5';
import Colors from '#helper/Colors';
import styles from '../styles';
import ClientModal from './ClientModal/ClientModal';
import { Client } from './ClientList';
import { HealthierLifeOption } from '#redux/types';
type Props = {
client: Client;
navigation: any;
isAdmin?: boolean;
fromAdminTab?: boolean;
showIcon?: boolean
}
const ClientItem = ({ client, navigation, isAdmin = false, fromAdminTab= false, showIcon= false }: Props) => {
const [showModal, setShowModal] = useState(false);
console.log(showIcon); // its not passed correclty
let clientIcon = Images.icon.logoIconOrange
const handleContinueButton = () => {
if(!fromAdminTab) {
navigation.navigate('ChatFromClientsList', { selectedId: client.id, showBackButton: true });
}else {
setShowModal(!showModal)
}
};
return (
<View style={styles.client_item_container}>
<TouchableOpacity style={styles.client_content_container} onPress={handleContinueButton}
>
<View style={styles.image_container}>
<Image
style={styles.client_profile_img}
source={clientIcon}
resizeMode="contain"
resizeMethod="resize"
/>
</View>
<View style={styles.title_container}>
<Text style={styles.title} >{ client.username } </Text>
</View>
<View style={styles.dot_container}>
{showIcon && <FontAwesome5
name="comment-dots"
size={20}
color={Colors.red}
/> }
</View>
<View style={styles.hamburger_container} >
<FontAwesome5
name="bars"
size={30}
color={Colors.black}
onPress={() => setShowModal(!showModal)}
/>
</View>
<View>
<ClientModal
isVisible={showModal}
onCollapse={(value: boolean) => setShowModal(value)}
closeModal={(value: boolean) => setShowModal(value)}
client={client}
navigation={navigation}
isAdmin={isAdmin}
/>
</View>
</TouchableOpacity>
</View>
);
};
export default ClientItem;
I am not sure if it is because of fethichng data from Pubnub or because of rendering or how i process data in foreach.
Run function after previous one is done
Using async and await in an empirical coding style. This helps organize the code. Making the code easy to read. This helps also for order of operations.
What we did was add a return in front of the await and promise in each function. That allows us to assign result variables after each function is finished executing while preserving order of execution as the code is written.
💡 Note: when the SDK is initialized with the secretKey, there is no need to grant yourself access to resources. Simply use any method as if you already had permissions. The SDK will automatically sign all requests using the your secret key.
Test this by pressing the Run code snippet button below the code snippet.
(async ()=>{
'use strict';
const publishKey = "pub-c-51f1008b-7ddf-42b7-aec9-2d0153b0e766";
const subscribeKey = "sub-c-1597b696-05af-11e6-a6dc-02ee2ddab7fe";
const secretKey = "sec-c-YTVjYjUzMWMtM2MxZC00YzdiLWE0ZjAtNWRmMWVmYmNkZGNl";
const myUUID = "myUniqueUUID"
const pubnub = new PubNub({
publishKey : publishKey,
subscribeKey : subscribeKey,
secretKey : secretKey,
uuid : myUUID,
});
const unread = await getUnreadMessagesProccess();
async function getUnreadMessagesProccess() {
const tokens = ['1000']; // placeholder change me
const channels = ['1234567']; // placeholder change me
const userId = 1234567; // placeholder change me
const auth = await setGrant(userId);
log([{statusCode: auth.statusCode}]);
const channelMetadata = await getMetadata();
log([channelMetadata,channels,tokens]);
setChannelsAndTokens(channelMetadata, channels, tokens);
const unread = await getUnreadedMessages(channels, tokens);
log([unread]);
return unread;
}
async function setGrant(userId) {
return new Promise( async resolve => {
await pubnub.grant({
channels: [userId.toString() + '.*'],
ttl: 55,
read: true,
write: true,
update: true,
get: true,
}, response => resolve(response));
});
}
async function getMetadata() {
const options = {include: {customFields: true}};
return await pubnub.objects.getAllChannelMetadata(options);
}
function setChannelsAndTokens(channelMetadata, channels, tokens) {
channelMetadata.data.forEach((value, index) => {
tokens.push(value.custom.lastToken);
channels.push(value.id)
});
}
async function getUnreadedMessages(channels, tokens) {
try {
return await pubnub.messageCounts({
channels: channels,
channelTimetokens: tokens,
});
}
catch(error) {
return error;
}
}
async function getUnreadedMessagesAlternative(channels, tokens) {
return new Promise(async resolve => {
pubnub.messageCounts({
channels: channels,
channelTimetokens: tokens,
}).then(result => resolve(result))
.catch(error => resolve(error));
});
}
function log(data) {
// console.log(data);
document.querySelector("#out").innerHTML +=
`<div>${JSON.stringify(data)}</div>`;
}
})();
<div id="out"></div>
<script src="https://cdn.pubnub.com/sdk/javascript/pubnub.4.33.0.js"></script>
Related
Im tryin to get my queries to update using the subscribeToMore function. Im getting lost on why the code is a failing to execute. so far all operations query, mutation and subscribe will work independently.
FRONTEND
export const QUERY_MESSAGES = gql`
query Messages($convoId: ID!) {
messages(convoId: $convoId) {
text
senderId
convoId
createdAt
}
}
`;
export const SUBSCRIBE_MESSAGES = gql`
subscription Messages($convoId: ID!) {
messages(convoID: $convoID) {
text
convoId
}
}
`;
const ActiveChat = ({ currentConvo, me }: any) => {
const { subscribeToMore, data } = useQuery(QUERY_MESSAGES, {
variables: {
convoId: currentConvo,
},
});
return (
<section className="chat-wrapper">
<div className="messages-container">
<Messages
messages={data.messages}
me={me}
subscribeToMessages={() => {
subscribeToMore({
document: SUBSCRIBE_MESSAGES,
variables: { convoId: currentConvo },
updateQuery: (prev, { subscriptionData }) => {
console.log("hit");
if (!subscriptionData.data) return prev;
const newFeedItem = subscriptionData.data.messages;
return Object.assign({}, prev, {
messages: [newFeedItem, ...prev.messages],
});
},
});
}}
/>
<Input currentConvo={currentConvo} />
</div>
</section>
);
};
const Messages = ({ messages, me, subscribeToMessages }: any) => {
useEffect(() => {
subscribeToMessages();
console.log("use effect ran");
});
const formatTime = (time: number) =>
new Date(time * 1000).toLocaleTimeString();
console.log(messages);
return (
messages && (
<div>
{messages.map((message: any, i: number) => {
return message.senderId === me?._id ? (
<SenderBubble
key={i}
text={message.text}
time={message.createdAt}
formatTime={formatTime}
/>
) : (
<OtherUserBubble
key={i}
text={message.text}
time={message.createdAt}
formatTime={formatTime}
/>
);
})}
</div>
)
);
};
BACKEND
const pubsub = new PubSub();
Query: {
messages: async (parent, { convoId }, context) => {
if (context.user) {
return Message.find({ convoId }).sort({ createdAt: "asc" });
}
},
},
Mutation: {
/// creates a new message using sender id and emits MESSAGE_SENT event
sendMessage: async (parent, { text, convoId }, context) => {
pubsub.publish("MESSAGE_SENT", {
message: { text, convoId },
});
return Message.create({ text, convoId, senderId: context.user._id });
},
},
Subscription: {
userActive: {
subscribe: () => pubsub.asyncIterator("ACTIVE_USERS"),
},
message: {
subscribe: withFilter(
() => pubsub.asyncIterator("MESSAGE_SENT"),
(payload, variables) => {
return payload.message.convoId === variables.convoId;
}
),
},
},
tdlr: I think subscribeToMore.updateQuery isn't firing but i don't how to debug
SOLVED: turns out I placed the wrong url and port number while building the client so the ws wasn't connecting.
I managed to build a simple music player using react-native-track-player following a tutorial as part of my learning curve in react native. Now, instead of just playing/streaming songs from provided url tracks in my array of songs, I want to stream the songs from API (though it can beb any API - but I have registered for Napster API) which I feel is not limited by just few songs. But, I can't put together how to implement or call the Napster API to fetch songs.
Please any help/guide I will appreciate so much.
Below is my code:
I have data.js, my array of songs:
const songs = [
{
title: "death bed",
artist: "Powfu",
artwork: require("../assets/album-arts/death-bed.jpg"),
url: "https://github.com/ShivamJoker/sample-songs/raw/master/death%20bed.mp3",
id: "1",
},
{
title: "bad liar",
artist: "Imagine Dragons",
artwork: require("../assets/album-arts/bad-liar.jpg"),
url: "https://github.com/ShivamJoker/sample-songs/raw/master/Bad%20Liar.mp3",
id: "2",
},
{
title: "faded",
artist: "Alan Walker",
artwork: require("../assets/album-arts/faded.jpg"),
url: "https://github.com/ShivamJoker/sample-songs/raw/master/Faded.mp3",
id: "3",
},
];
export default songs;
And here's my playerScreen.js :
import React, {useRef, useEffect, useState} from 'react';
import {
View,
SafeAreaView,
Text,
Image,
FlatList,
Dimensions,
Animated,
StyleSheet,
} from 'react-native';
import TrackPlayer, {
Capability,
useTrackPlayerEvents,
usePlaybackState,
TrackPlayerEvents,
STATE_PLAYING,
Event,
} from 'react-native-track-player';
import songs from './data';
import Controller from './Controller';
import SliderComp from './SliderComp';
const {width, height} = Dimensions.get('window');
// const events = [
// TrackPlayerEvents.PLAYBACK_STATE,
// TrackPlayerEvents.PLAYBACK_ERROR
// ];
export default function PlayerScreen() {
const scrollX = useRef(new Animated.Value(0)).current;
const slider = useRef(null);
const isPlayerReady = useRef(false);
const index = useRef(0);
const [songIndex, setSongIndex] = useState(0);
const isItFromUser = useRef(true);
// for tranlating the album art
const position = useRef(Animated.divide(scrollX, width)).current;
const playbackState = usePlaybackState();
useEffect(() => {
// position.addListener(({ value }) => {
// console.log(value);
// });
scrollX.addListener(({value}) => {
const val = Math.round(value / width);
setSongIndex(val);
});
TrackPlayer.setupPlayer().then(async () => {
// The player is ready to be used
console.log('Player ready');
// add the array of songs in the playlist
await TrackPlayer.reset();
await TrackPlayer.add(songs);
TrackPlayer.play();
isPlayerReady.current = true;
await TrackPlayer.updateOptions({
stopWithApp: false,
alwaysPauseOnInterruption: true,
capabilities: [
Capability.Play,
Capability.Pause,
Capability.SkipToNext,
Capability.SkipToPrevious,
],
});
//add listener on track change
TrackPlayer.addEventListener(Event.PlaybackTrackChanged, async (e) => {
console.log('song ended', e);
const trackId = (await TrackPlayer.getCurrentTrack()) - 1; //get the current id
console.log('track id', trackId, 'index', index.current);
if (trackId !== index.current) {
setSongIndex(trackId);
isItFromUser.current = false;
if (trackId > index.current) {
goNext();
} else {
goPrv();
}
setTimeout(() => {
isItFromUser.current = true;
}, 200);
}
// isPlayerReady.current = true;
});
//monitor intterupt when other apps start playing music
TrackPlayer.addEventListener(Event.RemoteDuck, (e) => {
// console.log(e);
if (e.paused) {
// if pause true we need to pause the music
TrackPlayer.pause();
} else {
TrackPlayer.play();
}
});
});
return () => {
scrollX.removeAllListeners();
TrackPlayer.destroy();
// exitPlayer();
};
}, []);
// change the song when index changes
useEffect(() => {
if (isPlayerReady.current && isItFromUser.current) {
TrackPlayer.skip(songs[songIndex].id)
.then((_) => {
console.log('changed track');
})
.catch((e) => console.log('error in changing track ', e));
}
index.current = songIndex;
}, [songIndex]);
const exitPlayer = async () => {
try {
await TrackPlayer.stop();
} catch (error) {
console.error('exitPlayer', error);
}
};
const goNext = async () => {
slider.current.scrollToOffset({
offset: (index.current + 1) * width,
});
await TrackPlayer.play();
};
const goPrv = async () => {
slider.current.scrollToOffset({
offset: (index.current - 1) * width,
});
await TrackPlayer.play();
};
const renderItem = ({index, item}) => {
return (
<Animated.View
style={{
alignItems: 'center',
width: width,
transform: [
{
translateX: Animated.multiply(
Animated.add(position, -index),
-100,
),
},
],
}}>
<Animated.Image
source={item.artwork}
style={{width: 320, height: 320, borderRadius: 5}}
/>
</Animated.View>
);
};
return (
<SafeAreaView style={styles.container}>
<SafeAreaView style={{height: 320}}>
<Animated.FlatList
ref={slider}
horizontal
pagingEnabled
showsHorizontalScrollIndicator={false}
scrollEventThrottle={16}
data={songs}
renderItem={renderItem}
keyExtractor={(item) => item.id}
onScroll={Animated.event(
[{nativeEvent: {contentOffset: {x: scrollX}}}],
{useNativeDriver: true},
)}
/>
</SafeAreaView>
<View>
<Text style={styles.title}>{songs[songIndex].title}</Text>
<Text style={styles.artist}>{songs[songIndex].artist}</Text>
</View>
<SliderComp />
<Controller onNext={goNext} onPrv={goPrv} />
</SafeAreaView>
);
}
I would pass the songs into your current PlayerScreen through props. You can use a separate component to load tracks from the Napster API and then render a PlayerScreen with those props.
The only part I'm not sure about is what path to pass to the player as the url. The Napster data contains a property previewURL which is an mp3 but it's not the whole song. I believe that the href is the streamable URL. It requires authentication to load the full track though.
The API path that I'm using here is for the most popular tracks.
export default function TopTracks() {
const [songs, setSongs] = useState([]);
useEffect(() => {
const loadData = async () => {
try {
const url = `http://api.napster.com/v2.2/tracks/top?apikey=${API_KEY}&limit=5`;
const res = await axios.get(url);
setSongs(
res.data.tracks.map((track) => ({
duration: track.playbackSeconds,
title: track.name,
artist: track.artistName,
album: track.albumName,
id: track.id,
url: track.href // or track.previewURL?
}))
);
} catch (error) {
console.error(error);
}
};
loadData();
}, []);
return <PlayerScreen songs={songs} />;
}
I have a post route:
router.post("/projects", async (req, res) => {
const {
projectName,
projectDescription,
projectBudget,
projectDuration,
industry,
companyName,
numberOfEmployees,
diamond,
} = req.body;
console.log(diamond);
const [projectDiamond] = diamond;
const { criteria } = projectDiamond;
//diamond is an array containing an object, and that object contains another object called criteria, hence destructuring the 'criteria' object. It's redundant I know but this thing is out of scope of this question!
if (
!projectName ||
!projectDescription ||
!projectBudget ||
!projectDuration ||
!industry ||
!companyName ||
!numberOfEmployees ||
!diamond
) {
return res.status(422).send({ error: "Must provide all project details" });
}
try {
const project = new Project({
projectName,
projectDescription,
projectBudget,
projectDuration,
industry,
companyName,
numberOfEmployees,
diamond,
userId: req.user._id,
});
const recommendation = await Recommendation.find({
"diamond.criteria": criteria,
}); //Need to render this on screen
const projectsWithSameDiamond = await Project.find({
"diamond.criteria": criteria,
}); //Need to render this on screen
const projectsWithSameIndustry = await Project.find({ industry }); //Need to render this on screen
await project.save();
} catch (err) {
res.status(422).send({ error: err.message });
}
});
It's a post request as you can see. Now every time a user let's say "post a new project", I want to retrieve "recommendation" and "projects with similar diamond" and "projects with similar industry" (you can see that I am trying to save all three of them in different variables in the post route).
Is there a way to retrieve these three variables and use them in a component in react native?
Suppose I have a component, A.js:
const A = () => {
return(
//returning something here
)
)
}
Now suppose this component uses axios to send http post request to the route I have defined:
const A = () => {
...
axios.post("/projects", {projectName,
projectDescription,
projectBudget,
projectDuration,
industry,
companyName,
numberOfEmployees,
diamond} );
return(
//returning something here
)
)
}
After the request has finished successfully ( and a project is posted) I want to let's say render those three variables on screen
const A = () => {
return(
<Text>{recommendation}</Text> {/*Not sure how to do get this after using axios to post new project */}
<Text>{projectWithSimilarDiamond</Text> {/*Not sure how to do get this after using axios to post new project */}
<Text>{projectWithSimilarIndustry}</Text> {/*Not sure how to do get this after using axios to post new project */}
)
)
}
Here is an example using state
snack: https://snack.expo.io/#ashwith00/get-post
code:
import * as React from 'react';
import { Text, View, StyleSheet, ActivityIndicator } from 'react-native';
import Constants from 'expo-constants';
import Axios from 'axios';
// You can import from local files
import AssetExample from './components/AssetExample';
// or any pure javascript modules available in npm
import { Card } from 'react-native-paper';
export default function App() {
const [state, setState] = React.useState({
loading: false,
data: [],
error: false,
});
const getPosts = async () => {
setState({
...state,
loading: true,
data: [],
error: false,
});
// try {
// const response = await Axios.post('/users/post', {
// //datas
// });
// setState({
// ...state,
// loading: false,
// data: response.data,
// });
// } catch (err) {
// setState({
// error: true,
// });
// }
};
React.useEffect(() => {
getPosts()
}, [])
return (
<View style={styles.container}>
{state.loading ? (
<ActivityIndicator />
) : (
state.data.map(({ title }, key) => {
<Text {...{ key }}>{title}</Text>;
})
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
});
I have updated my store by using mapDispatchToProps like so:
function mapDispatchToProps(dispatch){
return{
addFirstImageUrl: (firstUrl) => dispatch({
type: "ADD_FIRST_IMAGE_URL",
firstUrl
})
}
}
I know that this works because when I run mapStateToProps I log the state like so:
function mapStateToProps(state){
console.log(state)
return{
firstImageUrl: state.firstImageUrl
}
}
This returns:
}
Object {
"posts": Object {
"firstImageTitle": "",
"firstImageUrl": "https://firebasestorage.MYURL",
"secondImageTitle": "",
"secondImageUrl": "",
},
}
however when I call this.props.firstImageUrl it returns undefined. I feel like this should return the url above, is this thinking wrong?
component function:
uploadImage = async (uri) => {
const response = await fetch(uri);
const blob = await response.blob();
var ref = firebase
.storage()
.ref()
.child(new Date().toString());
const snapshot = await ref.put(blob)
const url = await snapshot.ref.getDownloadURL();
this.props.addFirstImageUrl(url);
console.log("First Image Url: " + this.props.firstImageUrl)
};
import React from "react";
import {
StyleSheet,
Text,
View,
TouchableOpacity
} from "react-native";
import { Camera } from "expo-camera";
import * as Permissions from "expo-permissions";
import { FontAwesome } from "#expo/vector-icons";
import * as firebase from "firebase";
import { connect } from "react-redux";
import { addFirstImageUrl } from "../store/actions/posts";
import { bindActionCreators } from "redux";
function mapStateToProps(state){
console.log(state)
return{
firstImageUrl: state.firstImageUrl
}
}
function mapDispatchToProps(dispatch){
return{
addFirstImageUrl: (firstUrl) => dispatch({
type: "ADD_FIRST_IMAGE_URL",
firstUrl
})
}
}
class FirstCameraScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
hasCameraPermissions: null,
type: Camera.Constants.Type.back,
isFlashLIghtOn: Camera.Constants.FlashMode.off,
firstImageUrl: " "
};
}
static navigationOptions = {
title: "FirstCamera"
};
//ask for camera permission
async componentDidMount() {
const { status } = await Permissions.askAsync(Permissions.CAMERA);
this.setState({
hasCameraPermissions: status === "granted"
});
}
// Flip the camera
flipCamera = () => {
this.setState({
type:
this.state.type === Camera.Constants.Type.back
? Camera.Constants.Type.front
: Camera.Constants.Type.back
});
};
//Toggle Flashlight
flashLight = () => {
this.setState({
isFlashLIghtOn:
this.state.isFlashLIghtOn === Camera.Constants.FlashMode.off
? Camera.Constants.FlashMode.on
: Camera.Constants.FlashMode.off
});
};
//Take Picture and send to home
takePicture = async () => {
if (this.camera) {
let photo = await this.camera.takePictureAsync();
if (!photo.cancelled) {
await this.uploadImage(photo.uri);
}
this.props.navigation.navigate("FirstNamingScreen");
}
};
uploadImage = async (uri) => {
const response = await fetch(uri);
const blob = await response.blob();
var ref = firebase
.storage()
.ref()
.child(new Date().toString());
const snapshot = await ref.put(blob)
const url = await snapshot.ref.getDownloadURL();
this.props.addFirstImageUrl(url);
console.log("First Image Url: " + props.posts)
};
render() {
const { hasCameraPermissions } = this.state;
const { navigate } = this.props.navigation;
if (hasCameraPermissions === null) {
return <View />;
} else if (hasCameraPermissions === false) {
return (
<View>
<Text>No access to Camera</Text>
</View>
);
} else if (hasCameraPermissions === true) {
return (
<View style={styles.container}>
<Camera
style={styles.cameraView}
type={this.state.type}
flashMode={this.state.isFlashLIghtOn}
ref={ref => {
this.camera = ref;
}}
>
<View style={styles.actionContainer}>
<TouchableOpacity
onPress={() => this.flipCamera()}
style={styles.iconHolder}
>
<FontAwesome name="camera" size={35} style={styles.icon} />
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
this.takePicture();
}}
style={styles.iconHolder}
>
<FontAwesome name="circle" size={35} style={styles.icon} />
</TouchableOpacity>
<TouchableOpacity
onPress={() => this.flashLight()}
style={styles.iconHolder}
>
<FontAwesome name="flash" size={35} style={styles.icon} />
</TouchableOpacity>
</View>
</Camera>
</View>
);
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(FirstCameraScreen)
Change the mapStateToProps to this.
function mapStateToProps(state) {
console.log(state);
return {
firstImageUrl: state.posts.firstImageUrl
};
}
And your uploadImage method.
uploadImage() {
const response = await fetch(uri);
const blob = await response.blob();
var ref = firebase
.storage()
.ref()
.child(new Date().toString());
const snapshot = await ref.put(blob);
const url = await snapshot.ref.getDownloadURL();
this.props.addFirstImageUrl(url);
console.log("First Image Url: " + this.props.firstImageUrl);
};
constructor
constructor(props) {
super(props);
this.uploadImage = this.uploadImage.bind(this);
this.state = {
hasCameraPermissions: null,
type: Camera.Constants.Type.back,
isFlashLIghtOn: Camera.Constants.FlashMode.off,
firstImageUrl: " "
};
}
if i write this onPress={() => this.submit()} instead onPress={() => this.onSendBtnPressed()} i can get the text value but if i just write onPress={() => this.onSendBtnPressed()} this i can't see the text on the message list
here is my chatui
import React, { Component } from 'react';
import { connect } from 'react-redux';
import {KeyboardAvoidingView, Image, TouchableOpacity, ReactNative,
StyleSheet } from 'react-native';
import {Footer, FooterTab} from "native-base";
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-
view';
import { View, Title, Screen, ScrollView, TextInput,Text } from
'#shoutem/ui';
import Messages from '../containers/Messages';
import Input from '../containers/Input';
import { sendMessage } from '../actions';
const mapStateToProps = (state) => ({
chatHeight: state.chatroom.meta.height,
user: state.user,
});
class ChatUI extends Component {
constructor()
{
super();
this.state = {
text: '',
scrollViewHeight: 0,
inputHeight: 0
}
}
updateValue(text) {
console.warn(text)
}
componentDidMount() {
this.scrollToBottom(false);
}
componentDidUpdate() {
this.scrollToBottom();
}
onChangeText(text) {
this.setState({text: text})
};
onSendBtnPressed (text) {
return sendMessage(text, this.props.user)
this.textInput.clear();
Keyboard.dismiss();
}
onScrollViewLayout = (event) => {
const layout = event.nativeEvent.layout;
this.setState({
scrollViewHeight: layout.height
});
}
onInputLayout = (event) => {
const layout = event.nativeEvent.layout;
this.setState({
inputHeight: layout.height
});
}
scrollToBottom(animate = false) {
const { scrollViewHeight, inputHeight } = this.state,
{ chatHeight } = this.props;
const scrollTo = chatHeight - scrollViewHeight + inputHeight;
if (scrollTo > 0) {
this.refs.scroll.scrollToEnd();
}
}
_scrollToInput(reactRef) {
this.refs.scroll.scrollToFocusedInput(ReactNative.findNodeHandle(reactRef));
}
submit() {
let collection = {}
collection.text = this.state.text,
console.warn(collection);
}
render() {
return (
<Screen >
<Title style={{paddingTop: 25, paddingLeft: 10, borderBottomWidth: 0.5, backgroundColor: 'white', paddingBottom: 10}}>
<Image style={{width:80, height: 80}} source={require('../img/11.png')} />
<Text> GENERAL CHAT</Text>
</Title>
<KeyboardAwareScrollView ref="scroll"
onLayout={this.onScrollViewLayout}>
<Messages />
</KeyboardAwareScrollView>
<Footer style={{width:360,
height:30,
backgroundColor:'#fff',
borderRadius:20,
borderWidth: 0.5,
marginBottom: 3,
borderColor: 'gray',
}}>
<TextInput
onSubmitEditing = {this.onSubmitEditing}
multiline
onLayout={this.onInputLayout}
submitAction={this.onSendBtnPressed}
ref="input"
placeholder="Say something ..."
onChangeText={(text) => this.onChangeText(text, 'text')}
style={{backgroundColor: 'rgba(0,0,0,0)',
borderBottomWidth: 1.5, borderColor: '#f8f8f8', fontSize: 16, color: 'gray', paddingBottom:20}}
ref={input => { this.textInput = input; } }/>
</Footer>
<TouchableOpacity
onPress={() => this.onSendBtnPressed()}
style={{marginLeft:280, backgroundColor:'gray', width: 70, height: 30}}
title= "send"
>
</TouchableOpacity>
</Screen>
)
}
}
export default connect(mapStateToProps, {sendMessage})(ChatUI);
and here is my actions
import firebase from '../firebase';
import DeviceInfo from 'react-native-device-info';
import {Actions} from 'react-native-router-flux';
import FCM, { FCMEvent, NotificationType, WillPresentNotificationResult,
RemoteNotificationResult } from 'react-native-fcm';
import { Platform } from 'react-native';
export const addMessage = (msg) => ({
type: 'ADD_MESSAGE',
...msg
});
export const sendMessage = (text, user) => {
return function (dispatch) {
let msg = {
text: text,
time: Date.now(),
author: {
name: user.name,
avatar: user.avatar
}
};
const newMsgRef = firebase.database()
.ref('messages')
.push();
msg.id = newMsgRef.key;
newMsgRef.set(msg);
dispatch(addMessage(msg));
};
};
export const startFetchingMessages = () => ({
type: 'START_FETCHING_MESSAGES'
});
export const receivedMessages = () => ({
type: 'RECEIVED_MESSAGES',
receivedAt: Date.now()
});
export const fetchMessages = () => {
return function (dispatch) {
dispatch(startFetchingMessages());
firebase.database()
.ref('messages')
.orderByKey()
.limitToLast(20)
.on('value', (snapshot) => {
setTimeout(() => {
const messages = snapshot.val() || [];
dispatch(receiveMessages(messages))
}, 0);
});
}
}
export const receiveMessages = (messages) => {
return function (dispatch) {
Object.values(messages).forEach(msg => dispatch(addMessage(msg)));
dispatch(receivedMessages());
}
}
export const updateMessagesHeight = (event) => {
const layout = event.nativeEvent.layout;
return {
type: 'UPDATE_MESSAGES_HEIGHT',
height: layout.height
}
}
export const setUserName = (name) => {
return (dispatch) => {
dispatch({
type: 'SET_USER_NAME',
payload: name
});
};
};
export const setUserAvatar = (avatar) => ({
type: 'SET_USER_AVATAR',
avatar: avatar && avatar.length > 0 ? avatar : 'avatar.jpg'
});
export const login = () => {
return function (dispatch, getState) {
dispatch(startAuthorizing());
firebase.auth()
.signInAnonymously()
.then(() => {
const { name, avatar } = getState().user;
firebase.database()
.ref(`users/`)
.push({
name,
avatar
});
dispatch(userAuthorized());
dispatch(fetchMessages());
});
}
}
export const checkUserExists = () => {
return function (dispatch) {
dispatch(startAuthorizing());
firebase.auth()
.signInAnonymously()
.then(() => firebase.database()
.ref(`users/${DeviceInfo.getUniqueID()}`)
.once('value', (snapshot) => {
const val = snapshot.val();
if (val === null) {
dispatch(userNoExist());
}else{
dispatch(setUserName(val.name));
dispatch(setUserAvatar(val.avatar));
startChatting(dispatch);
}
}))
.catch(err => console.log(err))
}
}
const startChatting = function (dispatch) {
dispatch(userAuthorized());
dispatch(fetchMessages());
FCM.requestPermissions();
FCM.getFCMToken()
.then(token => {
console.log(token)
});
FCM.subscribeToTopic('secret-chatroom');
FCM.on(FCMEvent.Notification, async (notif) => {
console.log(notif);
if (Platform.OS === 'ios') {
switch (notif._notificationType) {
case NotificationType.Remote:
notif.finish(RemoteNotificationResult.NewData); //other
types available: RemoteNotificationResult.NewData,
RemoteNotificationResult.ResultFailed
break;
case NotificationType.NotificationResponse:
notif.finish();
break;
case NotificationType.WillPresent:
notif.finish(WillPresentNotificationResult.All); //other
types available: WillPresentNotificationResult.None
break;
}
}
});
FCM.on(FCMEvent.RefreshToken, token => {
console.log(token);
});
}
export const startAuthorizing = () => ({
type: 'USER_START_AUTHORIZING'
});
export const userAuthorized = () => ({
type: 'USER_AUTHORIZED'
});
export const userNoExist = () => ({
type: 'USER_NO_EXIST'
});
and MessageList
import React, { Component } from 'react';
import {
ListView, Text, Row, Image,
View, Subtitle, Caption, Heading
} from '#shoutem/ui';
import moment from 'moment';
const Message = ({ msg }) => (
<Row>
<Image styleName="small-avatar top"
source={{ uri: msg.author.avatar }} />
<View styleName="vertical">
<View styleName="horizontal space-between">
<Subtitle>{msg.author.name}</Subtitle>
<Caption>{moment(msg.time).from(Date.now())}</Caption>
</View>
<Text styleName="multiline">{msg.text}</Text>
</View>
</Row>
);
const MessageList = ({ messages, onLayout }) => (
<ListView data={messages}
autoHideHeader={true}
renderRow={msg => <Message msg={msg} />}
onLayout={onLayout}
/>
);
export default MessageList;
You are not passing a text variable into your onSendButtonPressed function. It looks like you should be calling it using the following syntax:
onPress={() => this.onSendBtnPressed(someText)}
Your onSendBtnPressed() function has the return statement on the first line, so the code will not be fired underneath the return statement (not related to your question, but hope to fix any other headaches...)