I have publishing setup along with sessions to send out messages to the right rooms.
I'm currently having issues as to how do I go about limiting returned messages so if a room has, say, 200 messages in it and another one is posted, the oldest one is deleted.
//how messages are created
Meteor.methods({
newMessage: function (message) {
message.user = Meteor.userId();
Messages.insert(message);
}
});
//how messages are published
Meteor.publish('messages', function (channel) {
return Messages.find({channel: channel});
});
//how chatrooms are published
Meteor.publish('channels', function () {
return Channels.find();
});
Problem is, normally I would do this by putting this in the publications {sort:{limit:15}}
However, that doesn't work in this case and results in ALL of the messages being limited.
They need to be sorted by room, or, well, per session:key.
Is there a simple way of going about this? Or would I have to make a method on the serverside to run .forEach channel?
There's no decent way of publishing the top 15 posts from each room in a single cursor. If the number of rooms is small it might make sense to publish an array of cursors instead, each cursor in the array corresponding to a single room.
Meteor.publish('messages', function (channel) {
return Messages.find({channel: channel}, {limit: 15});
});
I have no idea how I missed this but apparently I can just do it this way.
Huh.
(channel:channel corresponds to currently set session's name)
Related
I'm trying to create a basic TCG style game with Node/Vue/Socket.io and can't seem to figure out how to emit to both clients when a "ready" count = 2 but with different data, I'll explain a little below...
The sequence of events is as such:
player connects -> server sends player a "deck" -> player clicks ready to start and also sends back their first 'card'.. Then the server should send out to each player the other players first card. (Note my emit events don't have the correct titles atm - they were already written up on the front end so just kept them the same)
On connection I've pushed to an array called sockets, that I was using for testing. Then in the "ready" event I created an array called "firstCards" that I'm pushing the socket event data to then adding a .socket property to it (to signify who's who), then incrementing ready.
I've had a little bit of a play around with a few different methods but I can only seem to get the last card sent to both clients as opposed to each client getting the other clients first.. I also tried just putting the "if" statement outside of the socket event (as you will see below with the comment on the brackets/curly braces) which doesn't seem to work either.
I haven't tried this kind of asymmetric data transfer before and unsure if that is even the correct term... or whether this is even the correct way to do so, any help would be much appreciated!
This is the code I'm using so far:
socket.on('ready-up', function (card)
{
console.log(`Player ${socket.id} is ready`);
ready++;
console.log(ready);
card.socket = socket.id;
firstCards.push(card);
console.log(firstCards);
});
if (ready == 2)
{
for (let i = 0; i < sockets.length; i++)
{
io.to(sockets[i]).emit('p2hand', "Both players ready");
let opp = sockets.find(element => element != socket.id);
console.log(`Socket ID is: ${socket.id}`);
console.log(`Opp ID is: ${opp}`);
let card = firstCards.find(element => element.socket == opp)
console.log(card);
io.to(opp).emit('reveal',
{
'name': card.name,
'hp': card.hp,
'mp': card.mp,
'skills': card.skills,
'icon': card.icon
});
// io.to(opp).emit('reveal', card);
ready = 0;
}
}
// });
So I figured this one out for anyone who may end up wanting to do what I was trying to do....
I decided that upon connection, both clients join a room called "game1".
The server will then emit "firstCards" to that room.
After that it was just a case of making sure the player-client know which was the opponents card... Now I could have used the .name property for this, but I decided to add an "id" property using the socket.id instead due to the possibility of the same card being drawn for both players.
I'm thinking that all server-client interactions will now have to carry this property for any other cards in the game such as items, spells, etc
I'm building a chat application where I have conversations and messages, which are stored as respective objects. The messages are bundled according to the conversation that they belong to. So when I push messages, they're being pushed under their conversation's Id.
I have the following implementation :
allMsgs = {
...allMsgs,
[payload.conversationId]: {
...payload.message
}
}
I am able to add and categorize the messages into their respective conversations but not bundle the messages themselves. The last message is always overwriting the contents -which are the messages- of each conversationId. So I'm left with allMsgs having multiple conversatinIds but one message in each conversationId. This seems like such a duh thing but I can't seem to figure out.
Turned out to be a duh thing
state.allMsgs = {
...state.allMsgs,
[payload.conversationId]: {
...state.allMsgs[payload.conversationId],
[payload.message.id]: payload.message
}
}
I finally got RANGE_ADD mutations to work. Now I'm a bit confused and worried about their performance.
One outputField of a RANGE_ADD mutation is the edge to the newly created node. Each edge has a cursor. cursorForObjectInConnection()(docs) is a helper function that creates that cursor. It takes an array and a member object. See:
newChatroomMessagesEdge: {
type: chatroomMessagesEdge,
resolve: async ({ chatroom, message }) => {
clearCacheChatroomMessages();
const messages = await getChatroomMessages(chatroom.id);
let messageToPass;
for (const m of messages) {
if (m.id === message.id) {
messageToPass = m;
}
}
return {
cursor: cursorForObjectInConnection(messages, messageToPass),
node: messageToPass,
};
},
},
plus a similar edge for user messages.
Now this is what confuses me. Say you want to make a chatroom app. You have a userType, a chatroomTypeand a messageType. Both the userType and the chatroomType have a field messages. It queries for all of the user's or chatroom's messages respectively and is defined as a relay-connection that points to messageType. Now, each time a user sends a new message, two new RANGE_ADD mutations are committed. One that creates an edge for the chatroom's messages and one that creates an edge for the user's messages. Each time, because of cursorForObjectInConnection(), a query for all of the user's messages and one for all of the chatroom's messages is sent to a database. See:
const messages = await getChatroomMessages(chatroom.id);
As one can imagine, there occur lots of "message-sent-mutations" in a chatroom and the number of messages in a chatroom grows rapidly.
So here is my question: Is it really necessary to query for all of the chatroom messages each time? Performance-wise, this seems like an awful thing to do.
Sure, although I did not look into it yet, there are optimistic updates I can use to ease the pain client-side - a sent message gets displayed immediately. But still, the endless database queries continue.
Also, there is Dataloader. But as far as I understand Dataloader, it is used as a per request batching and caching mechanism - especially in conjunction with GraphQL. Since each mutation should be a new request, Dataloader does not help on that front either it seems.
If anyone can shed some light on my thoughts and worries, I'd be more than happy :)
I have an application where users can follow other users. I want to have a real-time update system, display the total count of followers a user has.
I've just started playing around with Firebase and Pusher, but I don't understand one thing - will each user have their own 'channel'? How would I handle such thing as a follower counter update?
I followed the tutorial and saw that push method can create lists, so one solution I can see is having a list of all the users, each user being an object something like this:
{ username: 'john_doe', follower_count: 6 }
But I don't understand how would I actually track it on the front end? Should I have a filter, something like this?
var user = users.filter(function(user) { return user.username === 'john_doe'; });
// code to display user.follower_count
Or is there a different way that I'm missing?
Most of Firebase logic is based on listeners, you can add a listener to events happening in your collections and when those events happen, you do something.
One way to go about this would be:
var myFollowerListRef = new Firebase(PATH_TO_YOUR_COLLECTION);
myFollowerListRef.on("value", function(snapshot) {
console.log(snapshot.length);
});
This way, every time your follower collection changes, the asynchronous function fires and you can do what you want with the fresh data.
For more information:
https://www.firebase.com/docs/web/guide/retrieving-data.html
Hope this helps, I'm still a beginner in Firebase.
#Th0rndike's approach is the simplest and works fine for relatively short lists. For longer lists, consider using a transaction. From the Firebase documentation on saving transactional data:
var upvotesRef = new Firebase('https://docs-examples.firebaseio.com/android/saving-data/fireblog/posts/-JRHTHaIs-jNPLXOQivY/upvotes');
upvotesRef.transaction(function (current_value) {
return (current_value || 0) + 1;
});
But I recommend that you read the entire Firebase guide. It contains solutions for a lot of common use-cases.
Not sure if I'm going about this the right way. I'll map out what I'm trying to achieve and please give me any feedback you can. Also very new to meteor so sorry if I'm a bit uneducated in some aspects. This is going to be a two player game where users login in with accounts-twitter or accounts-facebook. Here's the tricky part it will only initialize the game if there are two users logged in. To figure out who is logged in I have put this line of code in my server portion:
if (Meteor.isServer) {
Meteor.publish("userStatus", function() {
return Meteor.users.find({
"status.online": true
})
});
}
My idea of what needs to be done is write an if statement if(userStatus === true){get a users "_id"} then push that user into an array and have a for loop run through every 2 users signed on in the array and initialize a game for them. Also the main question is how do I grab the users ID if status.online is true? Also any input on how to make this more efficient is much appreciated.
Answering your "main question," you would do this with a mongoDB for-each like so.
Meteor.methods({
getOnline : function(){
var retArr = new Array();
Meteor.users.find({"status.online": true}).forEach(function(u){
retArr.push(u._id); //populated retArr with the id of users online
});
return retArr;
}
});
If I left out something or you need clarification on something, please comment.