fix: TYPING_START events now carry user_id and username

The backend was broadcasting TYPING_START with only channel_id.
Frontend required user_id + username to show who is typing.
Added Username to Client struct, fetched on WS connect via JOIN.
Typing events now enriched with sender identity before broadcast.
This commit is contained in:
2026-07-02 09:39:15 -04:00
parent 7d67d6f360
commit eb5b7d55a4
+26 -12
View File
@@ -102,10 +102,11 @@ func extractHostname(s string) string {
// Client is a middleman between the websocket connection and the hub. // Client is a middleman between the websocket connection and the hub.
type Client struct { type Client struct {
Hub *Hub Hub *Hub
Conn *websocket.Conn Conn *websocket.Conn
UserID string UserID string
send chan []byte Username string
send chan []byte
} }
// readPump pumps messages from the websocket connection to the hub. // readPump pumps messages from the websocket connection to the hub.
@@ -140,7 +141,19 @@ func (c *Client) readPump() {
c.Hub.RecordActivity(c.UserID) c.Hub.RecordActivity(c.UserID)
switch event.Type { switch event.Type {
case EventTypingStart, EventPresenceUpdate: case EventTypingStart:
// Enrich with sender info so receivers know who is typing
var data map[string]interface{}
if raw, ok := event.Data.(json.RawMessage); ok {
json.Unmarshal(raw, &data)
}
if data == nil {
data = make(map[string]interface{})
}
data["user_id"] = c.UserID
data["username"] = c.Username
c.Hub.BroadcastEvent(Event{Type: event.Type, Data: data})
case EventPresenceUpdate:
c.Hub.BroadcastEvent(event) c.Hub.BroadcastEvent(event)
case EventVoiceWhisper: case EventVoiceWhisper:
// Forward voice whispers only to the target user, not broadcast // Forward voice whispers only to the target user, not broadcast
@@ -247,11 +260,11 @@ func ServeWS(db *sql.DB, hub *Hub, logger *slog.Logger, w http.ResponseWriter, r
token = auth.Token token = auth.Token
} }
var userID string var userID, username string
err = db.QueryRowContext(context.Background(), err = db.QueryRowContext(context.Background(),
`SELECT user_id FROM sessions WHERE token = $1 AND expires_at > NOW()`, `SELECT u.id, u.username FROM sessions s JOIN users u ON u.id = s.user_id WHERE s.token = $1 AND s.expires_at > NOW()`,
token, token,
).Scan(&userID) ).Scan(&userID, &username)
if err != nil { if err != nil {
logger.Warn("ws auth: invalid session", "error", err) logger.Warn("ws auth: invalid session", "error", err)
conn.WriteMessage(websocket.TextMessage, []byte(`{"error":"invalid session"}`)) conn.WriteMessage(websocket.TextMessage, []byte(`{"error":"invalid session"}`))
@@ -263,10 +276,11 @@ func ServeWS(db *sql.DB, hub *Hub, logger *slog.Logger, w http.ResponseWriter, r
conn.WriteMessage(websocket.TextMessage, []byte(`{"type":"ready"}`)) conn.WriteMessage(websocket.TextMessage, []byte(`{"type":"ready"}`))
client := &Client{ client := &Client{
Hub: hub, Hub: hub,
Conn: conn, Conn: conn,
UserID: userID, UserID: userID,
send: make(chan []byte, 256), Username: username,
send: make(chan []byte, 256),
} }
hub.Register(client) hub.Register(client)