|
|
|
|
@ -143,6 +143,39 @@ class AsyncWorker(QThread):
|
|
|
|
|
return False
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def send_image(self, peer_id: str, image_path: str) -> bool:
|
|
|
|
|
"""发送图片(线程安全)"""
|
|
|
|
|
if self._loop and self._running:
|
|
|
|
|
future = asyncio.run_coroutine_threadsafe(
|
|
|
|
|
self.client.send_image(peer_id, image_path),
|
|
|
|
|
self._loop
|
|
|
|
|
)
|
|
|
|
|
try:
|
|
|
|
|
return future.result(timeout=300.0)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Send image error: {e}")
|
|
|
|
|
return False
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def start_voice_call(self, peer_id: str) -> bool:
|
|
|
|
|
"""发起语音通话(线程安全)"""
|
|
|
|
|
if self._loop and self._running:
|
|
|
|
|
future = asyncio.run_coroutine_threadsafe(
|
|
|
|
|
self.client.start_voice_call(peer_id),
|
|
|
|
|
self._loop
|
|
|
|
|
)
|
|
|
|
|
try:
|
|
|
|
|
return future.result(timeout=30.0)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Start voice call error: {e}")
|
|
|
|
|
return False
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def end_voice_call(self):
|
|
|
|
|
"""结束语音通话(线程安全)"""
|
|
|
|
|
if self._loop and self._running:
|
|
|
|
|
self.client.end_voice_call()
|
|
|
|
|
|
|
|
|
|
def refresh_users(self):
|
|
|
|
|
"""刷新用户列表(线程安全)"""
|
|
|
|
|
if self._loop and self._running:
|
|
|
|
|
@ -335,6 +368,26 @@ class P2PChatGUI(QObject):
|
|
|
|
|
f"收到来自 {sender} 的消息", 3000
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# 将消息添加到聊天窗口显示
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
from shared.models import ChatMessage
|
|
|
|
|
|
|
|
|
|
msg = ChatMessage(
|
|
|
|
|
message_id=message.message_id,
|
|
|
|
|
sender_id=sender,
|
|
|
|
|
receiver_id=message.receiver_id,
|
|
|
|
|
content_type=MessageType.TEXT,
|
|
|
|
|
content=content,
|
|
|
|
|
timestamp=datetime.fromtimestamp(message.timestamp),
|
|
|
|
|
is_sent=True,
|
|
|
|
|
is_read=False
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# 如果当前正在和发送者聊天,直接显示消息
|
|
|
|
|
if hasattr(self, '_chat_widget') and self._chat_widget:
|
|
|
|
|
if self._current_chat_peer == sender:
|
|
|
|
|
self._chat_widget.add_message(msg, is_self=False)
|
|
|
|
|
|
|
|
|
|
logger.info(f"Message from {sender}: {content[:50]}...")
|
|
|
|
|
|
|
|
|
|
elif message.msg_type == MessageType.VOICE_CALL_REQUEST:
|
|
|
|
|
@ -343,6 +396,8 @@ class P2PChatGUI(QObject):
|
|
|
|
|
"来电",
|
|
|
|
|
f"{sender} 正在呼叫你"
|
|
|
|
|
)
|
|
|
|
|
# 显示来电对话框
|
|
|
|
|
self._show_incoming_call_dialog(sender)
|
|
|
|
|
|
|
|
|
|
def _on_user_list_updated(self, users: List[UserInfo]):
|
|
|
|
|
"""用户列表更新回调"""
|
|
|
|
|
@ -379,7 +434,11 @@ class P2PChatGUI(QObject):
|
|
|
|
|
|
|
|
|
|
def _on_send_file(self):
|
|
|
|
|
"""发送文件"""
|
|
|
|
|
from PyQt6.QtWidgets import QFileDialog
|
|
|
|
|
from PyQt6.QtWidgets import QFileDialog, QMessageBox
|
|
|
|
|
|
|
|
|
|
if not self._current_chat_peer:
|
|
|
|
|
QMessageBox.warning(self.main_window, "提示", "请先选择一个联系人")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
file_path, _ = QFileDialog.getOpenFileName(
|
|
|
|
|
self.main_window,
|
|
|
|
|
@ -390,11 +449,20 @@ class P2PChatGUI(QObject):
|
|
|
|
|
|
|
|
|
|
if file_path and self._current_chat_peer:
|
|
|
|
|
self.main_window._statusbar.showMessage(f"正在发送文件...")
|
|
|
|
|
# TODO: 实现文件发送
|
|
|
|
|
if self.worker:
|
|
|
|
|
success = self.worker.send_file(self._current_chat_peer, file_path)
|
|
|
|
|
if success:
|
|
|
|
|
self.main_window._statusbar.showMessage("文件发送成功", 3000)
|
|
|
|
|
else:
|
|
|
|
|
self.main_window._statusbar.showMessage("文件发送失败", 3000)
|
|
|
|
|
|
|
|
|
|
def _on_send_image(self):
|
|
|
|
|
"""发送图片"""
|
|
|
|
|
from PyQt6.QtWidgets import QFileDialog
|
|
|
|
|
from PyQt6.QtWidgets import QFileDialog, QMessageBox
|
|
|
|
|
|
|
|
|
|
if not self._current_chat_peer:
|
|
|
|
|
QMessageBox.warning(self.main_window, "提示", "请先选择一个联系人")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
file_path, _ = QFileDialog.getOpenFileName(
|
|
|
|
|
self.main_window,
|
|
|
|
|
@ -405,13 +473,60 @@ class P2PChatGUI(QObject):
|
|
|
|
|
|
|
|
|
|
if file_path and self._current_chat_peer:
|
|
|
|
|
self.main_window._statusbar.showMessage(f"正在发送图片...")
|
|
|
|
|
# TODO: 实现图片发送
|
|
|
|
|
if self.worker:
|
|
|
|
|
success = self.worker.send_image(self._current_chat_peer, file_path)
|
|
|
|
|
if success:
|
|
|
|
|
self.main_window._statusbar.showMessage("图片发送成功", 3000)
|
|
|
|
|
else:
|
|
|
|
|
self.main_window._statusbar.showMessage("图片发送失败", 3000)
|
|
|
|
|
|
|
|
|
|
def _on_voice_call(self):
|
|
|
|
|
"""发起语音通话"""
|
|
|
|
|
if self._current_chat_peer:
|
|
|
|
|
self.main_window._statusbar.showMessage(f"正在呼叫...")
|
|
|
|
|
# TODO: 实现语音通话
|
|
|
|
|
from PyQt6.QtWidgets import QMessageBox
|
|
|
|
|
|
|
|
|
|
if not self._current_chat_peer:
|
|
|
|
|
QMessageBox.warning(self.main_window, "提示", "请先选择一个联系人")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
self.main_window._statusbar.showMessage(f"正在呼叫...")
|
|
|
|
|
if self.worker:
|
|
|
|
|
success = self.worker.start_voice_call(self._current_chat_peer)
|
|
|
|
|
if success:
|
|
|
|
|
self.main_window._statusbar.showMessage("通话已建立", 3000)
|
|
|
|
|
else:
|
|
|
|
|
self.main_window._statusbar.showMessage("呼叫失败", 3000)
|
|
|
|
|
|
|
|
|
|
def _show_incoming_call_dialog(self, caller_id: str):
|
|
|
|
|
"""显示来电对话框"""
|
|
|
|
|
from PyQt6.QtWidgets import QMessageBox
|
|
|
|
|
|
|
|
|
|
reply = QMessageBox.question(
|
|
|
|
|
self.main_window,
|
|
|
|
|
"来电",
|
|
|
|
|
f"{caller_id} 正在呼叫你,是否接听?",
|
|
|
|
|
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if reply == QMessageBox.StandardButton.Yes:
|
|
|
|
|
# 接听
|
|
|
|
|
if self.worker and self.worker._loop and self.worker._running:
|
|
|
|
|
future = asyncio.run_coroutine_threadsafe(
|
|
|
|
|
self.client.accept_voice_call(caller_id),
|
|
|
|
|
self.worker._loop
|
|
|
|
|
)
|
|
|
|
|
try:
|
|
|
|
|
success = future.result(timeout=10.0)
|
|
|
|
|
if success:
|
|
|
|
|
self.main_window._statusbar.showMessage("通话已接通", 3000)
|
|
|
|
|
else:
|
|
|
|
|
self.main_window._statusbar.showMessage("接听失败", 3000)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Accept call error: {e}")
|
|
|
|
|
else:
|
|
|
|
|
# 拒绝
|
|
|
|
|
if self.client:
|
|
|
|
|
self.client.reject_voice_call(caller_id)
|
|
|
|
|
self.main_window._statusbar.showMessage("已拒绝来电", 3000)
|
|
|
|
|
|
|
|
|
|
def _on_contact_selected(self, user_id: str):
|
|
|
|
|
"""联系人选中回调 - 打开聊天窗口"""
|
|
|
|
|
@ -452,6 +567,7 @@ class P2PChatGUI(QObject):
|
|
|
|
|
message_id=f"msg_{datetime.now().timestamp()}",
|
|
|
|
|
sender_id=self._current_user.user_id if self._current_user else "",
|
|
|
|
|
receiver_id=peer_id,
|
|
|
|
|
content_type=MessageType.TEXT,
|
|
|
|
|
content=content,
|
|
|
|
|
timestamp=datetime.now(),
|
|
|
|
|
is_sent=True
|
|
|
|
|
|