├── .editorconfig ├── .env.example ├── .env.testing ├── .env.testing.example ├── .gitattributes ├── .gitignore ├── README.md ├── app ├── Events │ ├── ChatCreated.php │ └── ChatUpdated.php ├── Http │ └── Controllers │ │ └── Controller.php ├── Livewire │ ├── Actions │ │ └── Logout.php │ ├── Chats │ │ ├── Index.php │ │ ├── ListChats.php │ │ ├── Save.php │ │ └── Show.php │ ├── Forms │ │ └── LoginForm.php │ ├── Layout │ │ └── Navigation.php │ ├── Pages │ │ ├── Auth │ │ │ ├── ConfirmPassword.php │ │ │ ├── ForgotPassword.php │ │ │ ├── Login.php │ │ │ ├── Register.php │ │ │ └── ResetPassword.php │ │ ├── Chats.php │ │ ├── Dashboard.php │ │ └── Profile.php │ ├── Profile │ │ ├── DeleteUserForm.php │ │ ├── UpdatePasswordForm.php │ │ └── UpdateProfileInformationForm.php │ ├── Rooms │ │ ├── Create.php │ │ └── Index.php │ └── Welcome │ │ └── Navigation.php ├── Models │ ├── Chat.php │ ├── Member.php │ ├── Room.php │ └── User.php └── Providers │ └── AppServiceProvider.php ├── artisan ├── bootstrap ├── app.php ├── cache │ └── .gitignore └── providers.php ├── composer.json ├── composer.lock ├── config ├── app.php ├── auth.php ├── broadcasting.php ├── cache.php ├── database.php ├── filesystems.php ├── logging.php ├── mail.php ├── queue.php ├── reverb.php ├── services.php └── session.php ├── database ├── .gitignore ├── factories │ ├── ChatFactory.php │ ├── MemberFactory.php │ ├── RoomFactory.php │ └── UserFactory.php ├── migrations │ ├── 0001_01_01_000000_create_users_table.php │ ├── 0001_01_01_000001_create_cache_table.php │ ├── 0001_01_01_000002_create_jobs_table.php │ ├── 2024_09_01_114347_create_rooms_table.php │ ├── 2024_09_01_122615_create_members_table.php │ ├── 2024_09_01_132337_create_chats_table.php │ └── 2025_04_29_155057_add_deleted_at_column_to_chats_table.php └── seeders │ └── DatabaseSeeder.php ├── package-lock.json ├── package.json ├── phpstan.neon ├── phpunit.xml ├── pint.json ├── public ├── .htaccess ├── favicon.ico ├── index.php └── robots.txt ├── rector.php ├── resources ├── css │ └── app.css ├── js │ ├── app.js │ ├── bootstrap.js │ ├── echo.js │ ├── notification.js │ └── save-chat.js └── views │ ├── components │ ├── action-message.blade.php │ ├── application-logo.blade.php │ ├── auth-session-status.blade.php │ ├── danger-button.blade.php │ ├── dropdown-link.blade.php │ ├── dropdown.blade.php │ ├── icons │ │ ├── add.blade.php │ │ ├── check.blade.php │ │ ├── close.blade.php │ │ ├── edit.blade.php │ │ ├── info.blade.php │ │ ├── reply.blade.php │ │ ├── trash.blade.php │ │ ├── warning.blade.php │ │ └── x.blade.php │ ├── input-error.blade.php │ ├── input-label.blade.php │ ├── layouts │ │ ├── app.blade.php │ │ └── guest.blade.php │ ├── modal.blade.php │ ├── nav-link.blade.php │ ├── notifications.blade.php │ ├── primary-button.blade.php │ ├── responsive-nav-link.blade.php │ ├── secondary-button.blade.php │ ├── select-input.blade.php │ └── text-input.blade.php │ ├── livewire │ ├── .gitkeep │ ├── chats │ │ ├── index.blade.php │ │ ├── list-chats.blade.php │ │ ├── save.blade.php │ │ └── show.blade.php │ ├── layout │ │ └── navigation.blade.php │ ├── pages │ │ ├── auth │ │ │ ├── confirm-password.blade.php │ │ │ ├── forgot-password.blade.php │ │ │ ├── login.blade.php │ │ │ ├── register.blade.php │ │ │ └── reset-password.blade.php │ │ ├── chats.blade.php │ │ ├── dashboard.blade.php │ │ └── profile.blade.php │ ├── profile │ │ ├── delete-user-form.blade.php │ │ ├── update-password-form.blade.php │ │ └── update-profile-information-form.blade.php │ ├── rooms │ │ ├── create.blade.php │ │ └── index.blade.php │ └── welcome │ │ └── navigation.blade.php │ └── welcome.blade.php ├── routes ├── auth.php ├── channels.php ├── console.php └── web.php ├── storage ├── app │ ├── .gitignore │ └── public │ │ └── .gitignore ├── framework │ ├── .gitignore │ ├── cache │ │ ├── .gitignore │ │ └── data │ │ │ └── .gitignore │ ├── sessions │ │ └── .gitignore │ ├── testing │ │ └── .gitignore │ └── views │ │ └── .gitignore └── logs │ └── .gitignore ├── tests ├── Feature │ ├── Auth │ │ ├── AuthenticationTest.php │ │ ├── PasswordConfirmationTest.php │ │ ├── PasswordResetTest.php │ │ └── RegistrationTest.php │ ├── ChatsTest.php │ ├── ProfileTest.php │ └── WelcomeTest.php ├── Pest.php ├── TestCase.php └── Unit │ ├── Events │ ├── ChatCreatedTest.php │ └── ChatUpdatedTest.php │ ├── Livewire │ ├── Chats │ │ ├── IndexTest.php │ │ ├── ListChatTest.php │ │ ├── SaveTest.php │ │ └── ShowTest.php │ ├── Layout │ │ └── NavigationTest.php │ ├── Pages │ │ └── Auth │ │ │ ├── ConfirmPasswordTest.php │ │ │ ├── ForgotPasswordTest.php │ │ │ ├── LoginTest.php │ │ │ ├── RegisterTest.php │ │ │ └── ResetPasswordTest.php │ ├── Profile │ │ ├── DeleteUserFormTest.php │ │ ├── UpdatePasswordFormTest.php │ │ └── UpdateProfileInformationFormTest.php │ └── Rooms │ │ ├── CreateTest.php │ │ └── IndexTest.php │ └── Models │ ├── ChatTest.php │ ├── MemberTest.php │ ├── RoomTest.php │ └── UserTest.php └── vite.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 4 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.{yml,yaml}] 15 | indent_size = 2 16 | 17 | [docker-compose.yml] 18 | indent_size = 4 19 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | APP_NAME=Laravel 2 | APP_ENV=local 3 | APP_KEY= 4 | APP_DEBUG=true 5 | APP_TIMEZONE=UTC 6 | APP_URL=http://localhost 7 | 8 | APP_LOCALE=en 9 | APP_FALLBACK_LOCALE=en 10 | APP_FAKER_LOCALE=en_US 11 | 12 | APP_MAINTENANCE_DRIVER=file 13 | # APP_MAINTENANCE_STORE=database 14 | 15 | BCRYPT_ROUNDS=12 16 | 17 | LOG_CHANNEL=stack 18 | LOG_STACK=single 19 | LOG_DEPRECATIONS_CHANNEL=null 20 | LOG_LEVEL=debug 21 | 22 | DB_CONNECTION=sqlite 23 | # DB_HOST=127.0.0.1 24 | # DB_PORT=3306 25 | # DB_DATABASE=laravel 26 | # DB_USERNAME=root 27 | # DB_PASSWORD= 28 | 29 | SESSION_DRIVER=database 30 | SESSION_LIFETIME=120 31 | SESSION_ENCRYPT=false 32 | SESSION_PATH=/ 33 | SESSION_DOMAIN=null 34 | 35 | BROADCAST_CONNECTION=log 36 | FILESYSTEM_DISK=local 37 | QUEUE_CONNECTION=database 38 | 39 | CACHE_STORE=database 40 | CACHE_PREFIX= 41 | 42 | MEMCACHED_HOST=127.0.0.1 43 | 44 | REDIS_CLIENT=phpredis 45 | REDIS_HOST=127.0.0.1 46 | REDIS_PASSWORD=null 47 | REDIS_PORT=6379 48 | 49 | MAIL_MAILER=log 50 | MAIL_HOST=127.0.0.1 51 | MAIL_PORT=2525 52 | MAIL_USERNAME=null 53 | MAIL_PASSWORD=null 54 | MAIL_ENCRYPTION=null 55 | MAIL_FROM_ADDRESS="hello@example.com" 56 | MAIL_FROM_NAME="${APP_NAME}" 57 | 58 | AWS_ACCESS_KEY_ID= 59 | AWS_SECRET_ACCESS_KEY= 60 | AWS_DEFAULT_REGION=us-east-1 61 | AWS_BUCKET= 62 | AWS_USE_PATH_STYLE_ENDPOINT=false 63 | 64 | VITE_APP_NAME="${APP_NAME}" 65 | 66 | REVERB_APP_ID= 67 | REVERB_APP_KEY= 68 | REVERB_APP_SECRET= 69 | REVERB_HOST="localhost" 70 | REVERB_PORT=8080 71 | REVERB_SCHEME=http 72 | 73 | VITE_REVERB_APP_KEY="${REVERB_APP_KEY}" 74 | VITE_REVERB_HOST="${REVERB_HOST}" 75 | VITE_REVERB_PORT="${REVERB_PORT}" 76 | VITE_REVERB_SCHEME="${REVERB_SCHEME}" 77 | -------------------------------------------------------------------------------- /.env.testing: -------------------------------------------------------------------------------- 1 | APP_ENV=testing 2 | APP_KEY=base64:8wjSBjHE9DUzgVvzEYMYEZmK4aHSdZGINs+kQFFYrf0= 3 | APP_DEBUG=true 4 | 5 | 6 | DB_CONNECTION=sqlite 7 | DB_DATABASE=:memory: 8 | 9 | CACHE_DRIVER=array 10 | QUEUE_CONNECTION=sync 11 | SESSION_DRIVER=array 12 | 13 | 14 | MAIL_MAILER=array 15 | 16 | 17 | # 18 | 19 | 20 | BCRYPT_ROUNDS=4 21 | 22 | 23 | TELESCOPE_ENABLED=false 24 | -------------------------------------------------------------------------------- /.env.testing.example: -------------------------------------------------------------------------------- 1 | APP_ENV=testing 2 | APP_KEY= 3 | APP_DEBUG=true 4 | 5 | 6 | DB_CONNECTION=sqlite 7 | DB_DATABASE=:memory: 8 | 9 | 10 | CACHE_DRIVER=array 11 | QUEUE_CONNECTION=sync 12 | SESSION_DRIVER=array 13 | 14 | 15 | MAIL_MAILER=array 16 | 17 | 18 | # 19 | 20 | 21 | BCRYPT_ROUNDS=4 22 | 23 | 24 | TELESCOPE_ENABLED=false 25 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | 3 | *.blade.php diff=html 4 | *.css diff=css 5 | *.html diff=html 6 | *.md diff=markdown 7 | *.php diff=php 8 | 9 | /.github export-ignore 10 | CHANGELOG.md export-ignore 11 | .styleci.yml export-ignore 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.phpunit.cache 2 | /node_modules 3 | /public/build 4 | /public/hot 5 | /public/storage 6 | /storage/*.key 7 | /vendor 8 | .env 9 | .env.backup 10 | .env.production 11 | .phpactor.json 12 | .phpunit.result.cache 13 | Homestead.json 14 | Homestead.yaml 15 | auth.json 16 | npm-debug.log 17 | yarn-error.log 18 | /.fleet 19 | /.idea 20 | /.vscode 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Livewire Chat App 2 | 3 | ### It's WIP but already usable 4 | 5 | ## Getting Started 🚀 6 | 7 | These instructions will guide you through setting up the project on your local machine for development and testing. 8 | 9 | ### Prerequisites 10 | 11 | You need to have installed the following software: 12 | 13 | - PHP 8.3 14 | - Composer 2.0.8 15 | - Node 20.10.0 16 | 17 | ### Installing 18 | 19 | Follow these steps to set up a development environment: 20 | 21 | 1. **Clone the repository** 22 | 23 | ```bash 24 | git clone https://github.com/mrpunyapal/livewire-chat-app.git 25 | ``` 26 | 27 | 2. **Install dependencies** 28 | 29 | ```bash 30 | composer install 31 | ``` 32 | 33 | ```bash 34 | npm install 35 | ``` 36 | 37 | 3. **Duplicate the .env.example file and rename it to .env** 38 | 39 | ```bash 40 | cp .env.example .env 41 | ``` 42 | 43 | 4. **Generate the application key** 44 | 45 | ```bash 46 | php artisan key:generate 47 | ``` 48 | 49 | 5. **Run migration and seed** 50 | 51 | ```bash 52 | php artisan migrate --seed 53 | ``` 54 | 55 | 6. **Run the application** 56 | 57 | ```bash 58 | npm run dev 59 | ``` 60 | 61 | ```bash 62 | php artisan serve 63 | ``` 64 | 65 | ## How to Test the Application 🧪 66 | 67 | - Copy .env.testing.example to .env.testing 68 | - Run the following commands 69 | 70 | ```bash 71 | php artisan key:generate --env=testing 72 | ``` 73 | 74 | ```bash 75 | npm install && npm run build 76 | ``` 77 | 78 | ```bash 79 | # Lint the code using Pint 80 | composer lint 81 | composer test:lint 82 | 83 | # Refactor the code using Rector 84 | composer refactor 85 | composer test:refactor 86 | 87 | # Run PHPStan 88 | composer test:types 89 | 90 | # Run type coverage 91 | composer test:type-coverage 92 | 93 | # Run the test suite 94 | composer test:unit 95 | 96 | # Run all the tests 97 | composer test 98 | ``` 99 | Check [composer.json](/composer.json#L57-L71) for more details on scripts. 100 | 101 | ### Give Feedback 💬 102 | 103 | Give your feedback on [@MrPunyapal](https://x.com/MrPunyapal) 104 | 105 | ### Contribute 🤝 106 | 107 | Contribute if you have any ideas to improve this project. 108 | -------------------------------------------------------------------------------- /app/Events/ChatCreated.php: -------------------------------------------------------------------------------- 1 | 32 | */ 33 | public function broadcastOn(): array 34 | { 35 | return [ 36 | new PrivateChannel('chats.'.$this->roomId), 37 | ]; 38 | } 39 | 40 | public function broadcastAs(): string 41 | { 42 | return 'chat-created'; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/Events/ChatUpdated.php: -------------------------------------------------------------------------------- 1 | 32 | */ 33 | public function broadcastOn(): array 34 | { 35 | return [ 36 | new PrivateChannel('chats.'.$this->roomId), 37 | ]; 38 | } 39 | 40 | public function broadcastAs(): string 41 | { 42 | return 'chat-updated'; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/Http/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | logout(); 18 | 19 | Session::invalidate(); 20 | Session::regenerateToken(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/Livewire/Chats/Index.php: -------------------------------------------------------------------------------- 1 | roomId === null ? null : Room::query() 28 | ->whereRelation('users', 'users.id', auth()->id()) 29 | ->find($this->roomId); 30 | } 31 | 32 | #[On('room-selected')] 33 | public function selectRoom(int $id): void 34 | { 35 | $this->dispatch('room-closed', roomId: $this->roomId); 36 | 37 | $this->roomId = $id; 38 | } 39 | 40 | public function render(): View 41 | { 42 | return view('livewire.chats.index', [ 43 | 'room' => $this->room, 44 | ]); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/Livewire/Chats/ListChats.php: -------------------------------------------------------------------------------- 1 | 25 |
{{ $room->name }}
16 | @else 17 |18 | Please select room. 19 |
20 | @endif 21 |No chats found
16 |End of the chat
30 |
73 |
87 | {{ $chat->message }} 88 |
89 | @else 90 |91 | This message has been deleted. 92 |
93 | @endif 94 |8 | {{ __('Once your account is deleted, all of its resources and data will be permanently deleted. Before deleting your account, please download any data or information that you wish to retain.') }} 9 |
10 |8 | {{ __('Ensure your account is using a long, random password to stay secure.') }} 9 |
10 |8 | {{ __("Update your account's profile information and email address.") }} 9 |
10 |