Tired with both realtime/hander.js and public/js
// bench/apps/chat/realtime/chat_handlers.js
function getIndividualRoomName(user1, user2) {
const sorted = [user1, user2].sort(); // Ensure consistency
return individual:${sorted[0]}-${sorted[1]}
;
}
function chat_handlers(socket) {
socket.on(“join_room”, (data) => {
try {
const { chat_type, person_email, channel_name, user_email } = data;
if (!chat_type || (!person_email && !channel_name)) return;
const room =
chat_type === "Individual"
? getIndividualRoomName(user_email, person_email)
: `group:${channel_name}`;
socket.join(room);
console.log(`Joined room: ${room}`);
} catch (err) {
console.error("Failed to handle join_room:", err);
}
});
socket.on("leave_room", (data) => {
try {
const { chat_type, person_email, channel_name, user_email } = data;
if (!chat_type || (!person_email && !channel_name)) return;
const room =
chat_type === "Individual"
? getIndividualRoomName(user_email, person_email)
: `group:${channel_name}`;
socket.leave(room);
console.log(`Left room: ${room}`);
} catch (err) {
console.error("Failed to handle leave_room:", err);
}
});
socket.on("send_message", (data) => {
const { chat_type, sender, person_email, channel_name, content } = data;
if (!content || !chat_type) return;
let message = {
doctype: "Chat Message VM",
chat_type,
sender,
content,
};
let room;
if (chat_type === "Individual") {
message.receiver = person_email;
room = getIndividualRoomName(sender, person_email);
} else {
message.channel_name = channel_name;
room = `group:${channel_name}`;
}
frappe.db.insert(message)
.then((doc) => {
socket.to(room).emit("new_message", doc);
socket.emit("new_message", doc);
})
.catch((err) => {
console.error("Failed to handle send_message:", err);
});
});
}
module.exports = chat_handlers;
index.js
// bench/apps/your_app/realtime/index.js
const chat_handlers = require(“./handler”);
frappe.socketio.on(“connection”, (socket) => {
chat_handlers(socket);
});
public/js
function getIndividualRoomName(user1, user2) {
const sorted = [user1, user2].sort(); // Ensure consistency
return individual:${sorted[0]}-${sorted[1]}
;
}
frappe.socketio.on(“join_room”, async function (socket, data) {
try {
const { chat_type, person_email, channel_name, user_email } = data;
if (!chat_type || (!person_email && !channel_name)) return;
const room =
chat_type === "Individual"
? getIndividualRoomName(user_email, person_email)
: `group:${channel_name}`;
socket.join(room);
console.log(room)
} catch (err) {
console.error("Failed to handle join_room:", err);
}
});
frappe.socketio.on(“leave_room”, async function (socket, data) {
try {
const { chat_type, person_email, channel_name, user_email } = data;
if (!chat_type || (!person_email && !channel_name)) return;
const room =
chat_type === "Individual"
? getIndividualRoomName(user_email, person_email)
: `group:${channel_name}`;
socket.leave(room);
} catch (err) {
console.error("Failed to handle leave_room:", err);
}
});
frappe.socketio.on(“send_message”, function (socket, data) {
const { chat_type, sender, person_email, channel_name, content } = data;
if (!content || !chat_type) return;
let message = {
doctype: "Chat Message VM",
chat_type,
sender,
content,
};
let room;
if (chat_type === "Individual") {
message.receiver = person_email;
room = getIndividualRoomName(sender, person_email);
} else {
message.channel_name = channel_name;
room = `group:${channel_name}`;
}
frappe.db.insert(message)
.then((doc) => {
socket.to(room).emit("new_message", doc);
socket.emit("new_message", doc); // emit to sender too
})
.catch((err) => {
console.error("Failed to handle send_message:", err);
});
});
please help me to setup socketio
frontend
import { useEffect, useState } from “react”;
import { io, Socket } from “socket.io-client”;
type ChatType = “Individual” | “Group”;
interface ChatMessage {
sender: string;
content: string;
[key: string]: any;
}
// Connect to Frappe’s WebSocket server
const socket: Socket = io(“http://test.localhost:9000”); // change if needed
function App() {
const [chatType, setChatType] = useState(“Individual”);
const [userEmail, setUserEmail] = useState(“user1@example.com”);
const [personEmail, setPersonEmail] = useState(“user2@example.com”);
const [channelName, setChannelName] = useState(“general”);
const [message, setMessage] = useState(“”);
const [messages, setMessages] = useState<ChatMessage>();
useEffect(() => {
socket.on(“connect”, () => {
console.log(“Connected to server:”, socket.id);
});
socket.on("new_message", (msg: ChatMessage) => {
console.log("Received:", msg);
setMessages((prev) => [...prev, msg]);
});
return () => {
socket.off("new_message");
};
}, );
const joinRoom = () => {
socket.emit(“join_room”, {
chat_type: chatType,
user_email: userEmail,
person_email: personEmail,
channel_name: channelName,
});
};
const leaveRoom = () => {
socket.emit(“leave_room”, {
chat_type: chatType,
user_email: userEmail,
person_email: personEmail,
channel_name: channelName,
});
};
const sendMessage = () => {
if (!message.trim()) return;
console.log(“Sending”)
socket.emit("send_message", {
chat_type: chatType,
sender: userEmail,
person_email: personEmail,
channel_name: channelName,
content: message,
});
setMessage("");
};
return (
<div style={{ padding: “2rem”, fontFamily: “Arial” }}>
Frappe Socket.IO Chat Test (TS)
<div style={{ marginBottom: "1rem" }}>
<label>
Chat Type:{" "}
<select
value={chatType}
onChange={(e) => setChatType(e.target.value as ChatType)}
>
<option value="Individual">Individual</option>
<option value="Group">Group</option>
</select>
</label>
</div>
<div>
<label>
Your Email:{" "}
<input
value={userEmail}
onChange={(e) => setUserEmail(e.target.value)}
/>
</label>
</div>
{chatType === "Individual" ? (
<div>
<label>
Receiver Email:{" "}
<input
value={personEmail}
onChange={(e) => setPersonEmail(e.target.value)}
/>
</label>
</div>
) : (
<div>
<label>
Channel Name:{" "}
<input
value={channelName}
onChange={(e) => setChannelName(e.target.value)}
/>
</label>
</div>
)}
<div style={{ marginTop: "1rem" }}>
<button onClick={joinRoom}>Join Room</button>
<button onClick={leaveRoom} style={{ marginLeft: "0.5rem" }}>
Leave Room
</button>
</div>
<div style={{ marginTop: "1rem" }}>
<input
value={message}
onChange={(e) => setMessage(e.target.value)}
placeholder="Enter message..."
style={{ width: "60%" }}
/>
<button onClick={sendMessage} style={{ marginLeft: "0.5rem" }}>
Send
</button>
</div>
<div style={{ marginTop: "2rem" }}>
<h3>💬 Messages</h3>
<ul>
{messages.map((msg, idx) => (
<li key={idx}>
<strong>{msg.sender}</strong>: {msg.content}
</li>
))}
</ul>
</div>
</div>
);
}
export default App;