Skip to content

Commit 0eaf6e9

Browse files
committed
fix
1 parent 09d4633 commit 0eaf6e9

6 files changed

Lines changed: 205 additions & 58 deletions

File tree

frontend/NextVibe/components/Chat/ChatBubble.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ const ChatBubble: React.FC<Props> = ({ message, onReply, onReactionPress, onEdit
164164
{/* Content text / tombstone */}
165165
<View style={styles.contentWrapper}>
166166
<Text style={[styles.text, isDeleted && styles.deletedText]}>
167-
{isDeleted ? '🚫 This message was deleted' : message.content}
167+
{isDeleted ? '🚫 This message was deleted' : (message.content || (message as any).text)}
168168
</Text>
169169

170170
<View style={styles.statusContainer}>
@@ -183,7 +183,7 @@ const ChatBubble: React.FC<Props> = ({ message, onReply, onReactionPress, onEdit
183183
<TouchableOpacity
184184
key={react.emoji}
185185
style={[styles.reactionPill, react.reacted_by_me && styles.reactionPillActive]}
186-
onPress={() => onReactionPress && onReactionPress(message.message_id, react.emoji)}
186+
onPress={() => onReactionPress && onReactionPress(message.server_msg_id || message.message_id || (message as any).id, react.emoji)}
187187
>
188188
<Text style={styles.reactionEmoji}>{react.emoji}</Text>
189189
<Text style={styles.reactionCount}>{react.count}</Text>

frontend/NextVibe/components/Chat/CustomChatScreen.tsx

Lines changed: 78 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -332,12 +332,19 @@ export default function CustomChatScreen() {
332332
if (event.user_id !== currentUserId) {
333333
setIsTyping(event.is_typing);
334334
}
335-
} else if (event.type === 'message_edited') {
335+
} else if (event.type === 'message_edited' || event.type === 'edit_message') {
336+
const targetId = String(event.server_msg_id || event.message_id || event.id);
337+
const updatedText = event.content || event.text;
336338
setMessages(prev =>
337339
prev.map(msg => {
338-
const msgId = msg.server_msg_id || (msg as any).message_id || msg.id;
339-
if (String(msgId) === String(event.message_id)) {
340-
return { ...msg, text: event.content, content: event.content, edited_at: event.edited_at };
340+
const msgId = String(msg.server_msg_id || (msg as any).message_id || msg.id || msg.client_msg_id);
341+
if (msgId === targetId || (msg.server_msg_id && String(msg.server_msg_id) === targetId)) {
342+
return {
343+
...msg,
344+
text: updatedText || msg.text || msg.content,
345+
content: updatedText || msg.content || msg.text,
346+
edited_at: event.edited_at || new Date().toISOString()
347+
};
341348
}
342349
return msg;
343350
})
@@ -426,12 +433,32 @@ export default function CustomChatScreen() {
426433

427434
// If Editing existing message:
428435
if (editingMessage) {
429-
const msgId = editingMessage.server_msg_id || (editingMessage as any).message_id || editingMessage.id;
436+
const rawMsgId = editingMessage.server_msg_id || (editingMessage as any).message_id || editingMessage.id;
437+
const numMsgId = Number(rawMsgId);
438+
439+
// Optimistically update message in state immediately
440+
setMessages(prev =>
441+
prev.map(m => {
442+
const mKey = String(m.server_msg_id || (m as any).message_id || m.id || m.client_msg_id);
443+
if (mKey === String(rawMsgId) || (!isNaN(numMsgId) && (m.server_msg_id === numMsgId || (m as any).message_id === numMsgId))) {
444+
return {
445+
...m,
446+
text: messageText,
447+
content: messageText,
448+
edited_at: new Date().toISOString()
449+
};
450+
}
451+
return m;
452+
})
453+
);
454+
430455
try {
431-
await editMessage(chatId, Number(msgId), messageText);
456+
await editMessage(chatId, numMsgId, messageText);
432457
setToast({ visible: true, message: 'Message edited', isSuccess: true });
433-
} catch (err) {
458+
} catch (err: any) {
434459
console.error('Failed to edit message:', err);
460+
const errorMsg = err?.response?.data?.detail || err?.message || 'Failed to edit message';
461+
setToast({ visible: true, message: errorMsg, isSuccess: false });
435462
} finally {
436463
setEditingMessage(null);
437464
setIsSending(false);
@@ -487,10 +514,43 @@ export default function CustomChatScreen() {
487514

488515
const handleToggleReaction = async (emoji: string) => {
489516
if (!selectedActionMessage) return;
490-
const msgId = selectedActionMessage.server_msg_id || (selectedActionMessage as any).message_id || selectedActionMessage.id;
517+
const rawMsgId = selectedActionMessage.server_msg_id || (selectedActionMessage as any).message_id || selectedActionMessage.id;
518+
const numMsgId = Number(rawMsgId);
491519
setActionModalVisible(false);
520+
521+
if (isNaN(numMsgId)) return;
522+
523+
// Optimistically update reactions in local state
524+
setMessages(prev =>
525+
prev.map(msg => {
526+
const mKey = String(msg.server_msg_id || (msg as any).message_id || msg.id || msg.client_msg_id);
527+
if (mKey === String(rawMsgId) || (!isNaN(numMsgId) && (msg.server_msg_id === numMsgId || (msg as any).message_id === numMsgId))) {
528+
const currentReactions = msg.reactions || [];
529+
const existingIdx = currentReactions.findIndex(r => r.emoji === emoji);
530+
let newReactions = [...currentReactions];
531+
532+
if (existingIdx !== -1) {
533+
const existing = newReactions[existingIdx];
534+
if (existing.reacted_by_me) {
535+
if (existing.count <= 1) {
536+
newReactions.splice(existingIdx, 1);
537+
} else {
538+
newReactions[existingIdx] = { ...existing, count: existing.count - 1, reacted_by_me: false };
539+
}
540+
} else {
541+
newReactions[existingIdx] = { ...existing, count: existing.count + 1, reacted_by_me: true };
542+
}
543+
} else {
544+
newReactions.push({ emoji, count: 1, reacted_by_me: true });
545+
}
546+
return { ...msg, reactions: newReactions };
547+
}
548+
return msg;
549+
})
550+
);
551+
492552
try {
493-
await addReaction(chatId, Number(msgId), emoji);
553+
await addReaction(chatId, numMsgId, emoji);
494554
} catch (err) {
495555
console.error('Failed to add reaction:', err);
496556
}
@@ -550,7 +610,10 @@ export default function CustomChatScreen() {
550610
onLongPress={() => handleMessageLongPress(item)}
551611
onReply={() => setReplyToMessage(item)}
552612
onReactionPress={(msgId, emoji) => {
553-
addReaction(chatId, msgId, emoji);
613+
const numId = Number(msgId);
614+
if (!isNaN(numId)) {
615+
addReaction(chatId, numId, emoji);
616+
}
554617
}}
555618
/>
556619
);
@@ -657,7 +720,7 @@ export default function CustomChatScreen() {
657720
<LiquidGlassView
658721
glassEffectStyle="clear"
659722
colorScheme="auto"
660-
fallbackBackgroundColor="rgba(21, 7, 35, 0.4)"
723+
fallbackBackgroundColor="transparent"
661724
style={styles.floatingGlassCapsule}
662725
>
663726
{/* Action Banners */}
@@ -677,7 +740,7 @@ export default function CustomChatScreen() {
677740
)}
678741

679742
{editingMessage && (
680-
<View style={[styles.actionBanner, { backgroundColor: 'rgba(167, 139, 250, 0.25)' }]}>
743+
<View style={[styles.actionBanner, { backgroundColor: 'rgba(255, 255, 255, 0.08)' }]}>
681744
<View style={[styles.bannerBar, { backgroundColor: '#A78BFA' }]} />
682745
<View style={styles.bannerContent}>
683746
<Text style={[styles.bannerTitle, { color: '#A78BFA' }]}>Editing message</Text>
@@ -966,7 +1029,7 @@ const styles = StyleSheet.create({
9661029
actionBanner: {
9671030
flexDirection: 'row',
9681031
alignItems: 'center',
969-
backgroundColor: 'rgba(167, 139, 250, 0.18)',
1032+
backgroundColor: 'rgba(255, 255, 255, 0.08)',
9701033
paddingHorizontal: 14,
9711034
paddingVertical: 8,
9721035
borderBottomWidth: 1,
@@ -1004,7 +1067,7 @@ const styles = StyleSheet.create({
10041067
width: 36,
10051068
height: 36,
10061069
borderRadius: 18,
1007-
backgroundColor: 'rgba(167, 139, 250, 0.15)',
1070+
backgroundColor: 'transparent',
10081071
justifyContent: 'center',
10091072
alignItems: 'center',
10101073
marginRight: 8,
@@ -1016,7 +1079,7 @@ const styles = StyleSheet.create({
10161079
paddingHorizontal: 14,
10171080
paddingVertical: 8,
10181081
borderRadius: 20,
1019-
backgroundColor: 'rgba(255, 255, 255, 0.08)',
1082+
backgroundColor: 'transparent',
10201083
marginRight: 8,
10211084
},
10221085
sendButton: {

frontend/NextVibe/components/Shared/LiquidGlassView.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ function resolveFallbackBackground(
2929
? 'light'
3030
: 'dark';
3131

32-
return resolved === 'light' ? 'rgba(255, 255, 255, 0.85)' : 'rgba(21, 13, 36, 0.85)';
32+
return resolved === 'light' ? 'rgba(255, 255, 255, 0.85)' : 'rgba(255, 255, 255, 0.1)';
3333
}
3434

3535
function makeMoreTransparent(color: string | undefined): string | undefined {

socket_service/main.py

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -217,10 +217,14 @@ async def websocket_endpoint(websocket: WebSocket):
217217
await manager.send_to_users(participant_ids, response_data)
218218

219219
elif message_type in ("reaction_add", "reaction_remove"):
220-
chat_id = data.get("chat_id")
221-
message_id = data.get("message_id")
222-
emoji = data.get("emoji")
220+
try:
221+
chat_id = int(data.get("chat_id")) if data.get("chat_id") is not None else None
222+
message_id = int(data.get("message_id")) if data.get("message_id") is not None else None
223+
except (ValueError, TypeError):
224+
await websocket.send_json({"type": "error", "detail": "Invalid chat_id or message_id"})
225+
continue
223226

227+
emoji = data.get("emoji")
224228
if not chat_id or not message_id or not emoji:
225229
await websocket.send_json({"type": "error", "detail": "Missing chat_id, message_id or emoji"})
226230
continue
@@ -230,7 +234,7 @@ async def websocket_endpoint(websocket: WebSocket):
230234
await websocket.send_json({"type": "error", "detail": "Not authorized"})
231235
continue
232236

233-
message = db.query(Message).filter(Message.id == message_id, Message.chat_id == chat_id).first()
237+
message = db.query(Message).filter(Message.id == message_id).first()
234238
if not message:
235239
await websocket.send_json({"type": "error", "detail": "Message not found"})
236240
continue
@@ -254,26 +258,27 @@ async def websocket_endpoint(websocket: WebSocket):
254258
db.delete(existing)
255259
db.commit()
256260

257-
keys = r.keys(f"chat:{chat_id}:*")
261+
keys = r.keys(f"chat:{message.chat_id}:*")
258262
if keys:
259263
r.delete(*keys)
260264

261265
reactions = db.query(MessageReaction).filter(MessageReaction.message_id == message_id).all()
262-
summary = {}
263-
for r_item in reactions:
264-
if r_item.emoji not in summary:
265-
summary[r_item.emoji] = {"emoji": r_item.emoji, "count": 0, "reacted_by_me": False}
266-
summary[r_item.emoji]["count"] += 1
267-
if r_item.user_id == user_id:
268-
summary[r_item.emoji]["reacted_by_me"] = True
269-
270-
participant_ids = [u.user_id for u in chat.participants]
271-
await manager.send_to_users(participant_ids, {
272-
"type": "reaction_update",
273-
"chat_id": chat_id,
274-
"message_id": message_id,
275-
"reactions": list(summary.values())
276-
})
266+
for p in chat.participants:
267+
p_id = p.user_id
268+
summary = {}
269+
for r_item in reactions:
270+
if r_item.emoji not in summary:
271+
summary[r_item.emoji] = {"emoji": r_item.emoji, "count": 0, "reacted_by_me": False}
272+
summary[r_item.emoji]["count"] += 1
273+
if r_item.user_id == p_id:
274+
summary[r_item.emoji]["reacted_by_me"] = True
275+
276+
await manager.send_to_users([p_id], {
277+
"type": "reaction_update",
278+
"chat_id": message.chat_id,
279+
"message_id": message_id,
280+
"reactions": list(summary.values())
281+
})
277282

278283
elif message_type in ("typing_start", "typing_stop"):
279284
chat_id = data.get("chat_id")
@@ -324,11 +329,20 @@ async def websocket_endpoint(websocket: WebSocket):
324329
await manager.send_to_users([target_user_id], signal_payload)
325330

326331
elif message_type == "edit_message":
327-
chat_id = data.get("chat_id")
328-
message_id = data.get("message_id")
332+
try:
333+
chat_id = int(data.get("chat_id")) if data.get("chat_id") is not None else None
334+
message_id = int(data.get("message_id")) if data.get("message_id") is not None else None
335+
except (ValueError, TypeError):
336+
await websocket.send_json({"type": "error", "detail": "Invalid chat_id or message_id"})
337+
continue
338+
329339
new_text = data.get("text", "")
330340

331-
message = db.query(Message).filter(Message.id == message_id, Message.chat_id == chat_id).first()
341+
if not message_id:
342+
await websocket.send_json({"type": "error", "detail": "Missing message_id"})
343+
continue
344+
345+
message = db.query(Message).filter(Message.id == message_id).first()
332346
if not message:
333347
await websocket.send_json({"type": "error", "detail": "Message not found"})
334348
continue
@@ -349,17 +363,18 @@ async def websocket_endpoint(websocket: WebSocket):
349363
message.edited_at = datetime.utcnow()
350364
db.commit()
351365

352-
keys = r.keys(f"chat:{chat_id}:*")
366+
keys = r.keys(f"chat:{message.chat_id}:*")
353367
if keys:
354368
r.delete(*keys)
355369

356370
participant_ids = [u.user_id for u in message.chat.participants]
357371
await manager.send_to_users(participant_ids, {
358372
"type": "message_edited",
359-
"chat_id": chat_id,
360-
"message_id": message_id,
361-
"server_msg_id": message_id,
373+
"chat_id": message.chat_id,
374+
"message_id": message.id,
375+
"server_msg_id": message.id,
362376
"content": new_text,
377+
"text": new_text,
363378
"edited_at": message.edited_at.isoformat()
364379
})
365380

0 commit comments

Comments
 (0)