I have the following scenario for a chatroon with Symfony 5.4 and MercureBundle. Chat conversation function like a charm, but I don't understand how to include additional info (username) into a payload config of mercure to get connected users (subscribed users to a chatroom topic) from the subscriptions list with the condition that I use mercure() twig extension.
Environment vars:
return array(
'MERCURE_URL' => 'https://pami54.local/.well-known/mercure',
'MERCURE_PUBLIC_URL' => 'https://pami54.local/.well-known/mercure',
'MERCURE_JWT_SECRET' => 'm3rcu353cr37pa55pra53DEV',
);
mercure.yaml
:
mercure:
hubs:
default:
url: '%env(MERCURE_URL)%'
public_url: '%env(MERCURE_PUBLIC_URL)%'
jwt:
secret: '%env(MERCURE_JWT_SECRET)%'
publish: ['notif/unreaded/{user}', 'notif/mailbox/unreaded/{buzon}', 'app/chatroom', '/.well-known/mercure/subscriptions/{topic}/{subscriber}']
subscribe: ['/.well-known/mercure/subscriptions/{topic}/{subscriber}']
The following controller renders in the base template the variables needed to generate the EventSource object (declared topics):
/**
*
* @param Request $request
* @param UuidEncoder $uuidEncoder
* @param UserInterface $user
* @param LoggerInterface $logger
* @return Response
*/
public function mercureTopicsAction(Request $request, UuidEncoder $uuidEncoder, UserInterface $user, LoggerInterface $logger): Response
{
try {
$incluirMessageChatTopic = $request->get('includeMessageChatTopic', false);
$userIdPublico = $uuidEncoder->encode($user->getIdPublico());
$buzonInternoId = $uuidEncoder->encode($user->getBuzonInterno()->getIdPublico());
/**
* Se establecen desde el controller los 3 topic de notificaciones existenes.
* * */
$notifUnreadedTopic = sprintf("notif/unreaded/%s", $userIdPublico);
$notifMailboxTopic = sprintf("notif/mailbox/unreaded/%s", $buzonInternoId);
$messagesChatroomTopic = "app/chatroom";
$subscriptionsTopic = sprintf("/.well-known/mercure/subscriptions%s/%s", "?topic=" . $messagesChatroomTopic, $user->getIdPublico());
} catch (\Exception $exc) {
$logger->error(sprintf("[%s:%s]: %s", self::class, __FUNCTION__, $exc->getMessage()));
return $this->render('App\_topics.html.twig', ['topics' => [], 'subscriptionsTopic' => "", 'username' => "nobody", 'hubServer' => ""]);
}
$topics = [];
$topics[] = $notifUnreadedTopic;
$topics[] = $notifMailboxTopic;
$topics[] = $messagesChatroomTopic;
$topics[] = $subscriptionsTopic;
return $this->render('App\_topics.html.twig', ['topics' => $topics, 'subscriptionsTopic' => $subscriptionsTopic, 'username' => $user->getUserIdentifier(), 'hubServer' => $this->getParameter('app.MERCURE_URL')]);
}
The view fragment (_topics.html.twig) generated by the controller above:
{% set config = {'mercureHub':mercure(topics, { subscribe:topics}), 'subscriptionsTopic':subscriptionsTopic, 'username':username, 'hubServer':hubServer} %}
<script type="application/json" id="mercure">
{{ config|json_encode(constant('JSON_UNESCAPED_SLASHES') b-or constant('JSON_HEX_TAG'))|raw }}
</script>
The javascript code:
const {mercureHub, subscriptionsTopic, username, hubServer} = $.parseJSON($('#mercure').text());
let mercureHubURL = mercureHub;
let eventSource;
(async () => {
const resp = await fetch(new URL(subscriptionsTopic, hubServer), {
credentials: "include",
});
const subscriptionCollection = await resp.json();
console.log(subscriptionCollection);
mercureHubURL = mercureHubURL + '&lastEventID=' + subscriptionCollection.lastEventID;
eventSource = new ReconnectingEventSource(mercureHubURL, {withCredentials: true, max_retry_time: 60000});
eventSource.onopen = function () {
console.log('Conexion establecida con el socket!');
};
// eventSource.onmessage = function () {
// console.log('Unknow message!!!!');
// };
eventSource.onerror = function () {
console.log('Error de conexion al socket!');
};
eventSource.addEventListener("notif", function (e) {
if (e.type === 'notif') {
let parsedData = null;
try {
parsedData = $.parseJSON(e.data);
} catch (error) {
console.log(error);
}
if (parsedData) {
$('#totalNotificacionesNoAtendidas').html(parsedData.total);
let list = $('#lista-notificaciones');
list.html('');
$.each(parsedData.notificaciones, function (i, e) {
list.append('<li><div class="row"><div class="col-md-12 text-sm text-left ' + e.iconoColor + '"> <i class="' + e.icono + '"></i> ' + e.asunto + '</div></div><div class="row"><div class="col-md-12 text-right text-sm"> <a class="link-muted lnkCursorPuntero lnkModalDetallesNotificacion" data-src="' + Routing.generate('panel_notificaciones_detalle', {'id': e.id}) + '"><i class="fa fa-eye"></i> detalles</a> </div></div></li><li role="separator" class="divider"></li>');
});
}
} else {
console.log('Incoming unknown message');
}
}, false);
eventSource.addEventListener("mailbox", function (e) {
if (e.type === 'mailbox') {
let parsedData = null;
try {
parsedData = $.parseJSON(e.data);
} catch (error) {
console.log(error);
}
if (parsedData) {
$('.totalMensajesNoLeidosBuzonInternoRecibidos').html(parsedData.total);
let list = $('#lista-notificaciones-buzon');
list.html('');
$.each(parsedData.mensajes, function (i, e) {
list.append('<li><a href="' + Routing.generate('buzonInterno_detalleMensaje', {'id': e.id}) + '"><div class="pull-left"><img class="img-circle" src="' + e.foto + '" alt="User image"/></div><h4>' + e.remitente + '</h4><p class="text-truncate">' + e.asunto + '</p></a></li>');
});
if (parsedData.mostrarMensajeRecibido === true) {
toastr.info("Tiene un nuevo mensaje privado", 'Mensaje recibido', {
timeOut: 8000,
preventDuplicates: false,
positionClass: 'toast-top-right',
progressBar: true,
showDuration: 800,
hideDuration: 400,
extendedTimeOut: 1000,
showEasing: "swing",
hideEasing: "linear",
showMethod: "fadeIn",
hideMethod: "fadeOut",
});
}
}
} else {
console.log('Incoming unknown message');
}
}, false);
eventSource.addEventListener("chatroom", function (e) {
if (e.type === 'chatroom') {
let msg = null;
try {
msg = $.parseJSON(e.data);
} catch (error) {
console.log(error);
}
if (msg !== null) {
let chatConversationElement = $('#chat-conversations');
let sender = chatConversationElement.data('sender');
let classOnline = sender === msg.sender ? 'online' : '';
chatConversationElement.append('<div class="item" id="' + msg.idMensaje + '" data-sender="' + msg.sender + '"><img src="' + msg.foto + '" alt="user image" class="' + classOnline + '"><p class="message"><a class="name lnkCursorPuntero"><small class="text-muted pull-right"><i class="fa fa-clock-o"></i> ' + msg.time + '</small>' + msg.sender + '</a>' + msg.texto + '</p></div>');
chatConversationElement.slimScroll({scrollBy: '400px'});
}
} else {
console.log('Incoming unknown message');
}
}, false);
eventSource.addEventListener("Subscription", function (e) {
if (e.type === 'Subscription') {
let msg = null;
try {
msg = JSON.parse(e.data);
} catch (error) {
console.log(error);
}
console.log("Nueva suscripcion !!!");
} else {
console.log('Incoming unknown message');
}
}, false);
})();
The chat messaging works ok, but the subscription event is not fired. The other important thing, how include a username into a payload array of a JWS if I use Cookie Authorization mechanism?
I don't understand that part of the Mercure configuration.