diff --git a/app/Actions/SendMessageAction.php b/app/Actions/SendMessageAction.php index d270013..c9a8ad1 100644 --- a/app/Actions/SendMessageAction.php +++ b/app/Actions/SendMessageAction.php @@ -3,7 +3,9 @@ namespace App\Actions; use App\Events\MessageSent; +use App\Exceptions\MessageNotSendException; use App\Models\User; +use DB; final readonly class SendMessageAction { @@ -14,18 +16,30 @@ public function __construct(private CreateOrGetInboxAction $inboxAction) {} */ public function execute(User $sender, User $recipient, array $data): void { - // find the inbox between the two users - \DB::beginTransaction(); - $inbox = $this->inboxAction->execute($recipient, $sender); - $inbox->last_message = $data['message']; - $inbox->last_user_id = $sender->id; - $inbox->save(); - $message = $inbox->messages()->create([ - 'message' => $data['message'], - 'user_id' => $sender->id, - ]); + try { - broadcast(new MessageSent($message))->toOthers(); - \DB::commit(); + // find the inbox between the two users + DB::beginTransaction(); + $inbox = $this->inboxAction->execute($recipient, $sender); + + // update the inbox with the last message and last user as current user + $inbox->last_message = $data['message']; + $inbox->last_user_id = $sender->id; + $inbox->save(); + + // create a new message in the inbox + $message = $inbox->messages()->create([ + 'message' => $data['message'], + 'user_id' => $sender->id, + ]); + + // Send the message to all other users in the inbox + broadcast(new MessageSent($message))->toOthers(); + + DB::commit(); + } catch (\Throwable $e) { + DB::rollBack(); + throw new MessageNotSendException('Message not sent.', previous: $e); + } } } diff --git a/app/Exceptions/MessageNotSendException.php b/app/Exceptions/MessageNotSendException.php new file mode 100644 index 0000000..8c82fd1 --- /dev/null +++ b/app/Exceptions/MessageNotSendException.php @@ -0,0 +1,7 @@ +execute($recipient, $sender); - } catch (\Throwable $e) { - \Log::error('Inbox instantiation Failed: ', [$e->getMessage()]); + } catch (Throwable $e) { + Log::error('Inbox instantiation Failed: ', [$e->getMessage()]); abort(500); } @@ -33,7 +36,7 @@ public function show(#[CurrentUser] User $sender, User $recipient, CreateOrGetIn } /** - * @throws \Throwable + * @throws Throwable */ public function store( #[CurrentUser] User $sender, @@ -41,8 +44,16 @@ public function store( SendMessageRequest $request, SendMessageAction $action ) { - $action->execute($sender, $recipient, $request->validated()); + try { + $action->execute($sender, $recipient, $request->validated()); - response()->json(['message' => 'Message sent successfully.']); + return response()->json(['message' => 'Message sent successfully.']); + + } catch (MessageNotSendException $e) { + + Log::error('Message send failed', [$e->getMessage()]); + + return response()->json(['message' => 'Message sent failed.'], 500); + } } } diff --git a/app/Models/Message.php b/app/Models/Message.php index ad41f43..98af994 100644 --- a/app/Models/Message.php +++ b/app/Models/Message.php @@ -2,8 +2,11 @@ namespace App\Models; +use Eloquent; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Support\Carbon; /** * @property int $id @@ -14,24 +17,24 @@ * @property string|null $delivered_at * @property string|null $deleted_at * @property string|null $failed_at - * @property \Illuminate\Support\Carbon $created_at - * @property-read \App\Models\Inbox|null $inbox - * @property-read \App\Models\User|null $user + * @property Carbon $created_at + * @property-read Inbox|null $inbox + * @property-read User|null $user * - * @method static \Illuminate\Database\Eloquent\Builder|Message newModelQuery() - * @method static \Illuminate\Database\Eloquent\Builder|Message newQuery() - * @method static \Illuminate\Database\Eloquent\Builder|Message query() - * @method static \Illuminate\Database\Eloquent\Builder|Message whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|Message whereDeletedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|Message whereDeliveredAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|Message whereFailedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|Message whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Message whereInboxId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Message whereMessage($value) - * @method static \Illuminate\Database\Eloquent\Builder|Message whereReadAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|Message whereUserId($value) + * @method static Builder|Message newModelQuery() + * @method static Builder|Message newQuery() + * @method static Builder|Message query() + * @method static Builder|Message whereCreatedAt($value) + * @method static Builder|Message whereDeletedAt($value) + * @method static Builder|Message whereDeliveredAt($value) + * @method static Builder|Message whereFailedAt($value) + * @method static Builder|Message whereId($value) + * @method static Builder|Message whereInboxId($value) + * @method static Builder|Message whereMessage($value) + * @method static Builder|Message whereReadAt($value) + * @method static Builder|Message whereUserId($value) * - * @mixin \Eloquent + * @mixin Eloquent */ class Message extends Model { @@ -43,6 +46,10 @@ class Message extends Model 'created_at', 'delivered_at', ]; + protected $casts = [ + 'created_at' => 'datetime', + ]; + public function inbox(): BelongsTo { return $this->belongsTo(Inbox::class); diff --git a/resources/js/message.js b/resources/js/message.js index 4f9eda1..19940e7 100644 --- a/resources/js/message.js +++ b/resources/js/message.js @@ -1,34 +1,51 @@ +import {showToast} from "./toast.js"; + export const sendMessage = async (element, event) => { - event.preventDefault() + event.preventDefault(); const messageInput = element.querySelector('[name="message"]'); - if (!messageInput.value) return; + const message = messageInput.value; + if (!message) return; const recipentId = element.dataset.recipientId; - const message = messageInput.value; messageInput.value = ''; - addMessageToChat({message: message}, true); + // Capture the ID of the UI element we just added + const tempMessageId = addMessageToChat({message: message}, true); - const response = await axios.post(`/api/chat/${recipentId}/message`, {message: message}); + try { + await axios.post(`/api/chat/${recipentId}/message`, {message: message}); + } catch (e) { + console.error(e); + showToast('Message could not be sent.'); + + const failedMessage = document.getElementById(tempMessageId); + if (failedMessage) { + failedMessage.remove(); + } + + messageInput.value = message; + } } export const addMessageToChat = (message, right = false) => { const chatContainer = document.getElementById('chat-container'); if (!chatContainer) return; + const tempId = 'msg-' + Date.now(); + const placeholder = chatContainer.querySelector('#no-messages-placeholder'); - if (placeholder) { - placeholder.remove(); - } + if (placeholder) placeholder.remove(); const messagePlaceholder = ` -
-
+
+
${message.message}
`; chatContainer.insertAdjacentHTML('afterbegin', messagePlaceholder); - chatContainer.scrollTop = 0; + + // Return the ID so the caller can find this specific message later + return tempId; } diff --git a/resources/views/components/chat/message-box.blade.php b/resources/views/components/chat/message-box.blade.php index 6679e01..bcb08a5 100644 --- a/resources/views/components/chat/message-box.blade.php +++ b/resources/views/components/chat/message-box.blade.php @@ -1,14 +1,32 @@ @props(['recipient', 'messages' => []]) +@php + $groupedMessages = $messages->groupBy(function($msg) { + return $msg->created_at->format('M j, Y'); + }); +@endphp +
- @forelse($messages as $message) - {{$message->message}} - @empty -
-

No Messages Found!

+ @forelse($groupedMessages as $date => $dayMessages) + @foreach($dayMessages as $message) + +

{{ $message->message }}

+ + {{ $message->created_at->format('g:i A') }} + +
+ @endforeach + + {{-- The Date Divider --}} +
+ + {{ $date }} +
+ @empty +
No Messages Found !
@endforelse
diff --git a/resources/views/components/chat/message.blade.php b/resources/views/components/chat/message.blade.php index 4ba36dd..3a0c52c 100644 --- a/resources/views/components/chat/message.blade.php +++ b/resources/views/components/chat/message.blade.php @@ -1,7 +1,11 @@ @props(['right' => false])
-
- {{ $slot }} - +
+ {{$slot}}
diff --git a/resources/views/components/explore/profile-menu.blade.php b/resources/views/components/explore/profile-menu.blade.php index b563fa6..fcbf2b6 100644 --- a/resources/views/components/explore/profile-menu.blade.php +++ b/resources/views/components/explore/profile-menu.blade.php @@ -1,27 +1,30 @@ +@php use App\Enums\UserTypes; @endphp @props(['profileLink' => ''])
@auth -
- - +
@endauth @guest diff --git a/resources/views/dashboards/user/chat.blade.php b/resources/views/dashboards/user/chat.blade.php index c1358cd..7539428 100644 --- a/resources/views/dashboards/user/chat.blade.php +++ b/resources/views/dashboards/user/chat.blade.php @@ -1,10 +1,11 @@ +@php use App\Services\ProfileInitialsService; @endphp @forelse($inboxes as $inbox) @empty No chats found ! @@ -20,4 +21,5 @@
@endif
+