react-native-gifted-chat showing same message multiple time - reactjs

I'm using react-native-gifted-chat in my react-native app. As I shown in this image, there is same message displayed multiple time and message: Yes getting new msg 's place is also varied from it's actual position.
My issue is same as this. Can anyone please help me to solve this.
Thank you in advance.

I got a solution of my question. #Ron you are right but in my case the issue is different. I solved it by change my format of parameters. It took different format and I passed different so they conflicted each other. Here is the solution it may useful to others.
parse = snapshot => {
const { timestamp: numberStamp, text } = snapshot.val();
const { key: _id } = snapshot;
const createdAt = moment(snapshot.val().createdAt, "DD/MM/YYYY hh:mm:ss");
const user = { };
var temp_data = snapshot.val()
if(snapshot.val().name == this.state.temp_logged_name) {
user._id = 1;
user.name = temp_data.name;
user.avatar = temp_data.avatar;
}
const message = {
_id,
createdAt,
text,
user,
};
return message;
};

I had encountered this issue as well. I had set up react-native-gifted-chat on my mobile app. And at the other end I had set up a simple HTML page with a script to initialise the Websocket connection and send messages on the onsend event. What I had realised later that while the unique id was getting generated at the app end (because the id was being generated by the library itself), nothing of such sort existed at the other end.
Basically, this weird behaviour crops up when a unique id _id is missing for a message. Each message must have at least the following properties while executing GiftedChat.append(previousMessages, messages).
{
_id: 1,
text: 'Hello developer',
createdAt: new Date(),
user: {
_id: 2
}
}

There could be two reasons behind it,
1) Each message should be passed a unique id, so just use uuidv4 npm package and append it to _id prop of the object.
Example:
messages: GiftedChat.append(previousState.messages, {
_id: uuidv4(), // or use Math.round(Math.random() * 1000000)
text: text,
createdAt: new Date(),
user: {
_id: 2,
name: "React Native",
avatar: "https://placeimg.com/140/140/any"
},
image: attachment
})
2) Second possibility could be on the gateway you are using to initiate the chat between users. So, some gateways have known issues to repeat the message multiple times. You could to string comparison each time a new message is received and pushed to the chat screen, however it is not advised to do this.

I figured this out by simply applying the filter to the incoming message in useLayout Effect:
useLayoutEffect(() => {
db.collection('Chats').doc(docID).collection('messages').orderBy("createdAt", "desc").onSnapshot(snapshot => {
setMessages(
prev =>
prev
.filter((ftr,index,self) => ftr?.user?._id !== loginUser?.id) //login user id is the current user's id you can do the same for recieved messages
.concat
(snapshot.docs.map(doc => doc.data({
_id: doc?.id,
user: doc.data().user,
text: doc.data().text,
createdAt:new Date(doc.data().createdAt),
})
))
)
})
},[])
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Related

AxiosError when integrating Stripe with Next.js

I am relatively new to Next.js, and I though I have been encountering some bugs and issues here and there, I have been able to overcome most of them. The latest one I have not been able to figure out, so let's see if somebody else knows what's going on.
I am creating an e-commerce platform on Next.js, Redux and Axios. For the moment I am using fake data to populate the products. When creating a checkout session, the data of the items in the cart is pushed (I can console.log() and I see the items in the terminal. However, the mapping of the checkout session to Stripe is not working. The error I get is an AxiosError: Request failed with status code 500
Error message screenshot
I am trying to add the item data dynamically to the checkout session as follows:
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
export default async (req, res) => {
const { items, email } = req.body;
const transformedItems = items.map((item) => ({
description: item.description,
// if quantities are bundled, this needs to change.
quantity: 1,
price_data: {
currency: 'usd',
unit_amount: item.price * 100,
product_data: {
name: item.title,
images: [item.image],
},
},
}));
const session = await stripe.checkout.sessions.create({
line_items: transformedItems,
mode: 'payment',
success_url: `${process.env.HOST}/success`,
cancel_url: `${process.env.HOST}/checkout`,
metadata: {
email,
images: JSON.stringify(items.map((item) => item.image)),
},
});
res.status(200).json({ id: session.id });
};
I have also tried copying the exact code from the Stripe documentation and implementing the changes, but this hasn't changed anything either.
I know, Stripe has made some changes to their API, and that for instance you can't specify anymore with statements like
payment_method_types: ["card"],
anymore. So I took it out.
I have not included any code from the checkout piece, as this seems to be working (as stated, it console.logs() just fine. I can provide this as well though, if someone thinks the issue might be there.
Thanks in advance.
Nela.
Thanks to Code-Apprentice and maiorano84 whose hints in the comments:
A status code 500 means there is an error on the backend. If the server is under your control, then you need to look at the server logs to see what the problem is. The server logs will have a stack trace that shows you where the problem occurs. If you need help understanding the stacktrace, you will need to include it in your question. – Code-Apprentice 22 hours ago
Is this a server-side or client-side AJAX request? If it's the latter, check your network tab to see the full output of your failed request (marked in red in Chrome Devtools). You should be able to get more information about the failed request there. If it's failing on the Stripe side, the Response Headers and Body should have more information there to help you debug. If it's failing on your own success and checkout callbacks, your server logs might have additional information that can help you. – maiorano84 22 hours ago
led me to the answer. I checked my console, and the error that was given was from Stripe. It read as follows:
StripeInvalidRequestError: You cannot use line_items.amount, line_items.currency, line_items.name, line_items.description, or line_items.images in this API version. Please use line_items.price or line_items.price_data.
So I moved the item.description I had outside of the product_data object, into it, and it worked.
The code looks now like this:
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
export default async (req, res) => {
const { items, email } = req.body;
const transformedItems = items.map((item) => ({
// if quantities are bundled, this needs to change.
quantity: 1,
price_data: {
currency: 'usd',
unit_amount: item.price * 100,
product_data: {
name: item.title,
description: item.description,
images: [item.image],
},
},
}));
const session = await stripe.checkout.sessions.create({
line_items: transformedItems,
mode: 'payment',
success_url: `${process.env.HOST}/success`,
cancel_url: `${process.env.HOST}/checkout`,
metadata: {
email,
images: JSON.stringify(items.map((item) => item.image)),
},
});
res.status(200).json({ id: session.id });
};

Trouble with updating state with socket.io, after state is set it is not retained before the next socket message

So I am trying to make a basic comment app using reactjs, I have a nestjs backend which I know already works as I made a bare bones html page to test the concept.
Basically on a new "message" I am trying to set the state of comments equal to [...comments, newComment] so it retains the previous comments.
But what is happening is every time I receive a message from the server, my state (comments) is getting overwritten with the new message and is not retaining any data.
I did some trouble shooting and it looks like the state (comments) is not being set at all?
Here is my code:
const socket = io('http://localhost:4000');
function App() {
const [comments, setComments] = useState([
]);
function newComment(comment) {
console.log("Request for new comment: ");
console.log(comments);
setComments([...comments, comment]);
}
useEffect(() => {
console.log("Comments updated to: ")
console.log(comments);
}, [comments]);
useEffect(() => {
socket.on("message", data => {
const theComment = data.data;
let leComment = { id: 4, name: 'ted', comment: theComment }
newComment(leComment);
});
}, []);
Here is my console in chrome for debugging:
Any help or suggestions is much appreciated, I've been stuck on this for a whole day now!
The end result I'm looking for is just for any new "message" that comes in, the comment in that message is appended to my state which is called comments
The problem I see here is this
setComments([...comments, comment])
try to change it in
setComments(comments => [...comments, comment])

Stripe webhook checkout.session items to Supabase

Im using next.js and Stripe webhooks to insert checkout sessions to Supabase that will create a customer's order history. I'm able to get the information about the whole order written to a table called 'orders', but am wondering what the best way to add individual items within each checkout session to another table called 'order_items' is. This way I can map through main orders and then the children items. Appreciate any help provided. Here is what I have for getting orders associated with a customer:
const upsertOrderRecord = async (session: Stripe.Checkout.Session, customerId: string) => {
const { data: customerData, error: noCustomerError } = await supabaseAdmin
.from<Customer>('customers')
.select('id')
.eq('stripe_customer_id', customerId)
.single();
if (noCustomerError) throw noCustomerError;
const { id: uuid } = customerData || {};
const sessionData: Session = {
id: session.id,
amount_total: session.amount_total ?? undefined,
user_id: uuid ?? undefined
};
const { error } = await supabaseAdmin.from<Session>('orders').insert([sessionData], { upsert: true });
if (error) throw error;
console.log(`Product inserted/updated: ${session.id}`);
};
The Checkout Session object contains a line_items field which is a list of each item included in the purchase.
However this field is not included in the object by default, and therefore won't be a part of your webhook payload. Instead you'll need to make an API call in your webhook handle to retrieve the Checkout Session object, passing the expand parameter to include the line_items field:
const session = await stripe.checkout.sessions.retrieve('cs_test_xxx', {
expand: ['line_items']
});

React Apollo updating client cache after mutation

I am trying to update my chache after succesfully executing a mutation. Here is my query and mutation:
export const Dojo_QUERY = gql`
query Dojo($id: Int!){
dojo(id: $id){
id,
name,
logoUrl,
location {
id,
city,
country
},
members{
id
},
disziplines{
id,
name
}
}
}`;
export const addDiszipline_MUTATION = gql`
mutation createDisziplin($input:DisziplineInput!,$dojoId:Int!){
createDisziplin(input:$input,dojoId:$dojoId){
disziplin{
name,
id
}
}
}`;
and my mutation call:
const [createDisziplin] = useMutation(Constants.addDiszipline_MUTATION,
{
update(cache, { data: { createDisziplin } }) {
console.log(cache)
const { disziplines } = cache.readQuery({ query: Constants.Dojo_QUERY,variables: {id}});
console.log(disziplines)
cache.writeQuery({
...some update logic (craches in line above)
});
}
}
);
when i execute this mutation i get the error
Invariant Violation: "Can't find field dojo({"id":1}) on object {
"dojo({\"id\":\"1\"})": {
"type": "id",
"generated": false,
"id": "DojoType:1",
"typename": "DojoType"
}
}."
In my client cache i can see
data{data{DojoType {...WITH ALL DATA INSIDE APPART FROM THE NEW DISZIPLINE}}
and
data{data{DisziplineType {THE NEW OBJECT}}
There seems to be a lot of confusion around the client cache around the web. Somehow none of the posed solutions helped, or made any sense to me. Any help would be greatly appreciated.
EDIT 1:
Maybe this can help?
ROOT_QUERY: {…}
"dojo({\"id\":\"1\"})": {…}​​​​​
generated: false​​​​​
id: "DojoType:1"​​​​​
type: "id"​​​​​
typename: "DojoType"​​​​​
<prototype>: Object { … }​​​​
<prototype>: Object { … }
Edit 2
I have taken Herku advice and started using fragment. however it still seems to not quite work.
My udated code:
const [createDisziplin] = useMutation(Constants.addDiszipline_MUTATION,
{
update(cache, { data: { createDisziplin } }) {
console.log(cache)
const { dojo } = cache.readFragment(
{ fragment: Constants.Diszilines_FRAGMENT,
id:"DojoType:"+id.toString()});
console.log(dojo)
}
}
);
with
export const Diszilines_FRAGMENT=gql`
fragment currentDojo on Dojo{
id,
name,
disziplines{
id,
name
}
}
`;
however the result from console.log(dojo) is still undefined.Any advice?
So I think your actual error is that you have to supply the ID as as a string: variables: {id: id.toString()}. You can see that these two lines are different:
dojo({\"id\":1})
dojo({\"id\":\"1\"})
But I would highly suggest to use readFragment instead of readQuery and update the dojo with the ID supplied. This should update the query as well and all other occurrences of the dojo in all your queries. You can find documentation on readFragment here.
Another trick is as well to simply return the whole dojo in the response of the mutation. I would say people should be less afraid of that and not do to much cache updates because cache updates are implicit behaviour of your API that is nowhere in your type system. That the new disziplin can be found in the disziplins field is now encoded in your frontend. Imagine you want to add another step here where new disziplins have to be approved first before they end up in there. If the mutation returns the whole dojo a simple backend change would do the job and your clients don't have to be aware of that behaviour.

Comparing results from two API calls and returning their difference in MEAN app

EDIT: Since I wasn't able to find a correct solution, I changed the
application's structure a bit and posted another question:
Mongoose - find documents not in a list
I have a MEAN app with three models: User, Task, and for keeping track of which task is assigned to which user I have UserTask, which looks like this:
const mongoose = require("mongoose");
const autopopulate = require("mongoose-autopopulate");
const UserTaskSchema = mongoose.Schema({
completed: { type: Boolean, default: false },
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
autopopulate: true
},
taskId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Task",
autopopulate: true
}
});
UserTaskSchema.plugin(autopopulate);
module.exports = mongoose.model("UserTask", UserTaskSchema);
In my frontend app I have AngularJS services and I already have functions for getting all users, all tasks, and tasks which are assigned to a particular user (by getting all UserTasks with given userId. For example:
// user-task.service.js
function getAllUserTasksForUser(userId) {
return $http
.get("http://localhost:3333/userTasks/byUserId/" + userId)
.then(function(response) {
return response.data;
});
}
// task-service.js
function getAllTasks() {
return $http.get("http://localhost:3333/tasks").then(function(response) {
return response.data;
});
}
Then I'm using this data in my controllers like this:
userTaskService
.getAllUserTasksForUser($routeParams.id)
.then(data => (vm.userTasks = data));
...and because of autopopulate plugin I have complete User and Task objects inside the UserTasks that I get. So far, so good.
Now I need to get all Tasks which are not assigned to a particular User. I guess I should first get all Tasks, then all UserTasks for a given userId, and then make some kind of difference, with some "where-not-in" kind of filter.
I'm still a newbie for all the MEAN components, I'm not familiar with all those then()s and promises and stuff... and I'm really not sure how to do this. I tried using multiple then()s but with no success. Can anyone give me a hint?
You can do at server/API side that will more efficient.
In client side, if you want to do then try below
var userid = $routeParams.id;
userTaskService
.getAllTasks()
.then((data) => {
vm.userTasks = data.filter(task => task.userId !== userid)
});

Resources