{"id":24870996,"url":"https://github.com/jsexpertcoder/reverb-rn-chat","last_synced_at":"2025-10-15T14:31:47.610Z","repository":{"id":245907177,"uuid":"819523593","full_name":"JsExpertCoder/reverb-rn-chat","owner":"JsExpertCoder","description":"Documentation about a real time chat divided in two repo.","archived":false,"fork":false,"pushed_at":"2024-12-18T18:28:04.000Z","size":9,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-08T07:42:50.287Z","etag":null,"topics":["chat","laravel","react-native","real-time","reverb"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/JsExpertCoder.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-06-24T17:16:51.000Z","updated_at":"2024-12-18T18:28:08.000Z","dependencies_parsed_at":"2024-06-24T19:28:26.154Z","dependency_job_id":null,"html_url":"https://github.com/JsExpertCoder/reverb-rn-chat","commit_stats":null,"previous_names":["jsexpertcoder/reverb-rn-chat"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/JsExpertCoder/reverb-rn-chat","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JsExpertCoder%2Freverb-rn-chat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JsExpertCoder%2Freverb-rn-chat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JsExpertCoder%2Freverb-rn-chat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JsExpertCoder%2Freverb-rn-chat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JsExpertCoder","download_url":"https://codeload.github.com/JsExpertCoder/reverb-rn-chat/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JsExpertCoder%2Freverb-rn-chat/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279085462,"owners_count":26100017,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-15T02:00:07.814Z","response_time":56,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chat","laravel","react-native","real-time","reverb"],"created_at":"2025-02-01T04:19:00.900Z","updated_at":"2025-10-15T14:31:47.374Z","avatar_url":"https://github.com/JsExpertCoder.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Let´s explore how to use Laravel Reverb to establish a WebSocket connection between your Laravel backend and a React Native application built with Expo. I think I covered the necessary steps to set up Laravel Reverb, configure the WebSocket connection, subscribe to channels using Laravel Echo and chat with others!\n\n## 1 - [Laravel (Backend)](https://github.com/JsExpertCoder/lara-chat)\n\n#### 1.1 - Create a new laravel project\n\n[Instructions in the official documentation](https://www.laravel.com/docs/11.x/installation)\n\n#### 1.2 - Install Broadcasting Reverb\n\n[Instructions in the official documentation](https://www.laravel.com/docs/11.x/reverb)\n\n#### 1.3 - Install Sanctum for API communication\n\n[Instructions in the official documentation](https://www.laravel.com/docs/11.x/sanctum#main-content)\n\n#### 1.4 - Additional configurations\n\n[Based in \"cors-and-cookies\" in the docs](https://www.laravel.com/docs/11.x/sanctum#cors-and-cookies)\n\n```\nphp artisan config:publish cors\n```\n\n##### config/cors.php\n\n```\n\u003c?php\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | Cross-Origin Resource Sharing (CORS) Configuration\n    |--------------------------------------------------------------------------\n    |\n    | Here you may configure your settings for cross-origin resource sharing\n    | or \"CORS\". This determines what cross-origin operations may execute\n    | in web browsers. You are free to adjust these settings as needed.\n    |\n    | To learn more: developer.mozilla.org/en-US/docs/Web/HTTP/CORS\n    |\n    */\n\n    'paths' =\u003e ['api/*', 'sanctum/csrf-cookie'],\n\n    'allowed_methods' =\u003e ['*'],\n\n    'allowed_origins' =\u003e ['*'],\n\n    'allowed_origins_patterns' =\u003e [],\n\n    'allowed_headers' =\u003e ['*'],\n\n    'exposed_headers' =\u003e [],\n\n    'max_age' =\u003e 0,\n\n    'supports_credentials' =\u003e true,\n\n];\n```\n\n##### resources/js/bootstrap.js\n\n```\nimport axios from \"axios\";\nwindow.axios = axios;\n\nwindow.axios.defaults.headers.common[\"X-Requested-With\"] = \"XMLHttpRequest\";\n\n// Enable credentials and XSRF token handling\nwindow.axios.defaults.withCredentials = true;\nwindow.axios.defaults.withXSRFToken = true;\n\n/**\n * Echo exposes an expressive API for subscribing to channels and listening\n * for events that are broadcast by Laravel. Echo and event broadcasting\n * allow your team to quickly build robust real-time web applications.\n */\n\nimport \"./echo\";\n```\n\n##### bootstrap/app.php\n\n[Based in \"authorizing-private-broadcast-channels\" in the docs](https://www.laravel.com/docs/11.x/sanctum#authorizing-private-broadcast-channels)\n\n```\n\u003c?php\n\nuse Illuminate\\Foundation\\Application;\nuse Illuminate\\Foundation\\Configuration\\Exceptions;\nuse Illuminate\\Foundation\\Configuration\\Middleware;\n\nreturn Application::configure(basePath: dirname(__DIR__))\n    -\u003ewithRouting(\n        web: __DIR__ . '/../routes/web.php',\n        api: __DIR__ . '/../routes/api.php',\n        commands: __DIR__ . '/../routes/console.php',\n        // channels: __DIR__.'/../routes/channels.php',\n        health: '/up',\n    )\n    -\u003ewithBroadcasting(\n        __DIR__ . '/../routes/channels.php',\n        ['prefix' =\u003e 'api', 'middleware' =\u003e ['api', 'auth:sanctum']],\n    )\n    -\u003ewithMiddleware(function (Middleware $middleware) {\n        //\n    })\n    -\u003ewithExceptions(function (Exceptions $exceptions) {\n        //\n    })-\u003ecreate();\n```\n\n#### 1.5 - Setup a Channel\n\n##### routes/channels.php\n\n```\n\u003c?php\n\nuse Illuminate\\Support\\Facades\\Broadcast;\n\nuse App\\Models\\User;\n\nBroadcast::channel('chat.{receiverId}', function (User $user, int $receiverId) {\n    return (int) $user-\u003eid === (int) $receiverId;\n});\n```\n\n#### 1.6 - \"chat_messages\" table migration\n\n```\n\u003c?php\n\nuse Illuminate\\Database\\Migrations\\Migration;\nuse Illuminate\\Database\\Schema\\Blueprint;\nuse Illuminate\\Support\\Facades\\Schema;\n\nreturn new class extends Migration\n{\n    /**\n     * Run the migrations.\n     */\n    public function up(): void\n    {\n        Schema::create('chat_messages', function (Blueprint $table) {\n            $table-\u003eid();\n            $table-\u003eforeignId('user_id')-\u003econstrained();\n            $table-\u003einteger('from');\n            $table-\u003etext('message');\n            $table-\u003etimestamps();\n        });\n    }\n\n    /**\n     * Reverse the migrations.\n     */\n    public function down(): void\n    {\n        Schema::dropIfExists('chat_messages');\n    }\n};\n\n```\n\n### 1.7 Database seeder\n\n#### Database/seeders/DatabaseSeeder.php\n\n```\n\u003c?php\n\nnamespace database/Seeders/DatabaseSeeder.php;\n\nuse App\\Models\\User;\n// use Illuminate\\Database\\Console\\Seeds\\WithoutModelEvents;\nuse Illuminate\\Support\\Str;\nuse Illuminate\\Database\\Seeder;\nuse Illuminate\\Support\\Facades\\Hash;\n\nclass DatabaseSeeder extends Seeder\n{\n    /**\n     * Seed the application's database.\n     */\n    public function run(): void\n    {\n        $roles = [\n            'Software Engineer', 'DevOps', 'Database Analyst', 'Cyber Security Consultant', 'Project Manager',\n            'System Architect', 'QA Engineer', 'Product Owner', 'UI/UX Designer', 'Scrum Master'\n        ];\n        shuffle($roles);\n\n        foreach ($roles as $role) {\n            User::create([\n                'name' =\u003e fake()-\u003ename(),\n                'role' =\u003e $role,\n                'email' =\u003e fake()-\u003eunique()-\u003esafeEmail(),\n                'email_verified_at' =\u003e now(),\n                'password' =\u003e Hash::make('password'),\n                'remember_token' =\u003e Str::random(10),\n            ]);\n        }\n        // User::factory(10)-\u003ecreate();\n\n        // User::factory()-\u003ecreate([\n        //     'name' =\u003e 'Test User',\n        //     'email' =\u003e 'test@example.com',\n        // ]);\n    }\n}\n```\n\n### 1.8 - Create an Event do broadcast\n\n```\nphp artisan make:event MessageSent\n```\n\n#### app/Events/MessageSent.php\n\n```\n\u003c?php\n\nnamespace App\\Events;\n\nuse App\\Models\\User;\nuse Illuminate\\Broadcasting\\Channel;\nuse Illuminate\\Broadcasting\\InteractsWithSockets;\nuse Illuminate\\Broadcasting\\PresenceChannel;\nuse Illuminate\\Broadcasting\\PrivateChannel;\nuse Illuminate\\Contracts\\Broadcasting\\ShouldBroadcast;\nuse Illuminate\\Contracts\\Broadcasting\\ShouldBroadcastNow;\nuse Illuminate\\Foundation\\Events\\Dispatchable;\nuse Illuminate\\Queue\\SerializesModels;\n\nclass MessageSent implements ShouldBroadcastNow\n{\n    use Dispatchable, InteractsWithSockets, SerializesModels;\n\n    /**\n     * Create a new event instance.\n     */\n    public $receiver;\n    public $sender;\n    public $message;\n\n    public function __construct($receiver, $sender, $message)\n    {\n        $this-\u003ereceiver = [\n            'id' =\u003e $receiver-\u003eid,\n            'name' =\u003e $receiver-\u003ename,\n            'role' =\u003e $receiver-\u003erole,\n        ];\n\n        $this-\u003esender = [\n            'id' =\u003e $sender-\u003eid,\n            'name' =\u003e $sender-\u003ename,\n            'role' =\u003e $sender-\u003erole,\n        ];\n\n        $this-\u003emessage = $message;\n    }\n\n    /**\n     * Get the channels the event should broadcast on.\n     */\n    public function broadcastOn(): Channel\n    {\n        return new PrivateChannel('chat.' . $this-\u003ereceiver['id']);\n    }\n}\n```\n\n### 1.9 - Create Controllers\n\n```\nphp artisan make:Controller API/Auth/_Auth\n```\n\n```\nphp artisan make:Controller API/ChatMessageController\n```\n\n#### app/Http/Controllers/API/Auth/\\_Auth.php\n\n```\n\u003c?php\n\nnamespace App\\Http\\Controllers\\API\\Auth;\n\nuse App\\Http\\Controllers\\Controller;\n\nuse App\\Models\\User;\nuse Illuminate\\Http\\Request;\nuse Illuminate\\Support\\Facades\\Auth;\nuse Illuminate\\Support\\Facades\\Hash;\nuse Illuminate\\Validation\\ValidationException;\n\nclass _Auth extends Controller\n{\n    public function login(Request $request)\n    {\n        try {\n            $request-\u003evalidate([\n                'email' =\u003e 'required|email',\n                'password' =\u003e 'required',\n            ]);\n\n            $user = User::where('email', $request-\u003eemail)-\u003efirst();\n\n            if (!$user || !Hash::check($request-\u003epassword, $user-\u003epassword)) {\n                return response()-\u003ejson([\n                    'status' =\u003e 401,\n                    'type' =\u003e 'error',\n                    'title' =\u003e 'Feedback 👋',\n                    'message' =\u003e '❌ The provided credentials are incorrect'\n                ]);\n            }\n\n            return [\n                'status' =\u003e 200,\n                'type' =\u003e 'success',\n                'title' =\u003e 'Feedback 👋',\n                'message' =\u003e '✅ Everything is ok',\n                'data' =\u003e [\n                    'user' =\u003e $user,\n                    'token' =\u003e $user-\u003ecreateToken('login-token')-\u003eplainTextToken\n\n                ]\n            ];\n        } catch (\\Throwable $th) {\n            return response()-\u003ejson([\n                'status' =\u003e 500,\n                'type' =\u003e 'error',\n                'title' =\u003e 'Feedback 👋',\n                'message' =\u003e '❌ Internal server error'\n            ]);\n        }\n    }\n}\n```\n\n#### app/Http/Controllers/API/ChatMessageController.php (dispatch the event on \"store\" function)\n\n```\n\u003c?php\n\nnamespace App\\Http\\Controllers\\API;\n\nuse App\\Events\\MessageSent;\n\nuse App\\Http\\Controllers\\Controller;\nuse App\\Models\\ChatMessage;\nuse App\\Models\\User;\n\nuse Illuminate\\Database\\Eloquent\\Collection;\nuse Illuminate\\Http\\Request;\nuse Illuminate\\Http\\Response;\n\nclass ChatMessageController extends Controller\n{\n    public function store(Request $request): Response\n    {\n        $validatedData = $request-\u003evalidate([\n            'user_id' =\u003e 'required|exists:users,id',\n            'from' =\u003e 'required|exists:users,id',\n            'message' =\u003e 'required|string',\n        ]);\n\n        $message = ChatMessage::create($validatedData);\n        $receiver = User::find($request-\u003euser_id);\n        $sender = User::find($request-\u003efrom);\n\n        // broadcast(new MessageSent($receiver, $sender, $request-\u003emessage));\n        MessageSent::dispatch($receiver, $sender, $message);\n\n        return response([\n            'status' =\u003e 200,\n            'type' =\u003e 'success',\n            'title' =\u003e 'Feedback 👋',\n            'message' =\u003e '✅ message sent',\n            'data' =\u003e [\n                'sender' =\u003e $sender,\n                'message' =\u003e  $message,\n                'receiver' =\u003e $receiver\n            ]\n        ]);\n    }\n}\n```\n\n### 1.10 - Setup API routes (login and \"send-message\")\n\n#### routes/api.php\n\n```\n\u003c?php\n\nuse Illuminate\\Http\\Request;\nuse Illuminate\\Support\\Facades\\Route;\n\nuse App\\Http\\Controllers\\API\\Auth\\_Auth;\nuse App\\Http\\Controllers\\API\\ChatMessageController;\n\nRoute::post('login', [_Auth::class, 'login']);\n\nRoute::get('user', function (Request $request) {\n    return $request-\u003euser();\n})-\u003emiddleware('auth:sanctum');\n\nRoute::middleware(['auth:sanctum'])-\u003egroup(function () {\n\n    // Route::get('get-unread-messages', [ChatMessageController::class, 'getUnreadMessages']);\n\n    // Route::post('/get-team-members', [TeamController::class, 'index']);\n\n    Route::post('send-message', [ChatMessageController::class, 'store']);\n});\n\n```\n\n## 2 - [React Native (Frontend)](https://github.com/JsExpertCoder/rnative-chat)\n\n### 2.1 Create a new React Native app (I used Expo)\n\n[Instructions in the official documentation](docs.expo.dev/)\n\n### 2.2 Install the dependencies\n\n##### check the dependencies I used (package.json)\n\n```\n{\n  \"name\": \"YOUR APP NAME\",\n  \"version\": \"1.0.0\",\n  \"main\": \"expo-router/entry\",\n  \"scripts\": {\n    \"start\": \"expo start\",\n    \"android\": \"expo start --android\",\n    \"ios\": \"expo start --ios\",\n    \"web\": \"expo start --web\"\n  },\n  \"dependencies\": {\n    \"@react-native-community/netinfo\": \"11.3.1\",\n    \"axios\": \"^1.7.2\",\n    \"expo\": \"~51.0.11\",\n    \"expo-constants\": \"~16.0.2\",\n    \"expo-linking\": \"~6.3.1\",\n    \"expo-router\": \"~3.5.15\",\n    \"expo-status-bar\": \"~1.12.1\",\n    \"laravel-echo\": \"^1.16.1\",\n    \"pusher-js\": \"^8.4.0-rc2\",\n    \"react\": \"18.2.0\",\n    \"react-dom\": \"18.2.0\",\n    \"react-native\": \"0.74.2\",\n    \"react-native-safe-area-context\": \"4.10.1\",\n    \"react-native-screens\": \"3.31.1\",\n    \"react-native-web\": \"~0.19.10\",\n    \"socket.io-client\": \"^4.7.5\",\n    \"twrnc\": \"^4.2.0\"\n  },\n  \"devDependencies\": {\n    \"@babel/core\": \"^7.20.0\",\n    \"react-native-dotenv\": \"^3.4.11\"\n  },\n  \"private\": true\n}\n```\n\n[check this to configure react-native-dotenv](https://www.github.com/goatandsheep/react-native-dotenv)\nI just made this (babel.config.js):\n\n```\nmodule.exports = function (api) {\n  api.cache(true);\n  return {\n    presets: [\"babel-preset-expo\"],\n    plugins: [\n      [\"module:react-native-dotenv\"],\n      // {\n      // envName: \"APP_ENV\",\n      // moduleName: \"@env\",\n      // path: \".env\",\n      // blocklist: null,\n      // allowlist: null,\n      // blacklist: null, // DEPRECATED\n      // whitelist: null, // DEPRECATED\n      // safe: false,\n      // allowUndefined: true,\n      // verbose: false,\n      // },\n    ],\n  };\n};\n```\n\n### 2.3 Setup .env\n\n#### .env\n\n```\n# YOUR-SERVER-HOST:YOUR-SERVER-PORT\n# (YOU CAN GET IT WHEN YOU RUN php artisan server --host=YOUR-LOCAL-IP)\n\nBACKEND_API_URL=http://YOUR-SERVER-HOST:YOUR-SERVER-PORT/api\n\nREVERB_APP_ID= # FIND IT IN .env FILE OF YOU LARAVEL APPLICATION\nREVERB_APP_KEY=  # FIND IT IN .env FILE OF YOU LARAVEL APPLICATION\nREVERB_APP_SECRET= # FIND IT IN .env FILE OF YOU LARAVEL APPLICATION\nREVERB_HOST= # FIND IT IN .env FILE OF YOU LARAVEL APPLICATION\nREVERB_PORT= # FIND IT IN .env FILE OF YOU LARAVEL APPLICATION\nREVERB_SCHEME= # FIND IT IN .env FILE OF YOU LARAVEL APPLICATION\n```\n\n### 2.4 Setup services (API)\n\n#### services/api\n\n```\nimport axios from \"axios\";\nimport { BACKEND_API_URL } from \"@env\";\n\nconst api = axios.create({\n  baseURL: BACKEND_API_URL,\n  headers: {\n    \"Content-Type\": \"application/json\",\n  },\n});\n\nconst setBearerToken = (token) =\u003e {\n  api.defaults.headers.common[\"Authorization\"] = `Bearer ${token}`;\n};\n\nexport { api, setBearerToken };\n\n```\n\n#### services/auth\n\n```\nimport { api } from \"./api\";\n\nexport const Auth = {\n  async login(email, password) {\n    const options = {\n      method: \"POST\",\n      url: \"/login\",\n      data: {\n        email,\n        password,\n      },\n    };\n\n    try {\n      const response = await api.request(options);\n      return response.data;\n    } catch (error) {\n      throw error;\n    }\n  },\n};\n\n```\n\n### 2.5 Create Echo instance (our websocket client)\n\n#### hooks/echo\n\n```\nimport { useEffect, useState } from \"react\";\n// import axios from \"axios\";\nimport { api as axios } from \"../services/api\";\nimport Echo from \"laravel-echo\";\nimport Pusher from \"pusher-js/react-native\";\nimport { REVERB_APP_KEY, REVERB_HOST, REVERB_PORT, REVERB_SCHEME } from \"@env\";\n\nconst useEcho = () =\u003e {\n  const [echoInstance, setEchoInstance] = useState(null);\n\n  useEffect(() =\u003e {\n    //  Setup Pusher client\n    const PusherClient = new Pusher(REVERB_APP_KEY, {\n      wsHost: REVERB_HOST,\n      wsPort: REVERB_PORT ?? 80,\n      wssPort: REVERB_PORT ?? 443,\n      forceTLS: (REVERB_SCHEME ?? \"https\") === \"https\",\n      enabledTransports: [\"ws\", \"wss\"],\n      disableStats: true,\n      cluster: \"mt1\",\n      authorizer: (channel, options) =\u003e {\n        return {\n          authorize: (socketId, callback) =\u003e {\n            axios\n              .post(\"/broadcasting/auth\", {\n                socket_id: socketId,\n                channel_name: channel.name,\n              })\n              .then((response) =\u003e {\n                callback(false, response.data);\n              })\n              .catch((error) =\u003e {\n                callback(true, error);\n              });\n          },\n        };\n      },\n    });\n\n    // Create Echo instance\n    const echo = new Echo({\n      broadcaster: \"reverb\",\n      client: PusherClient,\n    });\n\n    setEchoInstance(echo);\n\n    // Cleanup on unmount\n    return () =\u003e {\n      if (echo) {\n        echo.disconnect();\n      }\n    };\n  }, []);\n\n  return echoInstance;\n};\n\nexport default useEcho;\n\n```\n\n### 2.6 Create chat component\n\n#### components/chatInterface\n\n```\n// src/components/ChatInterface.js\nimport React, { useState, useEffect } from \"react\";\nimport {\n  SafeAreaView,\n  View,\n  TextInput,\n  TouchableOpacity,\n  Text,\n  FlatList,\n  KeyboardAvoidingView,\n  Platform,\n} from \"react-native\";\nimport { style as tw } from \"twrnc\";\nimport useEcho from \"../hooks/echo\";\nimport { api } from \"../services/api\";\n\nconst ChatInterface = ({ user }) =\u003e {\n  const [messages, setMessages] = useState([]);\n  const [inputText, setInputText] = useState(\"\");\n  const [userData, setUserData] = useState(user);\n\n  const echo = useEcho();\n\n  const handleSend = async () =\u003e {\n    if (inputText.trim()) {\n      setInputText(\"\");\n      const options = {\n        method: \"POST\",\n        url: \"/send-message\",\n        data: {\n          /**I logged in with user 1 and 7.\n           * then you configure this to be dynamic\n           * (giving the user the power to choose who they want to chat with)\n           * */\n          user_id: userData.id === 1 ? 7 : 1, // receiver\n          from: userData.id,\n          message: inputText,\n        },\n      };\n\n      try {\n        const response = await api.request(options);\n        if (response.status === 200) {\n          const data = response.data.data;\n          console.log(\"response\", data);\n          setMessages((prevMessages) =\u003e [\n            ...prevMessages,\n            {\n              id: data?.message?.id,\n              from: userData.id,\n              message: data?.message?.message,\n            },\n          ]);\n        }\n      } catch (error) {\n        console.error(error);\n        // Handle error appropriately\n      }\n    }\n  };\n\n  function subscribeToChatChannel() {\n    if (echo) {\n      echo.private(`chat.${user?.id}`).listen(\"MessageSent\", (e) =\u003e {\n        console.log(\"real-time-event\", {\n          id: e.message?.id,\n          message: e.message?.message,\n          from: e.message?.from,\n        });\n        setMessages((prevMessages) =\u003e [\n          ...prevMessages,\n          {\n            id: e.message?.id,\n            message: e.message?.message,\n            from: e.message?.from,\n          },\n        ]);\n      });\n    }\n  }\n\n  useEffect(() =\u003e {\n    if (echo) {\n      console.log(\"user\", userData.id);\n      subscribeToChatChannel();\n    }\n    return () =\u003e {\n      if (echo \u0026\u0026 user) {\n        echo.leaveChannel(`chat.${user?.id}`);\n      }\n    };\n  }, [echo, user]);\n\n  const renderItem = ({ item }) =\u003e (\n    \u003cView\n      style={tw(\n        `flex flex-row  my-2`,\n        item.from == userData.id ? `justify-end` : \"justify-start\"\n      )}\n    \u003e\n      \u003cView\n        style={tw(\n          `rounded-lg p-6 max-w-3/4`,\n          item.from == userData.id ? \"bg-[#1EBEA5]\" : \"bg-[#8696a0]\"\n        )}\n      \u003e\n        \u003cText style={tw`text-white`}\u003e{item.message}\u003c/Text\u003e\n      \u003c/View\u003e\n    \u003c/View\u003e\n  );\n\n  return (\n    \u003cSafeAreaView style={tw`flex-1 bg-[#0b141a]`}\u003e\n      \u003cFlatList\n        data={messages}\n        renderItem={renderItem}\n        keyExtractor={(item) =\u003e item.id}\n        contentContainerStyle={tw`p-4`}\n      /\u003e\n      \u003cKeyboardAvoidingView\n        behavior={Platform.OS === \"ios\" ? \"padding\" : \"height\"}\n      \u003e\n        \u003cView style={tw`p-4 border border-gray-700 gap-2`}\u003e\n          \u003cView style={tw`bg-blue-500 p-6 rounded-lg`}\u003e\n            \u003cText style={tw`text-white`}\u003e{userData?.name}\u003c/Text\u003e\n          \u003c/View\u003e\n        \u003c/View\u003e\n        \u003cView style={tw`flex flex-row p-4 border-t border-gray-700 gap-2`}\u003e\n          \u003cTextInput\n            style={tw`flex-1 bg-gray-800 text-white p-6 rounded-lg`}\n            value={inputText}\n            onChangeText={setInputText}\n            placeholder=\"Type a message...\"\n            placeholderTextColor=\"gray\"\n          /\u003e\n          \u003cTouchableOpacity\n            onPress={handleSend}\n            style={tw`bg-blue-500 p-6 rounded-lg`}\n          \u003e\n            \u003cText style={tw`text-white`}\u003eSend\u003c/Text\u003e\n          \u003c/TouchableOpacity\u003e\n        \u003c/View\u003e\n      \u003c/KeyboardAvoidingView\u003e\n    \u003c/SafeAreaView\u003e\n  );\n};\n\nexport default ChatInterface;\n```\n\n### 2.7 Login users\n\n#### index.js\n\n```\nimport { StatusBar } from \"expo-status-bar\";\nimport { Text, TouchableOpacity, View } from \"react-native\";\n\nimport { style as tw } from \"twrnc\";\n\nimport ChatInterface from \"../components/chatInterface\";\nimport { useState } from \"react\";\nimport { setBearerToken } from \"../services/api\";\nimport { Auth } from \"../services/auth\";\n\nexport default function App() {\n  const [userData, setUserData] = useState(null);\n\n  async function handleLogin(userCredentials) {\n    try {\n      const response = await Auth.login(...userCredentials);\n      if (response.status == 200) {\n        const data = response.data;\n        setBearerToken(data.token);\n        setUserData(data.user);\n      }\n    } catch (error) {\n      console.error(\"Error:\", error);\n    }\n  }\n\n  return userData ? (\n    \u003cChatInterface user={userData} /\u003e\n  ) : (\n    \u003cView style={tw(`bg-gray-300 flex-1 items-center justify-center gap-16`)}\u003e\n      \u003cTouchableOpacity\n        onPress={() =\u003e\n          handleLogin([\"FIRST-USER-EMAIL-HERE\", \"FIRST-USER-PASSWORD-HERE\"])\n        }\n        style={tw`w-80 bg-blue-500 p-7 rounded-lg`}\n      \u003e\n        \u003cText style={tw`text-white`}\u003elogin 1\u003c/Text\u003e\n      \u003c/TouchableOpacity\u003e\n      \u003cTouchableOpacity\n        onPress={() =\u003e\n          handleLogin([\"SECOND-USER-EMAIL-HERE\", \"SECOND-USER-PASSWORD-HERE\"])\n        }\n        style={tw`w-80 bg-blue-500 p-7 rounded-lg`}\n      \u003e\n        \u003cText style={tw`text-white`}\u003elogin 2\u003c/Text\u003e\n      \u003c/TouchableOpacity\u003e\n    \u003c/View\u003e\n  );\n}\n```\n\n## 3 - Notes\n\n**[One of the bases was this video on YouTube](https://www.youtube.com/watch?v=xEV7ruVUEvs\u0026t=1516s)**\n\n\u003e Feel free to ask, agree or complain anything\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjsexpertcoder%2Freverb-rn-chat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjsexpertcoder%2Freverb-rn-chat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjsexpertcoder%2Freverb-rn-chat/lists"}