Laravel Echo joining private channel `.here()` - laravel

I have a join to a private channel:
Echo.private('chat_room.'+comments_room_id)
.listen('.App.Events.Common.Comment.CommentCreated', function(e) {
e.comment.user = e.user;
e.comment.new_msg = 1;
_this.comment_room.comments.unshift(e.comment);
});
I would like to use the .here() presence call to keep a array of users updated with who is currently online.
I tried the following:
Echo.private('chat_room.'+comments_room_id)
.here(users => {
this.users = users;
})
.listen('.App.Events.Common.Comment.CommentCreated', function(e) {
But that did not work...
Error in console is:
Echo.private(...).here is not a function

So i discovered that you need to also join a Presence channel alongside the private channel in order to use the here() methods.
Echo.join('chat_room.'+comments_room_id)
.here((users) => {
this.users = users;
})
.joining((user) => {
this.users.push(user)
})
.leaving((person) => {
this.users = _.reject(this.users, user => user.id == person.id);
});

Related

RXJS how to optimize a ForEach in a nested subscribe?

I'm rather new to observables and still learning and i need some help.
I have a slider with some campaigns and for each campaign i have a producer which i need get and set to the campaign.
I know that using pipe, merge and MergeMap here is probably some of the solution but i can't seem to get it to work, does anyone have an idea for me?
This is what i currently have.
fetchCampaigns() {
this.loading = true;
this.campaignService
.getCampaigns(
this.campaignAmount,
this.sorting,
this.desc
)
.subscribe(
campaignListDto => {
this.campaigns = campaignListDto;
this.campaigns.map(campaign => {
this.producerService.getProducerByName(campaign.producer_name)
.subscribe((producer) => {
campaign.producer = producer
campaign.producer_name
})
})
this.loading = false;
this.fetchProducerMinPrices();
this.fetchProducerTotalCampaigns();
})
};
Try:
fetchCampaigns() {
this.loading = true;
this.campaignService.getCampaigns(this.campaignAmount,this.sorting,this.desc).pipe(
switchMap(campaignListDto => forkJoin(campaignListDto.map(
campaign => this.producerService.getProducerByName(campaign.producer_name).pipe(
map(producer => ({ ...campaign, producer })),
),
))),
).subscribe(campaignListDtoExtended => {
this.campaigns = campaignListDtoExtended;
this.loading = false;
this.fetchProducerMinPrices();
this.fetchProducerTotalCampaigns();
});
}

Pusher Laravel Broadcast Leave Room Notification

I am going to post all my methods inside my app just to keep this clear for me to read. Here is the goal. When a user enters a room, a message is added "USER HAS ENTERED THE ROOM" - that works fine. Now, what I want to do is when a user LEAVES a room, I want it to say "USER HAS LEFT THE ROOM" - the notification works, but it is showing the wrong username (it shows the username of the person SEEING the message.
What is it I a missing or not grasping
methods: {
connect() {
if (this.currentRoom?.id) {
let vm = this;
this.getMessages();
Echo.join("chat." + this.currentRoom.id)
.here(users => {
this.sendUserActivityNotification(this.$page.props.user, true)
this.users = users
this.usersCount = users.length
})
.joining(user => {
this.users.push(user)
this.usersCount = this.usersCount+1
})
.leaving(user => {
this.sendUserActivityNotification(this.$page.props.user, false)
this.users = this.users.filter(({id}) => (id !== user.id))
this.usersCount = this.usersCount-1
})
.listen('NewChatMessage', (e) => {
vm.getMessages();
});
}
},
setRoom(room) {
this.currentRoom = room;
},
getMessages() {
axios.get('/chat/room/' + this.currentRoom.id + '/messages')
.then(response => {
this.messages = response.data;
})
.catch(error => {
console.log(error);
})
},
sendUserActivityNotification(user, joining) {
const message = `${user.name} has ${joining === true ? 'entered' : 'left'} the room.`
return axios.post(`/chat/room/${this.currentRoom.id}/notifications`, {message}).then(() => this.getMessages())
.catch(console.error)
},
leaveRoom({id}) {
this.$store.dispatch(RoomTypes.LEAVE_ROOM, id)
}
},
I haven't used Laravel Echo, but after reading your code I think on the leaving method call, you are passing current user instead of what you are receiving from the event when you call the sendUserActivityNotification method.
Your implementation looks like this:
...
.leaving(user => {
this.sendUserActivityNotification(this.$page.props.user, false)
this.users = this.users.filter(({id}) => (id !== user.id))
this.usersCount = this.usersCount-1
})
....
You are passing this.$page.props.user to this.sendUserActivityNotification method instead of passing user.
You must change that call to this.sendUserActivityNotification(user, false)

Passing value from one RxJS operator to another

Here is my code:
#Injectable()
export class TraitementDetailEffects {
ingoing_loadDetail: { traitementID: number, obs: Promise<any> };
#Effect()
loadTraitementDetail$: Observable<Action> = this.actions$.pipe(
ofType(ETraitementDetailActions.loadTraitementDetail),
map((action: LoadTraitementDetail) => action.payload),
switchMap((traitementID) => {
if (this.ingoing_loadDetail && this.ingoing_loadDetail.traitementID === traitementID) {
return this.ingoing_loadDetail.obs;
}
const obs = this.traitementsService.loadDetail(traitementID);
this.ingoing_loadDetail = {traitementID: traitementID, obs: obs};
return obs;
}),
map(result => {
this.ingoing_loadDetail = null;
//here I don't have access to traitementID :'(
return new LoadTraitementDetailSuccess(traitementID, result);
})
);
constructor(
private actions$: Actions,
private traitementsService: TraitementsService
) {
}
}
I'm trying to pass the variable or value traitementID to the last map.
I tried to avoid the last map with an async await but then I get a weird errors "Effect dispatched an invalid action" and "Actions must have a type property" (FYI all my actions have a type property).
Try to bake this id into observable's resolve, like:
switchMap((traitementID) => {
return this.traitementsService.loadDetail(traitementID).pipe(
map(detail => ({detail,traitementID}))
);
}),
map(({detail,traitementID}) => {
...
})

RxJS multiple switchMap and map operators... is there a better way?

I'm pretty new to RxJS and am wondering if I am doing this right... in the ngOnInit() function below, I get a client object, then pipe it...
Is there a better way to do the repeat switchMap/map operations below?
My code works... but I am wondering if there is a more elegant approach that I should be adopting...
public client: Client;
public contract: Contract;
public alreadyPendingContract: boolean;
public alreadyActiveContract: boolean;
public minimumStartDate: Date;
public minimumEndDate: Date;
public rolloverExclusionDate: Date;
public startDateFilter;
ngOnInit() {
this.clientService.getClient$().pipe(
filter(client => client != null),
map(client => this.client = client),
pluck('client_id'),
map((client_id: string) => {
this.clientContractForm.get('client_id').setValue(client_id);
return client_id;
}),
switchMap((client_id: string) => {
return this.contractAddService.getAlreadyPendingContract$(client_id);
}),
map(alreadyPendingContract => {
this.alreadyPendingContract = alreadyPendingContract;
return this.client.client_id;
}),
switchMap((client_id: string) => {
return this.contractAddService.getAlreadyActiveContract$(client_id);
}),
map(alreadyActiveContract => {
this.alreadyActiveContract = alreadyActiveContract;
}),
switchMap(() => {
return this.contractAddService.getMinimumStartDate$(this.client.client_id);
}),
map((minimumStartDate: IMinimumStartDate) => {
this.minimumStartDate = minimumStartDate.minimumStartDate;
this.rolloverExclusionDate = minimumStartDate.rolloverExclusionDate;
this.startDateFilter = (m: Moment): boolean => {
// Filters out the rollover exclusion day from being an available start date.
return !moment.utc(m).isSame(moment.utc(this.rolloverExclusionDate), 'day');
}
})
).subscribe();
}
I am not sure this is more elegant, but it is an alternative way
ngOnInit() {
this.clientService.getClient$().pipe(
filter(client => client != null),
map(client => {
this.client = client;
this.clientContractForm.get('client_id').setValue(client_id);
return client.client_id;
},
switchMap(client_id => this.doStuffWithClientId(client_id)),
map((minimumStartDate: IMinimumStartDate) => {
this.minimumStartDate = minimumStartDate.minimumStartDate;
this.rolloverExclusionDate = minimumStartDate.rolloverExclusionDate;
this.startDateFilter = (m: Moment): boolean => {
// Filters out the rollover exclusion day from being an available start date.
return !moment.utc(m).isSame(moment.utc(this.rolloverExclusionDate), 'day');
}
})
).subscribe();
}
doStuffWithClientId(clientId: string) {
return this.contractAddService.getAlreadyPendingContract$(client_id).pipe(
tap(alreadyPendingContract => this.alreadyPendingContract = alreadyPendingContract),
switchMap(() => this.contractAddService.getAlreadyActiveContract$(clientId)),
tap(alreadyActiveContract => this.alreadyActiveContract = alreadyActiveContract),
switchMap(() => this.contractAddService.getMinimumStartDate$(clientId)),
)
}
I have not tested the code, so there may well be syntax mistakes. The basic idea though is to isolate all the things which depend on client_id into one function which receives the clientId as input and therefore makes it visible throughout the entire function.

How do i allow socket.io users to join a room with multiple namespaces?

I have a socket.js file on the server side, and I want my user communication to be separated into different namespaces, namely
this.mainRoom = io.of('/main_room')
this.privateRoom = io.of('/private_room')
When users join the room specific to '/main_room' namespace, it works fine.
But I want the user to be able to join the '/private_room' namespace.
Here's the code
const socks = function (io) {
let self = this
self.privateRoom = null
// where all the messaging and the events that are not part of the
// user-to-user communication
this.mainRoom = io.of('/main_room')
this.mainRoom.on('connection', (socket) => {
socket.on('set_name', (data) => {
const nickname = data.name
socket.nickname = nickname
socket.emit('name_set', data)
socket.send(JSON.stringify({
type: 'serverMessage',
nickname: socket.nickname,
message: "Welcome! "
})
)
socket.broadcast.emit('user_entered', {
nickname: socket.nickname,
})
})
socket.on('join_room', (room) => {
// concept roomname !== namespace
// concept Rooms are subchannels of the namespaces.
//create another socket, since socketio doesnt allow a single socket
//to be connected to multiple namespaces
socket.join(room.name, () => {
---------->> how do i allow user to join the room in the private_room namespace? <<---------
---------->> how should i create socket2 to join the same room.name? <<----------
// let socket2 = self.privateRoom.sockets[socket.id]
// socket2.join(room.name)
})
})
})
// split the socket code into 2 separate parts for namespaces
// this is so that the messaging and events that are related to users
// are in a namespace
// while messaging and events that are not part of user-to-user
// communication is separated
this.privateRoom = io.of('/private_room')
this.privateRoom.on('connection', (socket) => {
socket.on('message', (message) => {
message = JSON.parse(message)
if (message.type === "userMessage") {
socket.in(socket.room).broadcast.send(JSON.stringify(message))
message.type = "myMessage"
socket.send(JSON.stringify(message))
}
})
})
}
ah! silly error
Just had to rename this.privateRoom's socket as socket2 and make it a global variable
self.socket2 = null
this.privateRoom = io.of('/private_room')
this.privateRoom.on('connection', (socket2) => {
self.socket2 = socket2
self.socket2.on('message', (message) => {
message = JSON.parse(message)
if (message.type === "userMessage") {
self.socket2.in(self.socket2.room).send(JSON.stringify(message))
message.type = "myMessage"
self.socket2.send(JSON.stringify(message))
}
})
})
And give it back to mainRoom
let socket2 = self.socket2.join(room.name)
socket2.room = room.name
socket.in(room.name).emit('user_entered', {'name': socket.nickname})

Resources