In my flutter & firebase app, I use cloud functions to send notifications for any new messages. The problem I face is how I show count of total unread messages in the chatroom list tile as shown in red circles in this famous chat app.
My chatroom page looks almost like this picture except for the unread message counter at the right side of each tile. What logic do I have to use?
Please give me any advice to implement showing count of unread messages at the right side of each chatroom tile.
Here is my chatroom collection in firestore.
You can simply add 3 more fields in the document:-
1stpersonMessageCount
2ndPersonMessageCount
MessageCount
So now what you need to do is that in your chatting screen you simply need to increment the message count of the person who is sending the message everytime he send the message:-
chatRoomReference.updateData({
"messageCount":FieldValue.increment(1),
"${currentUser}messageCount":FieldValue.increment(1),
});
and for message seen purpose you can simply update the messagecount of reciever by
chatRoomReference.updateData({
"${currentUser}messageCount":snapshot.documents.length,
});
and on the page where you want to see the count of new messages you simply subtract the value of messageCount and cureentUserMessageCount.
In both the code sections you can see that currentUser is there bt don't get confused when the sender is sending the message then currentUser's value would be the sender's name or his id and when someone is reading then his id or name. Ex- John is sending then current User will be John and when mohan is reading the message then it would be mohan. Hence you will have 3 fields i.e. JohnMessageCount, MohanMessageCount and total messages i.e. messageCount.
Let`s imagine we have server A with publisher and servers B and C with consumers.
Also we have got 5 different subjects; foo1, foo2,... foo5.
We always want to send a message only to one consumer and receive only one response.
So we utilize the requestOne function from the JS SDK at the publisher side and subscribe function with the {queue: "default"} option.
So both servers B and C has been subscribed one time for each subject.
But every time they subscribe they use queue with name "default" to prevent multiple consumers receive the same message as mentioned in docs.
So the question is:
Will this queue with name "default" be shared across all the subjects? Or each subject will have his own queue with name "default" and it is just shared between the subscribers of particular subject.
For example: producer generates 10 messages 2 for each subject.
Will we have 10 messages processed at the same time or only 2 messages since all the subscription share the same queue with name "default"?
You form a queue group based on the queue name that you specify and the subject. So a queue group of "foo" is different than a queue group on "bar".
That being said, with wildcards, you could have multiple subjects being part of the same queue group. That is, 2 members of the group "bar" listening on "foo.*" would split processing of messages sent on "foo.bar", "foo.baz", etc..
The same queue name in different subjects is separate.
You can test it with the examples in the link below.
https://nats.io/documentation/additional_documentation/nats-queueing/
start nats server
gnatsd
sub subject1
go run nats-qsub.go subject1 default
...
sub subject2
go run nats-qsub.go subject2 default
...
pub subject1&2
go run nats-pub.go subject1 "message"
...
go run nats-pub.go subject2 "message"
...
I'm writing an application using Elixir Channels to handle realtime events. I understand that there will be 1 socket open per client and can multiplex multiple channels over it. So my app is a chat application where users are part of multiple group chats. I have 1 Phoenix Channel called MessageChannel where the join method will handle dynamic topics.
def join("groups:" <> group_id, payload, socket) do
....
Let's say John joins groups/topics A and B while Bob only join group/topic B. When john sends a message to group/topic A, broadcast!/3 will also send that message to Bob too correct? Because handle_in doesn't have a context of which topic/group the message was sent to.
How would I handle it so that Bob doesn't receive the events that was sent to group A. Am I designing this right?
Because handle_in doesn't have a context of which topic/group the message was sent to.
When Phoenix.Channel.broadcast/3 is called, apparently it does have the topic associated with the message (which is not obvious from the signature). You can see the code starting on this line of channel.ex:
def broadcast(socket, event, message) do
%{pubsub_server: pubsub_server, topic: topic} = assert_joined!(socket)
Server.broadcast pubsub_server, topic, event, message
end
So when the call to broadcast/3 is made using the socket, it pattern matches out the current topic, and then makes a call to the underlying Server.broadcast/4.
(If you're curious like I was, this in turn makes a call to the underlying PubSub.broadcast/3 which does some distribution magic to route the call to your configured pubsub implementation server, most likely using pg2 but I digress...)
So, I found this behavior not obvious from reading the Phoenix.Channel docs, but they do state it explicitly in the phoenixframework channels page in Incoming Events:
broadcast!/3 will notify all joined clients on this socket's topic and invoke their handle_out/3 callbacks.
So it's only being broadcasted "on this socket's topic". They define topic on that same page as:
topic - The string topic or topic:subtopic pair namespace, for example “messages”, “messages:123”
So in your example, the "topics" are actually the topic:subtopic pair namespace strings: "groups:A" and "groups:B". John would have to subscribe to both of these topics separately on the client, so you would actually have references to two different channels, even though they're using the same socket. So assuming you're using the javascript client, the channel creation looks something like this:
let channelA = this.socket.channel("groups:A", {});
let channelB = this.socket.channel("groups:B", {});
Then when you go to send a message on the channel from a client, you are using only the channel that has a topic that gets pattern matched out on the server as we saw above.
channelA.push(msgName, msgBody);
Actually, the socket routing is done based on how to define your topics in your projects Socket module with the channel API. For my Slack clone, I use three channels. I have a system level channel to handle presence update, a user channel, and a room channel.
Any given user is subscribed to 0 or 1 channels. However, users may be subscribed to a number of channels.
For messages going out to a specific room, I broadcast them over the room channel.
When I detect unread messages, notifications, or badges for a particular room, I use the user channel. Each user channel stores the list of rooms the user has subscribed too (they are listed on the client's side bar).
The trick to all this is using a couple channel APIs, mainly intercept, handle_out, My.Endpoint.subscribe, and handle_info(%Broadcast{},socket).
I use intercept to catch broadcasted messages that I want to either ignore, or manipulate before sending them out.
In the user channel, I subscribe to events broadcast from the room channel
When you subscribe, you get a handle_info call with the %Broadcast{} struct that includes the topic, event, and payload of the broadcasted message.
Here are couple pieces of my code:
defmodule UcxChat.UserSocket do
use Phoenix.Socket
alias UcxChat.{User, Repo, MessageService, SideNavService}
require UcxChat.ChatConstants, as: CC
## Channels
channel CC.chan_room <> "*", UcxChat.RoomChannel # "ucxchat:"
channel CC.chan_user <> "*", UcxChat.UserChannel # "user:"
channel CC.chan_system <> "*", UcxChat.SystemChannel # "system:"
# ...
end
# user_channel.ex
# ...
intercept ["room:join", "room:leave", "room:mention", "user:state", "direct:new"]
#...
def handle_out("room:join", msg, socket) do
%{room: room} = msg
UserSocket.push_message_box(socket, socket.assigns.channel_id, socket.assigns.user_id)
update_rooms_list(socket)
clear_unreads(room, socket)
{:noreply, subscribe([room], socket)}
end
def handle_out("room:leave" = ev, msg, socket) do
%{room: room} = msg
debug ev, msg, "assigns: #{inspect socket.assigns}"
socket.endpoint.unsubscribe(CC.chan_room <> room)
update_rooms_list(socket)
{:noreply, assign(socket, :subscribed, List.delete(socket.assigns[:subscribed], room))}
end
# ...
defp subscribe(channels, socket) do
# debug inspect(channels), ""
Enum.reduce channels, socket, fn channel, acc ->
subscribed = acc.assigns[:subscribed]
if channel in subscribed do
acc
else
socket.endpoint.subscribe(CC.chan_room <> channel)
assign(acc, :subscribed, [channel | subscribed])
end
end
end
# ...
end
I also use the user_channel for all events related to a specific user like client state, error messages, etc.
Disclaimer: I have not looked at the internal workings of a channel, this information is completely from my first experience of using channels in an application.
When someone joins a different group (based on the pattern matching in your join/3), a connection over a separate channel (socket) is made. Thus, broadcasting to A will not send messages to members of B, only A.
It seems to me the Channel module is similar to a GenServer and the join is somewhat like start_link, where a new server (process) is spun up (however, only if it does not already exist).
You can really ignore the inner workings of the module and just understand that if you join a channel with a different name than already existing ones, you are joining a unique channel. You can also just trust that if you broadcast to a channel, only members of that channel will get the message.
For instance, in my application, I have a user channel that I want only a single user to be connected to. The join looks like def join("agent:" <> _agent, payload, socket) where agent is just an email address. When I broadcast a message to this channel, only the single agent receives the message. I also have an office channel that all agents join and I broadcast to it when I want all agents to receive the message.
Hope this helps.
I'm afraid the response is a big NO, but i will expose the scenario i try to implement. Maybe someone sees a better way to do it.
We have a fanout exchange, representing a conversations between N users (conv1)
We have two exchanges named user1, user2, binded to conv1, where conv1 is the source,
Each of this user exchanges is binded to conv1, with conv1 as destination, with routing key conv1
If we send a message trough exchange user1 with routing key "conv1", the message reaches all consumers connected to user1 and user2 exchanges.
A user should be able to have more more than one consumer listening to his exchange
What i need to do is that when there's no consumer attached to a user exchange, those messages could be queued, and received via the user exchange when connected. My first idea was to put a queue between the user exchange and the conversation exchange, so the flow would go like:
consumer-user1 --> tempqueue --> user1 --> conv1 --> queueuser2 --> user2 --> consumer-user2
But it seems it's not possible to make the binding queueuser2 --> user2 in that direction.
If i make consumers to listen directly to this queueuserN, by binding them to conv1 instead of binding the user exchanges:
consumer-user1 --> tempqueue --> user1 --> conv1 --> queueuser2 --> consumer-user2
Then (ovbiously) the message only reaches one of the N possible connected consumers.
There is any way to do this? It seems that the shovel plugin does that, but between brokers...
You can use the alternate exchange RabbitMQ extension: its purpose is exactly to reroute messages that cannot be sent to any queue, and would otherwise be lost.
Couldn't find any articles answering this specific question so here goes.
Say you have a topic called companyorders and you have 3 filters/subscriptions, companyA, companyB and allcompanies.
Messages sent to the topic for companyA get passed to sub companyA and allcompanies etc. Then messages start coming in for a companyC that hasn't got a specific sub setup so they are sent to only allcompanies sub.
When companyC starts up their client app and it creates the companyC sub(I don't see a way of setting up a sub with a specific filter in the portal) how or can I pull messages from the allcompanies sub for companyC that where previously missed because the sub was not setup beforehand?
Hope that makes sense.
Thanks
Paul
Seems that the subscription/filter needs to be setup before messages are sent to the topic. I tested this by creating a topic and a subscription. I then posted messages to the topic with property DriverID. I passed in a DriverID = 1. This message ended up in the subscription setup earlier as this subscription has the 'MatchAll' filter by default.
I then created another subscription with a filter of DriverID = 1. When I posted a message to the topic and set the property DriverId = 1 it got sent to the 2 subscriptions as expected. Messages posted before this subscription was setup that had DriverID = 1 were not automatically moved to the new subscription that matches the filter.
Paul