Skip to content

Commit

Permalink
feat:added file upload and minor improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
199ocero committed Jul 21, 2024
1 parent 2cef09c commit b6c7e86
Show file tree
Hide file tree
Showing 15 changed files with 521 additions and 72 deletions.
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ php artisan filachat:install

You can view the full content of the config file here: [config/filachat.php](https://github.com/199ocero/filachat/blob/main/config/filachat.php)

Next, execute the following command to generate assets in your public folder.

```bash
php artisan filament:assets
```

> [!NOTE]
> This step is optional if you want to enable role restrictions. You only need to create an agent if you want to set up role-based chat support.
Expand Down Expand Up @@ -112,6 +118,27 @@ Then everytime you start your application in your local environment, you will ne
php artisan reverb:start
```

When using file uploads, Livewire has a default file size limit of 12 MB. To change this limit, you need to publish the Livewire configuration file using the command `php artisan livewire:publish --config` and then adjust the `rule`.

```php
<?php

return [
//...
'temporary_file_upload' => [
'rules' => 'max:20000', // Example: ['file', 'mimes:png,jpg'] | Default: ['required', 'file', 'max:12288'] (12MB)
],
//...
];
```

You also need to adjust the `post_max_size` and `upload_max_filesize` settings in your `php.ini` file.

```ini
post_max_size = 20MB
upload_max_filesize = 20MB
```

## Testing

```bash
Expand Down
62 changes: 62 additions & 0 deletions config/filachat.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,68 @@
*/
'receiver_name_column' => 'name',

/*
|--------------------------------------------------------------------------
| Upload Files
|--------------------------------------------------------------------------
|
| This option specifies the mime types and the type of disk to be used.
|
*/
'disk' => 'public',
// this configuration is only use if the disk is S3
's3' => [
'directory' => 'attachments',
'visibility' => 'public',
],
// these are the mime types that are allowed and you can remove if you want
'mime_types' => [
// audio
'audio/m4a',
'audio/wav',
'audio/mpeg',
'audio/ogg',
'audio/aac',
'audio/flac',
'audio/midi',

// images
'image/png',
'image/jpeg',
'image/jpg',
'image/gif',

// videos
'video/mp4',
'video/avi',
'video/quicktime',
'video/webm',
'video/x-matroska',
'video/x-flv',
'video/mpeg',

// documents
'application/pdf',
'application/msword',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'text/csv',
'text/plain',
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'application/vnd.ms-powerpoint',
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
],
/*
| If you want to change the maximum file size above 12mb you need to publish
| livewire config file and change the value for rules. Example below is from livewire config file.
| 'rules' => null, // Example: ['file', 'mimes:png,jpg'] | Default: ['required', 'file', 'max:12288'] (12MB)
*/
'max_file_size' => 12288, // default livewire 12MB converted to kilobytes
'min_file_size' => 1,
// this option here is for number of files to be uploaded
'max_files' => 10,
'min_files' => 0,

/*
|--------------------------------------------------------------------------
| Route Slug
Expand Down
3 changes: 3 additions & 0 deletions resources/css/filachat.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.filachat-filepond .filepond--root {
max-height: 20rem;
}
1 change: 0 additions & 1 deletion resources/css/index.css

This file was deleted.

152 changes: 127 additions & 25 deletions resources/views/filachat/components/chat-box.blade.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
@props(['selectedConversation'])
<!-- Right Section (Chat Conversation) -->
<div class="flex flex-col w-full md:w-2/3 overflow-hidden">
<div
x-load-css="[@js(\Filament\Support\Facades\FilamentAsset::getStyleHref('filachat-styles', package: 'jaocero/filachat'))]"
class="flex flex-col w-full md:w-2/3 overflow-hidden">
@if ($selectedConversation)
<!-- Chat Header -->
<div class="flex items-center h-20 gap-2 p-5 border-b dark:border-gray-800/60 border-gray-200/90">
Expand All @@ -27,18 +29,27 @@
</div>

<!-- Chat Messages -->
<div x-data="{ markAsRead: false }" x-init="Echo.channel('filachat')
.listen('.JaOcero\\FilaChat\\Events\\FilaChatMessageReadEvent', e => {
if (e.conversationId == @js($selectedConversation->id)) {
markAsRead = true;
}
});" id="chatContainer"
<div x-data="{ markAsRead: false }" x-init="
Echo.channel('filachat')
.listen('.JaOcero\\FilaChat\\Events\\FilaChatMessageReadEvent', e => {
if (e.conversationId == @js($selectedConversation->id)) {
markAsRead = true;
}
})
.listen('.JaOcero\\FilaChat\\Events\\FilaChatMessageReceiverIsAwayEvent', e => {
if (e.conversationId == @js($selectedConversation->id)) {
markAsRead = false;
}
});
" id="chatContainer"
class="flex flex-col-reverse flex-1 p-5 overflow-y-auto">
<!-- Message Item -->
@foreach ($messages as $index => $message)
@foreach ($conversationMessages as $index => $message)
<div wire:key="{{ $message->id }}">
@php
$nextMessage = $messages[$index + 1] ?? null;
$nextMessage = $conversationMessages[$index + 1] ?? null;
$nextMessageDate = $nextMessage ? \Carbon\Carbon::parse($nextMessage->created_at)->setTimezone(config('filachat.timezone', 'app.timezone'))->format('Y-m-d') : null;
$currentMessageDate = \Carbon\Carbon::parse($message->created_at)->setTimezone(config('filachat.timezone', 'app.timezone'))->format('Y-m-d');
Expand All @@ -47,19 +58,19 @@ class="flex flex-col-reverse flex-1 p-5 overflow-y-auto">
@endphp

@if ($showDateBadge)
<div class="flex justify-center">
<div class="flex justify-center my-4">
<x-filament::badge>
{{ \Carbon\Carbon::parse($message->created_at)->setTimezone(config('filachat.timezone', 'app.timezone'))->format('F j, Y') }}
</x-filament::badge>
</div>
@endif
@if ($message->senderable_id !== auth()->user()->id)
@php
$previousMessageDate = isset($messages[$index - 1]) ? \Carbon\Carbon::parse($messages[$index - 1]->created_at)->setTimezone(config('filachat.timezone', 'app.timezone'))->format('Y-m-d') : null;
$previousMessageDate = isset($conversationMessages[$index - 1]) ? \Carbon\Carbon::parse($conversationMessages[$index - 1]->created_at)->setTimezone(config('filachat.timezone', 'app.timezone'))->format('Y-m-d') : null;
$currentMessageDate = \Carbon\Carbon::parse($message->created_at)->setTimezone(config('filachat.timezone', 'app.timezone'))->format('Y-m-d');
$previousSenderId = $messages[$index - 1]->senderable_id ?? null;
$previousSenderId = $conversationMessages[$index - 1]->senderable_id ?? null;
// Show avatar if the current message is the first in a consecutive sequence or a new day
$showAvatar = $message->senderable_id !== auth()->user()->id && ($message->senderable_id !== $previousSenderId || $currentMessageDate !== $previousMessageDate);
Expand All @@ -73,20 +84,112 @@ class="flex flex-col-reverse flex-1 p-5 overflow-y-auto">
@else
<div class="w-6 h-6"></div> <!-- Placeholder to align the messages properly -->
@endif
<div class="max-w-md p-2 bg-gray-200 rounded-lg dark:bg-gray-800">
<p class="text-sm">{{ $message->message }}</p>
<div class="max-w-md p-2 bg-gray-200 rounded-t-xl rounded-br-xl dark:bg-gray-800">
@if ($message->message)
<p class="text-sm">{{ $message->message }}</p>
@endif
@if ($message->attachments && count($message->attachments) > 0)
@foreach ($message->attachments as $attachment)
@php
$originalFileName = $this->getOriginalFileName($attachment, $message->original_attachment_file_names);
@endphp
<div wire:click="downloadFile('{{ $attachment }}', '{{ $originalFileName }}')" class="flex items-center gap-1 bg-gray-50 dark:bg-gray-700 p-2 my-2 rounded-lg group cursor-pointer">
<div class="p-2 text-white bg-gray-500 dark:bg-gray-600 rounded-full group-hover:bg-gray-700 group-hover:dark:bg-gray-800">
@php
$icon = 'heroicon-m-x-mark';
if($this->validateImage($attachment)) {
$icon = 'heroicon-m-photo';
}
if ($this->validateDocument($attachment)) {
$icon = 'heroicon-m-paper-clip';
}
if ($this->validateVideo($attachment)) {
$icon = 'heroicon-m-video-camera';
}
if ($this->validateAudio($attachment)) {
$icon = 'heroicon-m-speaker-wave';
}
@endphp
<x-filament::icon icon="{{ $icon }}" class="w-4 h-4" />
</div>
<p class="text-sm text-gray-600 dark:text-white group-hover:underline">
{{ $originalFileName }}
</p>
</div>
@endforeach
@endif
<p class="mt-1 text-xs text-gray-500 dark:text-gray-600 text-start">
{{ \Carbon\Carbon::parse($message->created_at)->setTimezone(config('filachat.timezone', 'app.timezone'))->format('F j, Y') }}
@php
$createdAt = \Carbon\Carbon::parse($message->created_at)->setTimezone(config('filachat.timezone', 'app.timezone'));
if ($createdAt->isToday()) {
$date = $createdAt->format('g:i A');
} else {
$date = $createdAt->format('M d, Y g:i A');
}
@endphp
{{ $date }}
</p>
</div>
</div>
@else
<!-- Right Side -->
<div class="flex flex-col items-end gap-2 mb-2">
<div class="max-w-md p-2 text-white rounded-lg bg-primary-600 dark:bg-primary-500">
<p class="text-sm">{{ $message->message }}</p>
<div class="max-w-md p-2 text-white rounded-t-xl rounded-bl-xl bg-primary-600 dark:bg-primary-500">
@if ($message->message)
<p class="text-sm">{{ $message->message }}</p>
@endif
@if ($message->attachments && count($message->attachments) > 0)
@foreach ($message->attachments as $attachment)
@php
$originalFileName = $this->getOriginalFileName($attachment, $message->original_attachment_file_names);
@endphp
<div wire:click="downloadFile('{{ $attachment }}', '{{ $originalFileName }}')" class="flex items-center gap-1 bg-primary-500 dark:bg-primary-800 p-2 my-2 rounded-lg group cursor-pointer">
<div class="p-2 text-white bg-primary-600 rounded-full group-hover:bg-primary-700 group-hover:dark:bg-primary-900">
@php
$icon = 'heroicon-m-x-circle';
if($this->validateImage($attachment)) {
$icon = 'heroicon-m-photo';
}
if ($this->validateDocument($attachment)) {
$icon = 'heroicon-m-paper-clip';
}
if ($this->validateVideo($attachment)) {
$icon = 'heroicon-m-video-camera';
}
if ($this->validateAudio($attachment)) {
$icon = 'heroicon-m-speaker-wave';
}
@endphp
<x-filament::icon icon="{{ $icon }}" class="w-4 h-4" />
</div>
<p class="text-sm text-white group-hover:underline">
{{ $originalFileName }}
</p>
</div>
@endforeach
@endif
<p class="text-xs text-primary-300 dark:text-primary-200 text-end">
{{ \Carbon\Carbon::parse($message->created_at)->setTimezone(config('filachat.timezone', 'app.timezone'))->format('F j, Y') }}
@php
$createdAt = \Carbon\Carbon::parse($message->created_at)->setTimezone(config('filachat.timezone', 'app.timezone'));
if ($createdAt->isToday()) {
$date = $createdAt->format('g:i A');
} else {
$date = $createdAt->format('M d, Y g:i A');
}
@endphp
{{ $date }}
</p>
</div>
<template x-if="markAsRead || @js($message->last_read_at) !== null">
Expand All @@ -111,7 +214,7 @@ class="flex flex-col-reverse flex-1 p-5 overflow-y-auto">
<!-- Repeat Message Item for multiple messages -->
@if ($this->paginator->hasMorePages())
<div x-intersect="$wire.loadMoreMessages" class="h-4">
<div class="w-full mb-2 text-center text-gray-500">Loading more messages...</div>
<div class="w-full mb-6 text-center text-gray-500">Loading more messages...</div>
</div>
@endif
</div>
Expand All @@ -120,14 +223,13 @@ class="flex flex-col-reverse flex-1 p-5 overflow-y-auto">

<!-- Chat Input -->
<div class="w-full p-4 border-t dark:border-gray-800/60 border-gray-200/90">
<form wire:submit="sendMessage" class="flex items-center justify-between w-full gap-2">
<div class="w-full">
<form wire:submit="sendMessage" class="flex items-end justify-between w-full gap-4">
<div class="w-full max-h-96 overflow-y-auto">
{{ $this->form }}
</div>

<x-filament::button type="submit">
Send
</x-filament::button>
<div class="p-1">
<x-filament::button type="submit" icon="heroicon-m-paper-airplane" class="!gap-0"></x-filament::button>
</div>
</form>

<x-filament-actions::modals />
Expand Down
6 changes: 5 additions & 1 deletion resources/views/filachat/components/chat-list.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,11 @@
</div>
<div class="flex items-center justify-between gap-1">
<p class="text-sm text-gray-600 truncate dark:text-gray-400">
{{ $conversation->latest_message }}</p>
@if($conversation->is_sender)
<span class="text-primary-600 dark:text-primary-400 font-bold">You: </span>
@endif
{{ $conversation->latest_message }}
</p>
@if ($conversation->unread_count > 0)
<x-filament::badge>
{{ $conversation->unread_count }}
Expand Down
37 changes: 37 additions & 0 deletions src/Events/FilaChatMessageReceiverIsAwayEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace JaOcero\FilaChat\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class FilaChatMessageReceiverIsAwayEvent implements ShouldBroadcastNow
{
use Dispatchable;
use InteractsWithSockets;
use SerializesModels;

public string $type = FilaChatMessageReceiverIsAwayEvent::class;

/**
* Create a new event instance.
*/
public function __construct(
public int $conversationId,
) {
//
}

/**
* Get the channels the event should broadcast on.
*
* @return array<int, \Illuminate\Broadcasting\Channel>
*/
public function broadcastOn()
{
return new Channel('filachat');
}
}
2 changes: 1 addition & 1 deletion src/FilaChatServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ protected function getAssets(): array
{
return [
// AlpineComponent::make('filachat', __DIR__ . '/../resources/dist/components/filachat.js'),
// Css::make('filachat-styles', __DIR__ . '/../resources/dist/filachat.css'),
Css::make('filachat-styles', __DIR__ . '/../resources/css/filachat.css')->loadedOnRequest(),
// Js::make('filachat-scripts', __DIR__ . '/../resources/dist/filachat.js'),
];
}
Expand Down
Loading

0 comments on commit b6c7e86

Please sign in to comment.