Hi all,
Looking for an extra pair of eyes here... I can't seem to figure out why the message sent from one user account doesn't show up on the other other user account of which they are having a conversation with.
Here's what I have, I've put everything in one code block and comments on, also the typing indicator doesn't work either, but I can see the client events and API message(s) been sent when I login to my Pusher account.
// EVENT
<?php
namespace App\Events;
use App\Models\Conversation;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class NewMessage implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $conversation;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(Conversation $conversation)
{
$this->conversation = $conversation;
}
/**
* Get the channels the event should broadcast on.
*
* @return Channel|array
*/
public function broadcastOn()
{
return new Channel('groups.'.$this->conversation->group->id);
}
public function broadcastAs()
{
return 'NewMessage';
}
public function broadcastWith()
{
return [
'message' => $this->conversation->message,
'user' => [
'name' => $this->conversation->user->name,
],
'date' => $this->conversation->created_at
];
}
}
// Controller for storing message
try {
$conversation = Conversation::create([
'message' => request('message'),
'group_id' => request('group_id'),
'user_id' => auth()->user()->id,
]);
$date_sent = now();
// $to = Carbon::createFromFormat('d/m/Y, H:s:i', $date_sent);
$from = Carbon::createFromFormat('Y-m-d H:s:i', $conversation->created_at);
$diff_in_minutes = $date_sent->diffInMinutes($from);
$conversation->response_time = $diff_in_minutes;
$conversation->update();
$conversation->load('user');
broadcast(new NewMessage($conversation))->toOthers(); //Should broadcast to others, but doesn't
return response()->json($conversation);
} catch(\Exception $e) {
\Log::info($e->getMessage() . ' on line ' . $e->getLine() . ' within ' . $e->getFile());
}
// Vue for the chat page
<template>
<div>
<div class="card body">
<div class="card mb-3 d-flex justify-content-center">
<div class="accordion" id="accordionExample">
<a class="btn btn-link "
data-toggle="collapse"
:href="`#collapse-` + group.id"
@click="read(group)"
>
<span class="row">
<span class="col-md-12">
<span class="mr-2 badge badge-danger badge-pill font-weight-bold" style="font-size: 13px;" v-if="unReadCount">{{ unReadCount }}</span>
<div v-if="group.site">
<h6>Site Name: <span class="text-muted">{{ group.site | capitalize }}</span> - Block Name: <span class="text-muted">{{ group.block | capitalize }}</span> - Unit No: <span class="text-muted">{{ group.unit }}</span> </h6>
</div>
<small>
<strong>Conversation party:</strong>
<span v-for="(user, index) in group.users" :key="index">
({{ user.account_type }}) {{ user.name }}
</span>
</small>
<hr />
<small><strong>Subject:</strong> {{ group.subject }} - <strong>Message ID:</strong> {{ group.unique_id }}</small>
<br />
<small v-if="group.property">
<strong>Property:</strong> {{ group.property.name_number }}, {{ group.property.town }}, {{ group.property.postcode }}
</small>
<span v-show="group.urgency">
<strong>Urgency</strong> <span class="badge badge-info">{{ group.urgency | capitalize }}</span>
</span>
</span>
<span class="col-md-12">
<a data-toggle="collapse" :href="`#collapse-` + group.id" aria-expanded="false" aria-controls="collapseExample">
<i class="fas fa-chevron-down"></i>
<i class="fas fa-chevron-up"></i>
</a>
</span>
</span>
</a>
<div :id="`collapse-${group.id}`" class="collapse" aria-labelledby="headingOne" data-parent="#accordionExample" v-cloak>
<div class="card-body col-md-12">
<div class="panel-body chat-panel border border-light border-bottom-0" :class="`chat-${group.id}`">
<ul class="list-unstyled" v-cloak>
<li class="media" v-for="(conversation,index) in messages" :key="index">
<i class="far fa-user mr-3 img-thumbnail"></i>
<div class="media-body">
<h5 class="mt-0 mb-1">
<span v-if="conversation.user.id == currentUser.id">Me</span>
<span v-else>{{ conversation.user['name'] }}</span>
</h5>
<p class="font-md text-secondary">
{{ conversation.message }}<br />
<small class="text-muted pt-1">at {{ human(conversation.updated_at) }}</small>
</p>
</div>
</li>
</ul>
</div>
<div class="panel-footer">
<div class="input-group">
<input
type="text"
class="form-control input-sm chat-input"
placeholder="Type your message here..."
v-model="message"
@keydown="isTyping"
>
<span class="input-group-btn">
<button class="btn btn-success"
style="border-radius: 0px 10px 10px 0px!important;"
@click="sendMessage"
>
<i class="far fa-paper-plane"></i> Send
</button>
</span>
</div>
<span v-show="typing" class="mt-1 mb-3 typing">
{{ typing }}
</span>
<br /><br />
<small class="alert alert-info">If the conversation is finished, you can can archive by clicking <a href="#" @click.prevent="archive(group.id)">here</a></small>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import moment from 'moment';
export default {
props: ['group_data', 'user'],
data() {
return {
message: '',
messages: [],
typing: '',
group: [],
unReadCount: '',
currentUser: [],
groupId: '',
}
},
mounted() {
this.group = this.group_data;
this.currentUser = this.user;
this.messages = this.group_data.messages;
this.listenForNewMessage();
this.unReadCount = this.filterUnreadCount();
},
created() {
let _this = this;
Echo.private('groups.' + this.group.id)
.listenForWhisper('typing', (e) => {
this.typing = this.currentUser.name + ' is typing...';
// remove is typing indicator after 0.9s
setTimeout(function() {
_this.typing = false
}, 900);
});
},
methods: {
human : function (date) {
return moment(date).format('Do MMMM YYYY h:mm:ss');
},
filterUnreadCount() {
return this.group.messages.filter((message) => {
return message.read_at === null && Laravel.user.id != message.user_id;
}).length;
return 0;
},
read(group) {
axios.post('/conversations/markAsRead/' + group.id)
.then((response) => {
this.unReadCount = 0;
});
this.groupId = group.id;
},
isTyping() {
let channel = Echo.private('groups.' + this.group.id);
setTimeout(function() {
channel.whisper('typing', {
user: this.currentUser.name,
typing: true
});
}, 300);
},
sendMessage() {
if(this.message == '') {
return;
}
axios.post('/conversations', {
message: this.message,
group_id: this.group.id,
// date: new Date().toLocaleString()
}).then((response) => {
this.messages.unshift(response.data);
this.message = '';
this.typing = '';
}).catch((error) => {
swal({
title: 'Sorry, there is a problem!',
text: 'There are errors on the page, please check and try again \n',
icon: 'error',
button: 'Continue',
});
this.errors = error.response.data.errors;
});
},
listenForNewMessage() {
Echo.private('groups.' + this.group.id)
.listen('NewMessage', (e) => {
console.log(e);
this.messages.unshift({
message: e.message,
user: e.user.name
});
});
},
archive(group_id) {
axios.post('/conversations/archive/' + this.group.id)
.then((response) => {
swal({
title: 'Success!',
text: 'Chat now archived.',
icon: 'success',
buttons: false,
});
setTimeout(() => {
location.reload();
}, 3000);
});
}
}
};
</script>