2020-09-10 19:59:26 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace BeyondCode\LaravelWebSockets\Channels;
|
|
|
|
|
|
2020-09-11 05:34:21 +00:00
|
|
|
use BeyondCode\LaravelWebSockets\DashboardLogger;
|
2020-09-26 19:01:58 +00:00
|
|
|
use BeyondCode\LaravelWebSockets\Events\SubscribedToChannel;
|
|
|
|
|
use BeyondCode\LaravelWebSockets\Events\UnsubscribedFromChannel;
|
2020-09-10 19:59:26 +00:00
|
|
|
use BeyondCode\LaravelWebSockets\Server\Exceptions\InvalidSignature;
|
|
|
|
|
use Ratchet\ConnectionInterface;
|
|
|
|
|
use stdClass;
|
|
|
|
|
|
|
|
|
|
class PresenceChannel extends PrivateChannel
|
|
|
|
|
{
|
|
|
|
|
/**
|
|
|
|
|
* Subscribe to the channel.
|
|
|
|
|
*
|
|
|
|
|
* @see https://pusher.com/docs/pusher_protocol#presence-channel-events
|
|
|
|
|
* @param \Ratchet\ConnectionInterface $connection
|
|
|
|
|
* @param \stdClass $payload
|
2020-09-19 11:16:26 +00:00
|
|
|
* @return bool
|
2020-09-10 19:59:26 +00:00
|
|
|
* @throws InvalidSignature
|
|
|
|
|
*/
|
2020-09-19 11:16:26 +00:00
|
|
|
public function subscribe(ConnectionInterface $connection, stdClass $payload): bool
|
2020-09-10 19:59:26 +00:00
|
|
|
{
|
2020-09-11 05:34:21 +00:00
|
|
|
$this->verifySignature($connection, $payload);
|
|
|
|
|
|
|
|
|
|
$this->saveConnection($connection);
|
2020-09-10 19:59:26 +00:00
|
|
|
|
2020-09-19 11:16:26 +00:00
|
|
|
$user = json_decode($payload->channel_data);
|
2020-09-10 19:59:26 +00:00
|
|
|
|
|
|
|
|
$this->channelManager
|
2020-09-19 11:16:26 +00:00
|
|
|
->userJoinedPresenceChannel($connection, $user, $this->getName(), $payload)
|
2020-09-19 11:16:46 +00:00
|
|
|
->then(function () use ($connection) {
|
2020-09-19 11:16:26 +00:00
|
|
|
$this->channelManager
|
|
|
|
|
->getChannelMembers($connection->app->id, $this->getName())
|
|
|
|
|
->then(function ($users) use ($connection) {
|
|
|
|
|
$hash = [];
|
2020-09-11 05:46:02 +00:00
|
|
|
|
2020-09-19 11:16:26 +00:00
|
|
|
foreach ($users as $socketId => $user) {
|
|
|
|
|
$hash[$user->user_id] = $user->user_info ?? [];
|
|
|
|
|
}
|
2020-09-11 05:46:02 +00:00
|
|
|
|
2020-09-19 11:16:26 +00:00
|
|
|
$connection->send(json_encode([
|
|
|
|
|
'event' => 'pusher_internal:subscription_succeeded',
|
|
|
|
|
'channel' => $this->getName(),
|
|
|
|
|
'data' => json_encode([
|
|
|
|
|
'presence' => [
|
|
|
|
|
'ids' => collect($users)->map(function ($user) {
|
|
|
|
|
return (string) $user->user_id;
|
|
|
|
|
})->values(),
|
|
|
|
|
'hash' => $hash,
|
|
|
|
|
'count' => count($users),
|
|
|
|
|
],
|
|
|
|
|
]),
|
|
|
|
|
]));
|
|
|
|
|
});
|
|
|
|
|
})
|
|
|
|
|
->then(function () use ($connection, $user, $payload) {
|
|
|
|
|
// The `pusher_internal:member_added` event is triggered when a user joins a channel.
|
|
|
|
|
// It's quite possible that a user can have multiple connections to the same channel
|
|
|
|
|
// (for example by having multiple browser tabs open)
|
|
|
|
|
// and in this case the events will only be triggered when the first tab is opened.
|
|
|
|
|
$this->channelManager
|
|
|
|
|
->getMemberSockets($user->user_id, $connection->app->id, $this->getName())
|
2020-09-26 19:01:58 +00:00
|
|
|
->then(function ($sockets) use ($payload, $connection, $user) {
|
2020-09-19 11:16:26 +00:00
|
|
|
if (count($sockets) === 1) {
|
|
|
|
|
$memberAddedPayload = [
|
|
|
|
|
'event' => 'pusher_internal:member_added',
|
|
|
|
|
'channel' => $this->getName(),
|
|
|
|
|
'data' => $payload->channel_data,
|
|
|
|
|
];
|
2020-09-10 19:59:26 +00:00
|
|
|
|
2020-09-19 11:16:26 +00:00
|
|
|
$this->broadcastToEveryoneExcept(
|
|
|
|
|
(object) $memberAddedPayload, $connection->socketId,
|
|
|
|
|
$connection->app->id
|
|
|
|
|
);
|
2020-09-26 19:01:58 +00:00
|
|
|
|
|
|
|
|
SubscribedToChannel::dispatch(
|
|
|
|
|
$connection->app->id,
|
|
|
|
|
$connection->socketId,
|
|
|
|
|
$this->getName(),
|
|
|
|
|
$user
|
|
|
|
|
);
|
2020-09-19 11:16:26 +00:00
|
|
|
}
|
2020-09-11 05:34:21 +00:00
|
|
|
|
2020-09-19 11:16:26 +00:00
|
|
|
DashboardLogger::log($connection->app->id, DashboardLogger::TYPE_SUBSCRIBED, [
|
|
|
|
|
'socketId' => $connection->socketId,
|
|
|
|
|
'channel' => $this->getName(),
|
|
|
|
|
'duplicate-connection' => count($sockets) > 1,
|
|
|
|
|
]);
|
|
|
|
|
});
|
2020-09-16 08:09:14 +00:00
|
|
|
});
|
2020-09-19 11:16:26 +00:00
|
|
|
|
|
|
|
|
return true;
|
2020-09-10 19:59:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Unsubscribe connection from the channel.
|
|
|
|
|
*
|
|
|
|
|
* @param \Ratchet\ConnectionInterface $connection
|
2020-09-19 11:16:26 +00:00
|
|
|
* @return bool
|
2020-09-10 19:59:26 +00:00
|
|
|
*/
|
2020-09-19 11:16:26 +00:00
|
|
|
public function unsubscribe(ConnectionInterface $connection): bool
|
2020-09-10 19:59:26 +00:00
|
|
|
{
|
2020-09-19 11:16:26 +00:00
|
|
|
$truth = parent::unsubscribe($connection);
|
2020-09-10 19:59:26 +00:00
|
|
|
|
|
|
|
|
$this->channelManager
|
|
|
|
|
->getChannelMember($connection, $this->getName())
|
2020-09-19 11:16:26 +00:00
|
|
|
->then(function ($user) {
|
|
|
|
|
return @json_decode($user);
|
|
|
|
|
})
|
2020-09-10 19:59:26 +00:00
|
|
|
->then(function ($user) use ($connection) {
|
|
|
|
|
if (! $user) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-16 08:02:58 +00:00
|
|
|
$this->channelManager
|
2020-09-19 11:16:26 +00:00
|
|
|
->userLeftPresenceChannel($connection, $user, $this->getName())
|
|
|
|
|
->then(function () use ($connection, $user) {
|
|
|
|
|
// The `pusher_internal:member_removed` is triggered when a user leaves a channel.
|
|
|
|
|
// It's quite possible that a user can have multiple connections to the same channel
|
|
|
|
|
// (for example by having multiple browser tabs open)
|
|
|
|
|
// and in this case the events will only be triggered when the last one is closed.
|
|
|
|
|
$this->channelManager
|
|
|
|
|
->getMemberSockets($user->user_id, $connection->app->id, $this->getName())
|
|
|
|
|
->then(function ($sockets) use ($connection, $user) {
|
|
|
|
|
if (count($sockets) === 0) {
|
|
|
|
|
$memberRemovedPayload = [
|
|
|
|
|
'event' => 'pusher_internal:member_removed',
|
|
|
|
|
'channel' => $this->getName(),
|
|
|
|
|
'data' => json_encode([
|
|
|
|
|
'user_id' => $user->user_id,
|
|
|
|
|
]),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$this->broadcastToEveryoneExcept(
|
|
|
|
|
(object) $memberRemovedPayload, $connection->socketId,
|
|
|
|
|
$connection->app->id
|
|
|
|
|
);
|
2020-09-26 19:01:58 +00:00
|
|
|
|
|
|
|
|
UnsubscribedFromChannel::dispatch(
|
|
|
|
|
$connection->app->id,
|
|
|
|
|
$connection->socketId,
|
|
|
|
|
$this->getName(),
|
|
|
|
|
$user
|
|
|
|
|
);
|
2020-09-19 11:16:26 +00:00
|
|
|
}
|
|
|
|
|
});
|
2020-09-16 08:03:21 +00:00
|
|
|
});
|
2020-09-10 19:59:26 +00:00
|
|
|
});
|
2020-09-19 11:16:26 +00:00
|
|
|
|
|
|
|
|
return $truth;
|
2020-09-10 19:59:26 +00:00
|
|
|
}
|
|
|
|
|
}
|