generated from jonasfroeller/gh-pages-md-auto-publish
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
f1bb706
commit afc4f61
Showing
3 changed files
with
192 additions
and
51 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,14 +2,17 @@ | |
|
||
namespace App\Livewire; | ||
|
||
# waiting for v3 release (Declaration of WebSocket\Client::setLogger(Psr\Log\LoggerInterface $logger): WebSocket\Client must be compatible with Psr\Log\LoggerAwareInterface::setLogger(Psr\Log\LoggerInterface $logger): void) | ||
# waiting for v3 release (Declaration of WebSocket\Client::setLogger(Psr\Log\LoggerInterface $logger): WebSocket\Client must be compatible with Psr\Log\LoggerAwareInterface::setLogger(Psr\Log\LoggerInterface $logger): void) | ||
|
||
// TODO: implement joining, only if user clicks chat in ui, to reduce server load and loading time | ||
|
||
use Livewire\Component; | ||
use Illuminate\Support\Facades\Http; | ||
use WebSocket\Client as WebSocketClient; | ||
use WebSocket\Middleware as WebSocketMiddleware; | ||
use WebSocket\Connection as WebSocketConnection; | ||
use WebSocket\Message\Message as WebSocketMessage; | ||
use InvalidArgumentException; | ||
|
||
class ChatDTO | ||
{ | ||
|
@@ -22,75 +25,214 @@ class ChatDTO | |
public $created_at = ''; | ||
public $updated_at = ''; | ||
public $project_url = ''; | ||
|
||
public function __construct(array $data) | ||
{ | ||
foreach ($data as $key => $value) { | ||
$this->{$key} = $value; | ||
} | ||
} | ||
} | ||
|
||
class ChatMessageDTO | ||
{ | ||
public String $id = ''; | ||
public String $email = ''; | ||
public String $timestamp = ''; | ||
public String $text = ''; | ||
|
||
public function __construct(String $json) | ||
{ | ||
$data = json_decode($json, true); | ||
|
||
foreach ($data as $key => $value) { | ||
$this->{$key} = $value; | ||
} | ||
} | ||
} | ||
|
||
class ChatConnectionsDTO | ||
{ | ||
/** | ||
* @var array<String, array { | ||
* 'info' => `array { | ||
* 'detail' => ChatDTO, | ||
* 'messages' => ChatMessageDTO[] | ||
* }, | ||
* 'websocket' => WebSocketClient, | ||
* }> | ||
*/ | ||
private array $chats; | ||
|
||
public function setChats(array $chats, Bool $settingMessages = false, Bool $settingWebSocket = false): self | ||
{ | ||
foreach ($chats as $chat) { | ||
if (!isset($chat['info']['detail']) || !$chat['info']['detail'] instanceof ChatDTO) { | ||
throw new InvalidArgumentException('Invalid chat detail'); | ||
} | ||
if ($settingMessages) { | ||
if (!isset($chat['info']['messages']) || !is_array($chat['info']['messages'])) { | ||
throw new InvalidArgumentException('Invalid chat messages'); | ||
} | ||
|
||
foreach ($chat['info']['messages'] as $message) { | ||
if (!$message instanceof ChatMessageDTO) { | ||
throw new InvalidArgumentException('Invalid chat message'); | ||
} | ||
} | ||
} | ||
if ($settingWebSocket) { | ||
if (!isset($chat['websocket']) || !$chat['websocket'] instanceof WebSocketClient) { | ||
throw new InvalidArgumentException('Invalid websocket client'); | ||
} | ||
} | ||
} | ||
|
||
$this->chats = $chats; | ||
return $this; | ||
} | ||
|
||
public function getChats(): array | ||
{ | ||
return $this->chats; | ||
} | ||
} | ||
|
||
class ChatClient extends Component | ||
{ | ||
public WebSocketMessage $message; | ||
public String $monitorId = '$2y$12$W3pHWdAtePn1wjCm4.t4xO9lY9jOcu8/5SC0bDEsaAfSB8pKA5k.K'; | ||
public String $token; | ||
/** @var ChatDTO[] */ | ||
public array $chats; | ||
public String $output = ''; | ||
private WebSocketClient $websocket; | ||
public String $email = '[email protected]'; | ||
public String $password = 'password'; | ||
public String | null $token = null; | ||
|
||
public ?ChatConnectionsDTO $chats = null; | ||
|
||
public function mount() | ||
{ | ||
$this->chats = new ChatConnectionsDTO(); | ||
|
||
$this->login(); | ||
} | ||
|
||
public static function getChatUrl($monitorHash, $auth) | ||
{ | ||
return "ws://localhost:6969/chat/{$monitorHash}?auth={$auth}"; | ||
} | ||
|
||
public static function getMonitorOfClient(WebSocketClient $client) | ||
{ | ||
$url = $client->__toString(); | ||
$parts = parse_url($url); | ||
$path = explode('/', $parts['path']); | ||
$monitorHash = end($path); | ||
|
||
return $monitorHash; | ||
} | ||
|
||
public static function parseChats($chats) | ||
{ | ||
$chatMap = []; | ||
|
||
foreach ($chats as $chat) { | ||
$monitorHash = $chat['monitor_hash']; | ||
$chatMap[$monitorHash] = [ | ||
'info' => [ | ||
'detail' => new ChatDTO($chat), | ||
'messages' => [], | ||
], | ||
'websocket' => null, | ||
]; | ||
} | ||
|
||
return $chatMap; | ||
} | ||
|
||
public function login() | ||
{ | ||
$response = Http::post('http://localhost:6969/login', [ | ||
'email' => '[email protected]', | ||
'password' => 'password' | ||
'email' => $this->email, | ||
'password' => $this->password | ||
]); | ||
|
||
$json = $response->json(); | ||
$this->token = $json['token']; | ||
$this->chats = $json['chats']; | ||
if ($response && $response->ok()) { | ||
$json = $response->json(); | ||
if (isset($json['token']) && isset($json['chats'])) { | ||
$json = $response->json(); | ||
$this->token = $json['token']; | ||
$chats = $json['chats']; | ||
|
||
$chatMap = self::parseChats($chats); | ||
|
||
$this->connectToWebSocket(); | ||
$this->chats->setChats($chatMap); | ||
dump($this->chats); | ||
|
||
$this->connectToWebSockets(); | ||
} else { | ||
echo 'Error (invalid response body): ' . $response->body(); | ||
} | ||
} else { | ||
echo 'Error (response is not ok): ' . $response->body(); | ||
} | ||
} | ||
|
||
public function connectToWebSocket() | ||
public function connectToWebSockets() | ||
{ | ||
$monitorId = rawurlencode($this->monitorId); | ||
$wsUri = "ws://localhost:6969/chat/{$monitorId}?auth={$this->token}"; | ||
|
||
$this->websocket = new WebSocketClient($wsUri); | ||
|
||
$this->websocket // TODO: implement ping - ping every 2 or 3 seconds | ||
->addMiddleware(new WebSocketMiddleware\CloseHandler()) | ||
->onText(function (WebSocketClient $client, WebSocketConnection $connection, WebSocketMessage $message) { | ||
$this->output .= "<span class='text-right'>{$message->getContent()}</span>"; | ||
$this->emit('messageReceived', $message->getContent()); | ||
}) | ||
->onConnect(function (WebSocketClient $client, WebSocketConnection $connection) { | ||
$this->output .= "<span class='sticky top-0 left-0 px-2 font-black bg-gray-300 text-lime-900'>CONNECTED</span>"; | ||
}) | ||
->onClose(function (WebSocketClient $client, WebSocketConnection $connection) { | ||
$this->output .= "<span class='sticky top-0 left-0 px-2 font-black text-red-800 bg-gray-300'>CLOSED</span>"; | ||
}) | ||
->onError(function (WebSocketClient $client, WebSocketConnection $connection) { | ||
$this->output .= "<span class='sticky top-0 left-0 px-2 font-black text-red-800 bg-gray-300'>ERROR</span>"; | ||
}) | ||
->start(); | ||
foreach ($this->chats as $chat) { | ||
$monitorId = rawurlencode($chat['info']['detail']->monitor_hash); | ||
$wsUri = self::getChatUrl($monitorId, $this->token); | ||
|
||
$this->chats[$monitorId]['websocket'] = new WebSocketClient($wsUri); | ||
|
||
$this->chats[$monitorId]['websocket'] // TODO: implement ping - ping every 2 or 3 seconds | ||
->addMiddleware(new WebSocketMiddleware\CloseHandler()) | ||
->onText(function (WebSocketClient $client, WebSocketConnection $connection, WebSocketMessage $message) { | ||
$monitorId = self::getMonitorOfClient($client); | ||
$this->chats[$monitorId]['info']['messages']->array_push(new ChatMessageDTO($message->getContent())); | ||
|
||
$messageAsHTML = "<span class='text-right'>{$message->getContent()}</span>"; | ||
$this->dispatch('messageReceived', [ | ||
'monitorId' => $monitorId, | ||
'message' => $messageAsHTML | ||
]); | ||
}) | ||
->onConnect(function (WebSocketClient $client, WebSocketConnection $connection) { | ||
$messageAsHTML = "<span class='sticky top-0 left-0 px-2 font-black bg-gray-300 text-lime-900'>CONNECTED</span>"; | ||
$this->dispatch('messageReceived', $messageAsHTML); | ||
}) | ||
->onClose(function (WebSocketClient $client, WebSocketConnection $connection) { | ||
$messageAsHTML = "<span class='sticky top-0 left-0 px-2 font-black text-red-800 bg-gray-300'>CLOSED</span>"; | ||
$this->dispatch('messageReceived', $messageAsHTML); | ||
}) | ||
->onError(function (WebSocketClient $client, WebSocketConnection | null $connection) { | ||
$messageAsHTML = "<span class='sticky top-0 left-0 px-2 font-black text-red-800 bg-gray-300'>ERROR</span>"; | ||
$this->dispatch('messageReceived', $messageAsHTML); | ||
}) | ||
->start(); | ||
} | ||
} | ||
|
||
public function sendMessage($message) | ||
public function sendMessage(String $monitorId, String $message) | ||
{ | ||
if ($this->websocket && $message) { | ||
$this->output .= "<span class='text-left'>{$message}</span>"; | ||
$this->websocket->text($message); | ||
$this->message = ''; | ||
if ( | ||
!isset($this->chats[$monitorId]['websocket']) || | ||
!$this->chats[$monitorId]['websocket'] instanceof WebSocketClient || | ||
!$this->chats[$monitorId]['websocket']->isConnected() | ||
) { | ||
return; | ||
} | ||
|
||
$this->chats[$monitorId]['websocket']->text($message); | ||
|
||
$messageAsHTML = "<span class='text-right'>{$message}</span>"; | ||
$this->dispatch('messageSent', [ | ||
'monitorId' => $monitorId, | ||
'message' => $messageAsHTML | ||
]); | ||
} | ||
|
||
public function closeConnection() | ||
public function closeConnection(String $monitorId) | ||
{ | ||
if ($this->websocket) { | ||
$this->websocket->close(); | ||
if (isset($this->chats[$monitorId]['websocket']) && $this->chats[$monitorId]['websocket'] instanceof WebSocketClient) { | ||
$this->chats[$monitorId]['websocket']->close(); | ||
} | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters