npx create-vite@latest client
cd client
npm install

Instalación de dependencias

npm install socket.io-client
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

Configuración de Tailwind CSS

/** @type {import('tailwindcss').Config} */
export default {
  content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
  theme: {
    extend: {},
  },
  plugins: [],
};
@tailwind base;
@tailwind components;
@tailwind utilities;

Configuración del servidor

mkdir server
cd server
npm init -y
npm install express socket.io

A continuación, crea un archivo index.js en el directorio del servidor y agrega el siguiente código:

import express from 'express';
import http from 'http';
import { Server } from 'socket.io';

const app = express();
const server = http.createServer(app);
const io = new Server(server, {
  cors: {
    origin: 'http://localhost:5173',
    methods: ['GET', 'POST'],
  },
});

io.on('connection', (socket) => {
  console.log('a user connected');

  socket.on('chat message', (msg) => {
    io.emit('chat message', msg);
  });

  socket.on('disconnect', () => {
    console.log('user disconnected');
  });
});

server.listen(3000, () => {
  console.log('listening on *:3000');
});

Inicia el servidor escribiendo node index.js en la terminal.

"scripts": {
  "start": "node index.js",
  "dev": "nodemon index.js"
}

Construyendo el frontend

npm i emoji-picker-react
/* A blank page with an emoji selector */
import Picker from 'emoji-picker-react';

function App() {
  return (
    <div>
      <Picker />
    </div>
  );
}

export default App;
import React, { useState, useRef, useEffect } from 'react';
import Picker from 'emoji-picker-react';

const EmojiPicker = ({ onEmojiClick }) => {
  const [showPicker, setShowPicker] = useState(false);
  const pickerRef = useRef(null);

  const togglePicker = () => {
    setShowPicker(!showPicker);
  };

  const handleClickOutside = (event) => {
    if (pickerRef.current && !pickerRef.current.contains(event.target)) {
      setShowPicker(false);
    }
  };

  useEffect(() => {
    document.addEventListener('mousedown', handleClickOutside);
    return () => {
      document.removeEventListener('mousedown', handleClickOutside);
    };
  }, []);

  return (
    <div className="relative" ref={pickerRef}>
      <button
        type="button"
        className="p-2 bg-gray-200 rounded-full hover:bg-gray-300 focus:outline-none"
        onClick={togglePicker}
      >
        😀
      </button>
      {showPicker && (
        <div className="absolute bottom-12 right-0 z-10">
          <Picker onEmojiClick={onEmojiClick} />
        </div>
      )}
    </div>
  );
};

export default EmojiPicker;

Configuración de los componentes del chat

import React from 'react';
import ChatMessages from './ChatMessages';
import ChatInput from './ChatInput';

const ChatBox = () => {
  return (
    <>
      <ChatMessages />
      <ChatInput />
    </>
  );
};

export default ChatBox;
import React, { useEffect, useState, useRef } from 'react';
import { io } from 'socket.io-client';

const socket = io('http://localhost:3000');

const ChatMessages = () => {
  const [messages, setMessages] = useState([]);
  const messagesEndRef = useRef(null);

  useEffect(() => {
    socket.on('chat message', (msg) => {
      setMessages((prevMessages) => [...prevMessages, msg]);
    });

    return () => {
      socket.off('chat message');
    };
  }, []);

  useEffect(() => {
    if (messagesEndRef.current) {
      messagesEndRef.current.scrollIntoView({ behavior: 'smooth' });
    }
  }, [messages]);

  return (
    <div className="h-64 overflow-y-auto mb-4 p-4 bg-gray-100 rounded-lg">
      {messages.map((msg, index) => (
        <div
          key={index}
          className={`p-2 mb-2 rounded-lg text-left ${
            index % 2 === 0 ? 'bg-blue-100' : 'bg-indigo-300'
          }`}
        >
          {msg}
        </div>
      ))}
      <div ref={messagesEndRef} />
    </div>
  );
};

export default ChatMessages;

Componente ChatBox

import React, { useState } from 'react';
import { io } from 'socket.io-client';
import EmojiPicker from './EmojiPicker';

const socket = io('http://localhost:3000');

const ChatInput = () => {
  const [message, setMessage] = useState('');

  const sendMessage = (e) => {
    e.preventDefault();
    if (message.trim()) {
      socket.emit('chat message', message);
      setMessage('');
    }
  };

  const handleEmojiClick = (emoji, event) => {
    setMessage((prevMessage) => prevMessage + emoji.emoji);
  };

  return (
    <form onSubmit={sendMessage} className="flex items-center">
      <div className="m-2">
        <EmojiPicker onEmojiClick={handleEmojiClick} />
      </div>
      <input
        type="text"
        value={message}
        onChange={(e) => setMessage(e.target.value)}
        className="flex-grow p-2 border border-gray-300 rounded-l-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"
        placeholder="Type your message..."
      />
      <button
        type="submit"
        className="p-2 bg-indigo-500 text-white rounded-r-lg hover:bg-indigo-600 focus:outline-none focus:ring-2 focus:ring-indigo-500"
      >
        Send
      </button>
    </form>
  );
};

export default ChatInput;
import React from 'react';
import Chat from './Chat';

function App() {
  return (
    <div className="flex flex-col items-center justify-center h-screen">
      <div className="w-full max-w-md p-4 bg-white shadow-lg rounded-lg">
        <Chat />
      </div>
    </div>
  );
}

export default App;

Conclusión

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *