I am using Firebase Dynamic Links. How I am looking for it to work is, a user can share a recipe from my app and text it to a friend. When the friend opens the link it opens the app straight to the recipe screen for that specific recipe ID. How do I pass the recipe ID so I can navigate to that screen? Do I need a long link?
Here is what I have so far:
Share Screen
const buildLink = async recipeId => {
let link = await axios({
method: 'POST',
url: `https://firebasedynamiclinks.googleapis.com/v1/shortLinks?key=${FB_DEEP_LINKS}`,
headers: {
'Content-Type': 'application/json',
},
data: {
dynamicLinkInfo: {
domainUriPrefix: `https://example.page.link`,
link: 'https://www.google.com',
androidInfo: {
androidPackageName: 'com.example.app',
},
iosInfo: {
iosBundleId: 'com.example.app',
},
},
},
});
if (link.status === 200) {
return link.data.shortLink;
}
};
const shareLink = async recipeId => {
let shareURL;
try {
shareURL = await buildLink(recipeId);
console.log(shareURL);
} catch (error) {
console.log(error);
}
try {
if (shareURL !== '') {
await Share.share({
message: `Check out this recipe, ${shareURL}`,
});
}
} catch (error) {
console.log(error);
}
};
//Loads cart icon and cart data
React.useLayoutEffect(() => {
let recipeID = props.route.params.recipeID;
props.navigation.setOptions({
headerRight: () => {
return (
<>
<Feather
style={{paddingRight: 20}}
name={'share'}
size={24}
color={'white'}
onPress={() => {
shareLink(recipeID);
}}
/>
</>
);
},
});
}, [props.navigation]);
Home Screen
const handleDynamicLink = useCallback(
async link => {
console.log('this is the link: ', link);
if (link.url) {
console.log(link);
} else {
console.log('not working');
}
},
[props.navigation],
);
useEffect(() => {
const unSubscribe = dynamicLinks().onLink(handleDynamicLink);
return () => unSubscribe();
}, [handleDynamicLink]);
Related
I'm struggling to understand what could be causing a slow response from the server, upwards of 2 seconds for a simple patch. I've gone ahead and posted an example to help better illustrate the problem. Right now if you swipe right on a card, it will kick off a saveLike and saveVote. Both will fetch data from Card.js about what card you swiped right on and your userID to save to. I'm running next.js with sanity.io, using yarn dev and yarn start respectively. Even insight on how to debug what could be causing a long wait period for a query would be super helpful long term
#context
const handleRightSwipe = async (cardData, votesData) => {
try {
await fetch('/api/saveLike', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
likedCards: cardData,
currentUser: "abc123",
}),
})
// deduct one incrementally
let votesAmount = votesData--
try {
await fetch('/api/saveVote', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
_id: "abc123",
votes: votesAmount
}),
})
} catch (error) {
console.error(error)
}
// set vote tally the latest amount
setVotesData(votesAmount)
// request vote tally be updated on the client side
requestVoteAmount(userID)
} catch (error) {
console.error(error)
}
}
# saveVote
import { client } from '../../lib/sanity'
const saveVote = async (req, res) => {
try {
await client
.patch(req.body._id).set({votes:req.body.votes}).commit()
res.status(200).send({ message: 'success' })
} catch (error) {
res.status(500).send({ message: 'error', data: error.message })
}
}
export default saveVote
#saveLike
import { client } from '../../lib/sanity'
const saveLike = async (req, res) => {
try {
await client
.patch(req.body.likedCards._id)
.setIfMissing({'likes': []})
.insert('after', 'likes[-1]',[
{
_ref: req.body.currentUser,
_type: "reference",
},
])
.commit({
autoGenerateArrayKeys: true,
})
res.status(200).send({ message: 'success' })
} catch (error) {
res.status(500).send({ message: 'error', data: error.message })
}
}
#Card
import React, { useState, useMemo, useContext, useRef, useEffect } from 'react'
import { Context} from '../context/Context'
import TCard from 'react-tinder-card'
const Card = () => {
const { cardsData, votesData } = useContext(Context)
const { handleRightSwipe, handleLeftSwipe, currentAccountAddress } = useContext(Context)
const [currentIndex, setCurrentIndex] = useState(cardsData.length - 1)
const currentIndexRef = useRef(currentIndex)
const childRefs = useMemo(
() =>
Array(cardsData.length)
.fill(0)
.map((i) => React.createRef()),
[cardsData.length]
)
const updateCurrentIndex = (val) => {
setCurrentIndex(val)
currentIndexRef.current = val
}
const canVote = votesData > 0
const swiped = (direction, card, index) => {
if (!canVote) return
if (votesData > 0) {
if (direction === 'right') {
const voterAmount = votesData--
updateCurrentIndex(index - 1)
handleRightSwipe(card, currentAccountAddress, voterAmount)
}
if (direction === 'left') {
const voterAmount = votesData--
updateCurrentIndex(index - 1)
handleLeftSwipe(card, currentAccountAddress, voterAmount)
}
}
else {
}
}
const outOfFrame = (idx) => {
currentIndexRef.current >= idx && childRefs[idx].current.restoreCard()
}
return (
<div>
{
<div>
{cardsData.map((card, index) => (
<TCard
card={card}
key={card.name}
ref={childRefs[index]}
preventSwipe={['up', 'down']}
onCardLeftScreen={() => outOfFrame(card.name, index)}
onSwipe={(dir) => swiped(dir, card, index)}>
<div
style={{ backgroundImage: `url('${card.imageUrl}')`
>
</div>
</TCard> ))}
</div>
}
</div>
)
}
export default Card
I have the below code, I want to call a function and render a child component onCLick. What is the best way to achieve this?
import AddOrder from './AddOrder'
return (
<Button onClick={handleCheckout}>Checkout</Button>
)
const handleCheckout = () => {
<AddOrder />
fetch("http://localhost:5000/create-checkout-session", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
items: data?.getUser ? data.getUser.cart : cart,
email: currentUser ? currentUser.email : undefined,
}),
})
.then(async (res) => {
if (res.ok) return res.json();
const json = await res.json();
return await Promise.reject(json);
})
.then(({ url }) => {
window.location = url;
})
.catch((e) => {
console.error(e.error);
});
};
I tried making a new function called handleAll and adding it like this:
function handleAll(){
handleCheckout()
<AddOrder />
}
AddOrder.js:
function AddOrder() {
const d = new Date();
let text = d.toString();
const { currentUser } = useContext(AuthContext);
const { data, loading, error } = useQuery(queries.GET_USER_BY_ID, {
fetchPolicy: "cache-and-network",
variables: {
id: currentUser.uid
},
});
const [addOrder] = useMutation(queries.ADD_ORDER);
useEffect(() => {
console.log('hi')
})
if(error) {
return <h1> error</h1>;
}
if(loading) {
return <h1> loading</h1>;
}
if (data){
let newCart = []
for(let i=0; i< data.getUser.cart.length; i++){
newCart.push({quantity: data.getUser.cart[i].quantity, _id: data.getUser.cart[i]._id})
}
console.log(newCart)
addOrder({
variables: {
userId: currentUser.uid, status: 'ordered', createdAt: text, products: newCart
}
});
console.log("hello")
}
}
export default AddOrder;
This did not work either. When I reload this it add 3 copies of the same order to the mongodb collection. What is the right way to do this?
So far i'm stuck on my useEffect that fetches all the current messages and renders the state accordingly. as of right now it doesn't render the new state until page is refreshed.
const Home = ({ user, logout }) => {
const history = useHistory();
const socket = useContext(SocketContext);
const [conversations, setConversations] = useState([]);
const [activeConversation, setActiveConversation] = useState(null);
const classes = useStyles();
const [isLoggedIn, setIsLoggedIn] = useState(false);
const addSearchedUsers = (users) => {
const currentUsers = {};
// make table of current users so we can lookup faster
conversations.forEach((convo) => {
currentUsers[convo.otherUser.id] = true;
});
const newState = [...conversations];
users.forEach((user) => {
// only create a fake convo if we don't already have a convo with this user
if (!currentUsers[user.id]) {
let fakeConvo = { otherUser: user, messages: [] };
newState.push(fakeConvo);
}
});
setConversations(newState);
};
const clearSearchedUsers = () => {
setConversations((prev) => prev.filter((convo) => convo.id));
};
const saveMessage = async (body) => {
const { data } = await axios.post("/api/messages", body);
return data;
};
const sendMessage = (data, body) => {
socket.emit("new-message", {
message: data.message,
recipientId: body.recipientId,
sender: data.sender,
});
};
const postMessage = async (body) => {
try {
const data = await saveMessage(body);
if (!body.conversationId) {
addNewConvo(body.recipientId, data.message);
} else {
addMessageToConversation(data);
}
sendMessage(data, body);
} catch (error) {
console.error(error);
}
};
const addNewConvo = useCallback(
(recipientId, message) => {
conversations.forEach((convo) => {
if (convo.otherUser.id === recipientId) {
convo.messages.push(message);
convo.latestMessageText = message.text;
convo.id = message.conversationId;
}
});
setConversations(conversations);
},
[setConversations, conversations],
);
const addMessageToConversation = useCallback(
(data) => {
// if sender isn't null, that means the message needs to be put in a brand new convo
const { message, sender = null } = data;
if (sender !== null) {
const newConvo = {
id: message.conversationId,
otherUser: sender,
messages: [message],
};
newConvo.latestMessageText = message.text;
setConversations((prev) => [newConvo, ...prev]);
}
conversations.forEach((convo) => {
console.log('hi', message.conversationId)
if (convo.id === message.conversationId) {
const convoCopy = { ...convo }
convoCopy.messages.push(message);
convoCopy.latestMessageText = message.text;
console.log('convo', convoCopy)
} else {
return convo
}
});
setConversations(conversations);
},
[setConversations, conversations],
);
const setActiveChat = useCallback((username) => {
setActiveConversation(username);
}, []);
const addOnlineUser = useCallback((id) => {
setConversations((prev) =>
prev.map((convo) => {
if (convo.otherUser.id === id) {
const convoCopy = { ...convo };
convoCopy.otherUser = { ...convoCopy.otherUser, online: true };
return convoCopy;
} else {
return convo;
}
}),
);
}, []);
const removeOfflineUser = useCallback((id) => {
setConversations((prev) =>
prev.map((convo) => {
if (convo.otherUser.id === id) {
const convoCopy = { ...convo };
convoCopy.otherUser = { ...convoCopy.otherUser, online: false };
return convoCopy;
} else {
return convo;
}
}),
);
}, []);
// Lifecycle
useEffect(() => {
// Socket init
socket.on("add-online-user", addOnlineUser);
socket.on("remove-offline-user", removeOfflineUser);
socket.on("new-message", addMessageToConversation);
return () => {
// before the component is destroyed
// unbind all event handlers used in this component
socket.off("add-online-user", addOnlineUser);
socket.off("remove-offline-user", removeOfflineUser);
socket.off("new-message", addMessageToConversation);
};
}, [addMessageToConversation, addOnlineUser, removeOfflineUser, socket]);
useEffect(() => {
// when fetching, prevent redirect
if (user?.isFetching) return;
if (user && user.id) {
setIsLoggedIn(true);
} else {
// If we were previously logged in, redirect to login instead of register
if (isLoggedIn) history.push("/login");
else history.push("/register");
}
}, [user, history, isLoggedIn]);
useEffect(() => {
const fetchConversations = async () => {
try {
const { data } = await axios.get("/api/conversations");
setConversations(data);
} catch (error) {
console.error(error);
}
};
if (!user.isFetching) {
fetchConversations();
}
}, [user]);
const handleLogout = async () => {
if (user && user.id) {
await logout(user.id);
}
};
return (
<>
<Button onClick={handleLogout}>Logout</Button>
<Grid container component="main" className={classes.root}>
<CssBaseline />
<SidebarContainer
conversations={conversations}
user={user}
clearSearchedUsers={clearSearchedUsers}
addSearchedUsers={addSearchedUsers}
setActiveChat={setActiveChat}
/>
<ActiveChat
activeConversation={activeConversation}
conversations={conversations}
user={user}
postMessage={postMessage}
/>
</Grid>
</>
);
};
this is the main part im working on, the project had starter code when i began and was told not to touch the backend so i know its something wrong with the front end code. i feel like im missing something important for the socket.io
import { io } from 'socket.io-client';
import React from 'react';
export const socket = io(window.location.origin);
socket.on('connect', () => {
console.log('connected to server');
});
export const SocketContext = React.createContext();
this is how i have the socket.io setup, if anyone could point me in the right direction that would be cool. I have been reading up on socket.io as much as I can but am still pretty lost on it.
Based on the assumption the backend is working properly...
const addNewConvo = useCallback(
(recipientId, message) => {
conversations.forEach((convo) => {
if (convo.otherUser.id === recipientId) {
convo.messages.push(message);
convo.latestMessageText = message.text;
convo.id = message.conversationId;
}
});
setConversations(conversations);
},
[setConversations, conversations],
);
setConversations(conversations);
This is an incorrect way to set a state using the state's variable, and such it wont do anything. Likely why your code wont change until refresh.
Suggested fix:
const addNewConvo = useCallback(
(recipientId, message) => {
setConversations(previousState => previousState.map(convo => {
if (convo.otherUser.id === recipientId) {
convo.messages.push(message)
convo.latestMessageText = message.text;
convo.id = message.conversationId;
return convo
}
return convo
}))
},
[setConversations, conversations],
);
note: even above could be done more efficiently since I made a deep copy of messages
I'm trying to make a call from client side(browser) to FreeSWITCH server using this libs:
Sip.js
Sip.js framework
And also I use React.
When I make a call I successfully invite destination URI.
After click button and call function callSip our SessionState changes to Ringing, then successful invitation, then SessionState is "Answered" and after that the function onAccept in requestDelegate object is run, but no connection is established because the SessionState goes to "Ended"
After all I have this log.
How to establish a voice connection so that it does not break off after receiving a call?
How to add a remote sound from sip to an audio ref element?
const CallList: React.FC<Props> = (props) => {
const [userURI, setUserURI] = useState<URI | null>(null);
const audioRef = useRef(null);
useEffect(() => {
const uri = UserAgent.makeURI("sip:9012#serverpoint");
if (!uri) {
throw new Error("Failed to create URI");
}
setUserURI(uri);
return () => {
console.log("unmount");
};
}, []);
if (!userURI) {
return <div></div>;
}
const userAgentOptions: UserAgentOptions = {
uri: userURI,
authorizationPassword: "pasw",
authorizationUsername: "9012",
transportOptions: {
server: "ws://serverpoint",
},
};
const userAgent = new UserAgent(userAgentOptions);
const target = UserAgent.makeURI("sip:9005#serverpoint");
const session = new Inviter(userAgent, target as URI);
// Setup session state change handler
session.stateChange.addListener((newState: SessionState) => {
switch (newState) {
case SessionState.Establishing:
alert("Ringing");
break;
case SessionState.Established:
alert("Answered");
break;
case SessionState.Terminated:
alert("Ended");
break;
}
});
// Options including delegate to capture response messages
const inviteOptions: InviterInviteOptions = {
requestDelegate: {
onAccept: (response) => {
console.log(response.message);
alert("Positive response");
console.log("Positive response = " + response);
},
onReject: (response) => {
console.log("Negative response = " + response);
},
},
sessionDescriptionHandlerOptions: {
constraints: {
audio: true,
video: false,
},
},
};
const callSIP = () => {
session
.invite(inviteOptions)
.then((request: OutgoingInviteRequest) => {
alert("Successfully sent INVITE");
alert("INVITE request = " + request);
})
.catch((error: Error) => {
console.log("Failed to send INVITE");
});
};
const stop = () =>
setTimeout(() => {
userAgent
.stop()
.then(() => {
console.log("Stopped");
})
.catch((error) => {
console.error("Failed to stop");
});
}, 5000);
userAgent
.start()
.then(() => {
console.log("Connected");
const registerer = new Registerer(userAgent);
registerer.stateChange.addListener((newStat) => {
console.log(newStat);
// switch (newState) {
// case RegistererState.Registered:
// console.log("Registered");
// break;
// case RegistererState.Unregistered:
// console.log("Unregistered");
// break;
// case RegistererState.Terminated:
// console.log("Terminated");
// break;
// }
});
registerer
.register()
.then((request) => {
alert("Successfully sent REGISTER");
alert("Sent request = " + request);
})
.catch((error) => {
console.error("Failed to send REGISTER");
});
if (registerer.state === "Registered") {
// Currently registered
alert("Registered");
}
// stop();
})
.catch((error) => {
console.error("Failed to connect");
});
const panes = [
{
menuItem: "Calls",
render: () => (
<Tab.Pane loading={false}>
<List celled>
<List.Item>
<audio ref={audioRef} id="remote-audio"></audio>
</List.Item>
{props.data.map((item) => (
<List.Item key={v4()} onClick={callSIP}>
<Image avatar src="" />
<List.Content>
<List.Header>
{item.location} - {item.number}
</List.Header>
{item.timestamp}
</List.Content>
<List.Content floated="right">
<Button>Call</Button>
</List.Content>
</List.Item>
))}
</List>
</Tab.Pane>
),
},
{
menuItem: "tab 2",
render: () => <Tab.Pane>Tab 2 Content</Tab.Pane>,
},
];
return (
<Container>
<Tab panes={panes} />
</Container>
);
};
export default CallList;
After session is established you can set medias, example: https://github.com/onsip/SIP.js/blob/master/src/platform/web/simple-user/simple-user.ts#L750
I am running a graphql query inside getFriendId()that returns an id, followed by a mutation (inside addFriend(), which uses the id, along with an input (email) that the user types in. The problem is that on the first attempt, the mutation works fine and with correct values. However, when I change the email address on the input and run the query/mutation again, the values from my previous attempt are being used.
For instance, in the second attempt, the mutation is still using the id that we got in the first attempt. So basically, the values with setId and setEmail aren't being updated timely. How can I fix this?
const [id, setId] = useState('');
const [friendEmail, setFriendEmail] = useState('');
const [loadUsers, { loading, data, error }] = useLazyQuery(LoadUsersQuery);
const [createUserRelationMutation, { data: addingFriendData, loading: addingFriendLoading, error: addingFriendError }] = useCreateUserRelationMutation();
const getFriendId = () => {
console.log('Email', friendEmail.toLocaleLowerCase());
loadUsers({
variables: {
where: { email: friendEmail.toLocaleLowerCase() },
},
});
if (data) {
console.log('ID', data.users.nodes[0].id);
setId(data.users.nodes[0].id);
}
addFriend();
};
const addFriend = () => {
console.log('Whats the Id', Number(id));
createUserRelationMutation({
variables: {
input: {relatedUserId: Number(id), type: RelationType.Friend, userId: 7 }
},
});
if (addingFriendData){
console.log('Checking')
console.log(data);
}
if(addingFriendError){
console.log('errorFriend', addingFriendError.message);
setErrorMessage(addingFriendError.message);
}
}
const handleSubmit = () =>
{getFriendId();};
The return looks like something like this:
<Input
placeholder="Email"
onChangeText={(text) => setFriendEmail(text)}
value={friendEmail}
/>
<Button
rounded
onPress={() => handleSubmit()}>
Add Friend{' '}
</Button>
Current Picture Of My Code:
export const AddFriendEmailPage: React.FunctionComponent<AddFriendEmailPageProps> = ({
toggleShowPage,
showAddFriendEmailPage,
}) => {
const [id, setId] = useState('');
const [friendEmail, setFriendEmail] = useState('ana#hotmail.com');
const [errorMessage, setErrorMessage] = useState('');
const [loadUsers, { loading, data, error }] = useLazyQuery(LoadUsersQuery, {
variables: {
where: { email: friendEmail.toLocaleLowerCase() },
},
onCompleted: ( data: any ) => {
console.log('Working');
if (data) {
console.log(data);
if (data.users.nodes.length == 0) {
console.log('No user');
setErrorMessage('User Not Found');
} else {
const friendId = data.users.nodes[0].id;
console.log('friendId', friendId);
// setId(data.users.nodes[0].id);
const relationParams = {
input: {
relatedUserId: Number( friendId ),
type: RelationType.Friend,
userId: 8, // current user?
},
}
console.log("relation params", relationParams);
// fire second query/mutation using received data
createUserRelationMutation( { variables: relationParams } );
}
} else {
if (error) {
setErrorMessage(error.message);
}
}
}
});
const [
createUserRelationMutation,
{
data: addingFriendData,
loading: addingFriendLoading,
error: addingFriendError,
},
] = useCreateUserRelationMutation( {
variables: {
input: {
relatedUserId: Number(id),
type: RelationType.Friend,
userId: 8,
},
},
onCompleted: ( addingFriendData: any) => {
console.log("relation created", addingFriendData);
}
});
return (
<View style={scaledAddFriendEmailStyles.searchTopTextContainer}>
</View>
<View style={scaledAddFriendEmailStyles.searchFieldContainer}>
<Item style={scaledAddFriendEmailStyles.searchField}>
<Input
placeholder="Email"
//onChangeText={(text) => setFriendEmail(text)}
value={friendEmail}
/>
</Item>
<View style={scaledAddFriendEmailStyles.buttonContainer}>
<Button
rounded
style={scaledAddFriendEmailStyles.button}
//onPress={() => handleSubmit()}
onPress={()=>loadUsers()}
>
<Text style={scaledAddFriendEmailStyles.text}>
Add Friend{' '}
</Text>
</Button>
</View>
);
};
Both (mutation and lazyquery) have possibility to define onCompleted (inside options object) function for handling responses/results.
if(data) doesn't wait for results - data is from component scope, will be defined after first useLazyQuery firing.
onCompleted handler receives data as argument, it will be current request result (different to data in component scope - later, in next render 'flow' will be the same).
update
const [loadUsers, { loading, data, error }] = useLazyQuery(LoadUsersQuery, {
variables: {
where: { email: friendEmail.toLocaleLowerCase() },
},
onCompleted: ( data ) => {
// getFriendId
console.log('Working');
if (data) {
console.log(data);
if (data.users.nodes.length == 0) {
console.log('No user');
setErrorMessage('User Not Found');
} else {
const friendId = data.users.nodes[0].id;
console.log('friendId', friendId);
// needed?
// setId(data.users.nodes[0].id);
const relationParams = {
input: {
relatedUserId: Number( friendId ),
type: RelationType.Friend,
userId: 5, // current user?
},
}
console.log("relation params", relationParams);
// fire second query/mutation using received data
createUserRelationMutation( { variables: relationParams } );
}
} else {
if (error) {
setErrorMessage(error.message);
}
}
}
});
const [
createUserRelationMutation,
{
data: addingFriendData,
loading: addingFriendLoading,
error: addingFriendError,
},
] = useMutation(CreateUserRelationMutation, {
variables: {
input: {
relatedUserId: Number(id),
type: RelationType.Friend,
userId: 5,
},
},
onCompleted: ( data ) => {
console.log("relation created", data);
}
});