akborana4 commited on
Commit
7c539f4
·
verified ·
1 Parent(s): 914f045

Update client/src/Chat.jsx

Browse files
Files changed (1) hide show
  1. client/src/Chat.jsx +12 -38
client/src/Chat.jsx CHANGED
@@ -8,15 +8,13 @@ export default function Chat({ socket, roomId, name, isHost, members }) {
8
  const inputRef = useRef(null);
9
  const logRef = useRef(null);
10
 
11
- // Mentions
12
  const [showMentions, setShowMentions] = useState(false);
13
  const [mentionQuery, setMentionQuery] = useState('');
14
  const [mentionAtIndex, setMentionAtIndex] = useState(-1);
15
 
16
  const mentionCandidates = useMemo(() => {
17
  const q = (mentionQuery || '').toLowerCase();
18
- const arr = (members || []).filter(m => m.name.toLowerCase().includes(q)).slice(0, 8);
19
- return arr;
20
  }, [mentionQuery, members]);
21
 
22
  useEffect(() => {
@@ -34,18 +32,10 @@ export default function Chat({ socket, roomId, name, isHost, members }) {
34
  const parseCommand = (t) => {
35
  const raw = t.trim();
36
  const lower = raw.toLowerCase();
37
- if (lower.startsWith('/play ')) {
38
- return { cmd: 'play', arg: raw.slice(6).trim() };
39
- }
40
- if (lower.startsWith('/kick ')) {
41
- return { cmd: 'kick', arg: raw.slice(6).trim().replace(/^@/, '') };
42
- }
43
- if (lower.startsWith('/promote ')) {
44
- return { cmd: 'promote', arg: raw.slice(9).trim().replace(/^@/, '') };
45
- }
46
- if (lower.startsWith('/mute ')) {
47
- return { cmd: 'mute', arg: raw.slice(6).trim().replace(/^@/, '') };
48
- }
49
  return null;
50
  };
51
 
@@ -65,20 +55,15 @@ export default function Chat({ socket, roomId, name, isHost, members }) {
65
  return;
66
  }
67
  if (parsed.cmd === 'kick' || parsed.cmd === 'promote' || parsed.cmd === 'mute') {
68
- if (!isHost) {
69
- push('Only host/co-host can run admin commands', 'warn');
70
- } else if (!parsed.arg) {
71
- push('Provide a username like /kick @username', 'warn');
72
- } else {
73
- socket.emit('admin_command', { roomId, cmd: parsed.cmd, targetName: parsed.arg });
74
- }
75
  setText('');
76
  setShowMentions(false);
77
  return;
78
  }
79
  }
80
 
81
- // Normal chat
82
  const m = { name, text: t, at: Date.now() };
83
  setMessages((prev) => [...prev, m]);
84
  socket.emit('chat_message', { roomId, name, text: t });
@@ -91,7 +76,6 @@ export default function Chat({ socket, roomId, name, isHost, members }) {
91
  const caret = e.target.selectionStart ?? val.length;
92
  setText(val);
93
 
94
- // Find last '@' before caret
95
  let at = -1;
96
  for (let i = caret - 1; i >= 0; i--) {
97
  if (val[i] === '@') { at = i; break; }
@@ -118,7 +102,6 @@ export default function Chat({ socket, roomId, name, isHost, members }) {
118
  if (at < 0) return;
119
 
120
  const before = val.slice(0, at);
121
- // find end of mention segment from at+1 to caret (no spaces)
122
  const nextSpace = val.slice(at + 1).search(/\s/);
123
  const end = nextSpace < 0 ? val.length : (at + 1 + nextSpace);
124
  const after = val.slice(end);
@@ -127,12 +110,8 @@ export default function Chat({ socket, roomId, name, isHost, members }) {
127
  const nextVal = inserted + after;
128
  setText(nextVal);
129
 
130
- // place caret after inserted
131
  const newPos = inserted.length;
132
- setTimeout(() => {
133
- el.focus();
134
- el.setSelectionRange(newPos, newPos);
135
- }, 0);
136
 
137
  setShowMentions(false);
138
  setMentionQuery('');
@@ -159,18 +138,13 @@ export default function Chat({ socket, roomId, name, isHost, members }) {
159
  <button className="btn primary" onClick={handleSend}>Send</button>
160
  </div>
161
 
162
- {showMentions && mentionCandidates.length > 0 && (
163
  <div className="mentions" style={{ left: 8, bottom: 52 }}>
164
- {mentionCandidates.map(m => (
165
  <div key={m.id} className="item" onClick={() => chooseMention(m)}>
166
  @{m.name} {m.role !== 'member' ? `(${m.role})` : ''}
167
  </div>
168
- ))}
169
- </div>
170
- )}
171
- {showMentions && mentionCandidates.length === 0 && (
172
- <div className="mentions" style={{ left: 8, bottom: 52 }}>
173
- <div className="item" style={{ opacity:.7 }}>No matches</div>
174
  </div>
175
  )}
176
  </div>
 
8
  const inputRef = useRef(null);
9
  const logRef = useRef(null);
10
 
 
11
  const [showMentions, setShowMentions] = useState(false);
12
  const [mentionQuery, setMentionQuery] = useState('');
13
  const [mentionAtIndex, setMentionAtIndex] = useState(-1);
14
 
15
  const mentionCandidates = useMemo(() => {
16
  const q = (mentionQuery || '').toLowerCase();
17
+ return (members || []).filter(m => m.name.toLowerCase().includes(q)).slice(0, 8);
 
18
  }, [mentionQuery, members]);
19
 
20
  useEffect(() => {
 
32
  const parseCommand = (t) => {
33
  const raw = t.trim();
34
  const lower = raw.toLowerCase();
35
+ if (lower.startsWith('/play ')) return { cmd: 'play', arg: raw.slice(6).trim() };
36
+ if (lower.startsWith('/kick ')) return { cmd: 'kick', arg: raw.slice(6).trim().replace(/^@/, '') };
37
+ if (lower.startsWith('/promote ')) return { cmd: 'promote', arg: raw.slice(9).trim().replace(/^@/, '') };
38
+ if (lower.startsWith('/mute ')) return { cmd: 'mute', arg: raw.slice(6).trim().replace(/^@/, '') };
 
 
 
 
 
 
 
 
39
  return null;
40
  };
41
 
 
55
  return;
56
  }
57
  if (parsed.cmd === 'kick' || parsed.cmd === 'promote' || parsed.cmd === 'mute') {
58
+ if (!isHost) push('Only host/co-host can run admin commands', 'warn');
59
+ else if (!parsed.arg) push('Provide a username like /kick @username', 'warn');
60
+ else socket.emit('admin_command', { roomId, cmd: parsed.cmd, targetName: parsed.arg });
 
 
 
 
61
  setText('');
62
  setShowMentions(false);
63
  return;
64
  }
65
  }
66
 
 
67
  const m = { name, text: t, at: Date.now() };
68
  setMessages((prev) => [...prev, m]);
69
  socket.emit('chat_message', { roomId, name, text: t });
 
76
  const caret = e.target.selectionStart ?? val.length;
77
  setText(val);
78
 
 
79
  let at = -1;
80
  for (let i = caret - 1; i >= 0; i--) {
81
  if (val[i] === '@') { at = i; break; }
 
102
  if (at < 0) return;
103
 
104
  const before = val.slice(0, at);
 
105
  const nextSpace = val.slice(at + 1).search(/\s/);
106
  const end = nextSpace < 0 ? val.length : (at + 1 + nextSpace);
107
  const after = val.slice(end);
 
110
  const nextVal = inserted + after;
111
  setText(nextVal);
112
 
 
113
  const newPos = inserted.length;
114
+ setTimeout(() => { el.focus(); el.setSelectionRange(newPos, newPos); }, 0);
 
 
 
115
 
116
  setShowMentions(false);
117
  setMentionQuery('');
 
138
  <button className="btn primary" onClick={handleSend}>Send</button>
139
  </div>
140
 
141
+ {showMentions && (
142
  <div className="mentions" style={{ left: 8, bottom: 52 }}>
143
+ {mentionCandidates.length ? mentionCandidates.map(m => (
144
  <div key={m.id} className="item" onClick={() => chooseMention(m)}>
145
  @{m.name} {m.role !== 'member' ? `(${m.role})` : ''}
146
  </div>
147
+ )) : <div className="item" style={{ opacity:.7 }}>No matches</div>}
 
 
 
 
 
148
  </div>
149
  )}
150
  </div>