patch chat
This commit is contained in:
parent
96fe4d0b91
commit
229cab0dde
@ -2,7 +2,7 @@ DEBUG = True
|
||||
API_SCHEME = "https"
|
||||
API_HOST = "api.yobble.org"
|
||||
BASE_URL = f"{API_SCHEME}://{API_HOST}"
|
||||
APP_VERSION = "0.2_home_screen"
|
||||
APP_VERSION = "0.3_chat"
|
||||
APP_NAME = "yobble messenger"
|
||||
APP_HEADER = f"{APP_NAME}"
|
||||
if DEBUG: APP_HEADER=f"{APP_HEADER} ({APP_VERSION})"
|
||||
|
||||
@ -55,4 +55,24 @@ class PrivateChatHistoryData(BaseModel):
|
||||
|
||||
class PrivateChatHistoryResponse(BaseModel):
|
||||
status: str
|
||||
data: PrivateChatHistoryData
|
||||
data: PrivateChatHistoryData
|
||||
|
||||
|
||||
# send
|
||||
class PrivateMessageSendRequest(BaseModel):
|
||||
chat_id: UUID
|
||||
content: Optional[str] = Field(None, description="Содержимое сообщения (макс. 4096 символов)", max_length=4096)
|
||||
message_type: List[Literal["text"]] = Field(
|
||||
..., description="Один или несколько типов сообщения"
|
||||
)
|
||||
|
||||
|
||||
class PrivateMessageSendData(BaseModel):
|
||||
message_id: int
|
||||
chat_id: UUID
|
||||
created_at: datetime
|
||||
|
||||
|
||||
class PrivateMessageSendResponse(BaseModel):
|
||||
status: str
|
||||
data: PrivateMessageSendData
|
||||
@ -1,7 +1,11 @@
|
||||
import httpx
|
||||
from app.core import config
|
||||
from app.core.localizer import localizer
|
||||
from app.core.models.chat_models import PrivateChatListResponse, PrivateChatListData, PrivateChatHistoryResponse, PrivateChatHistoryData
|
||||
from app.core.models.chat_models import (
|
||||
PrivateChatListResponse, PrivateChatListData,
|
||||
PrivateChatHistoryResponse, PrivateChatHistoryData,
|
||||
PrivateMessageSendRequest, PrivateMessageSendResponse
|
||||
)
|
||||
from uuid import UUID
|
||||
|
||||
async def get_private_chats(token: str, offset: int = 0, limit: int = 20):
|
||||
@ -90,3 +94,48 @@ async def get_chat_history(token: str, chat_id: UUID, before_message_id: int = N
|
||||
return False, f"Ошибка сети: {e}"
|
||||
except Exception as e:
|
||||
return False, f"Произошла ошибка: {e}"
|
||||
|
||||
|
||||
async def send_private_message(token: str, payload: "PrivateMessageSendRequest"):
|
||||
"""
|
||||
Отправляет приватное сообщение в чат.
|
||||
|
||||
:param token: Токен доступа
|
||||
:param payload: Данные сообщения (Pydantic модель PrivateMessageSendRequest)
|
||||
:return: Кортеж (успех: bool, данные: PrivateMessageSendData | str)
|
||||
"""
|
||||
url = f"{config.BASE_URL}/v1/chat/private/send"
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
|
||||
try:
|
||||
async with httpx.AsyncClient(http2=True) as client:
|
||||
response = await client.post(url, headers=headers, json=payload.model_dump(mode='json'))
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
if data.get("status") == "fine":
|
||||
response_model = PrivateMessageSendResponse(**data)
|
||||
return True, response_model.data
|
||||
else:
|
||||
return False, data.get("detail", "Неизвестная ошибка ответа")
|
||||
|
||||
elif response.status_code in [401, 403, 404]:
|
||||
error_data = response.json()
|
||||
print("error_data", error_data)
|
||||
return False, error_data.get("detail", "Ошибка доступа или чат не найден")
|
||||
|
||||
elif response.status_code == 422:
|
||||
error_data = response.json()
|
||||
# Может быть список ошибок
|
||||
detail = error_data.get("detail")
|
||||
if isinstance(detail, list):
|
||||
return False, ", ".join([e.get("msg", "Неизвестная ошибка валидации") for e in detail])
|
||||
return False, detail or "Некорректные данные"
|
||||
|
||||
else:
|
||||
return False, f"Ошибка сервера: {response.status_code}"
|
||||
|
||||
except httpx.RequestError as e:
|
||||
return False, f"Ошибка сети: {e}"
|
||||
except Exception as e:
|
||||
return False, f"Произошла ошибка: {e}"
|
||||
|
||||
@ -1,11 +1,14 @@
|
||||
from PySide6.QtWidgets import QWidget, QVBoxLayout, QListWidget, QLineEdit, QPushButton, QHBoxLayout, QListWidgetItem
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtCore import Qt, Signal
|
||||
from app.core.models.chat_models import MessageItem
|
||||
from app.ui.widgets.message_bubble_widget import MessageBubbleWidget
|
||||
from app.core.theme import theme_manager
|
||||
from uuid import UUID
|
||||
|
||||
class ChatView(QWidget):
|
||||
# Сигнал, который отправляет текст сообщения для отправки
|
||||
send_message_requested = Signal(str)
|
||||
|
||||
def __init__(self, chat_id: UUID, current_user_id: UUID):
|
||||
super().__init__()
|
||||
self.chat_id = chat_id
|
||||
@ -39,6 +42,20 @@ class ChatView(QWidget):
|
||||
main_layout.addWidget(self.message_list)
|
||||
main_layout.addLayout(input_layout)
|
||||
|
||||
# --- Подключение сигналов ---
|
||||
self.send_button.clicked.connect(self._on_send)
|
||||
self.message_input.returnPressed.connect(self._on_send)
|
||||
|
||||
def _on_send(self):
|
||||
"""Обработчик нажатия кнопки отправки."""
|
||||
message_text = self.message_input.text().strip()
|
||||
if message_text:
|
||||
self.send_message_requested.emit(message_text)
|
||||
|
||||
def clear_input(self):
|
||||
"""Очищает поле ввода."""
|
||||
self.message_input.clear()
|
||||
|
||||
def update_theme(self):
|
||||
"""Обновляет стили в соответствии с темой."""
|
||||
palette = theme_manager.get_current_palette()
|
||||
|
||||
@ -16,7 +16,8 @@ from app.core.services.auth_service import get_user_role
|
||||
from app.core.database import get_current_access_token
|
||||
from app.core.localizer import localizer
|
||||
from app.ui.views.chat_view import ChatView
|
||||
from app.core.services.chat_service import get_chat_history
|
||||
from app.core.services.chat_service import get_chat_history, send_private_message
|
||||
from app.core.models.chat_models import PrivateMessageSendRequest, MessageItem
|
||||
from uuid import UUID
|
||||
|
||||
class YobbleHomeView(QWidget):
|
||||
@ -497,6 +498,8 @@ class YobbleHomeView(QWidget):
|
||||
|
||||
self.chat_view = ChatView(chat_id, self.current_user_id)
|
||||
self.chat_view.populate_history(data.items)
|
||||
# Подключаем сигнал отправки сообщения к нашему новому методу
|
||||
self.chat_view.send_message_requested.connect(self.send_message)
|
||||
|
||||
# Заменяем плейсхолдер на реальный виджет
|
||||
self.content_stack.removeWidget(self.chat_view_container)
|
||||
@ -509,6 +512,51 @@ class YobbleHomeView(QWidget):
|
||||
else:
|
||||
self.show_notification(f"Не удалось загрузить историю: {data}", is_error=True)
|
||||
|
||||
def send_message(self, content: str):
|
||||
"""Слот для отправки сообщения. Запускает асинхронную задачу."""
|
||||
if not self.chat_view:
|
||||
return
|
||||
|
||||
chat_id = self.chat_view.chat_id
|
||||
payload = PrivateMessageSendRequest(
|
||||
chat_id=chat_id,
|
||||
content=content,
|
||||
message_type=["text"]
|
||||
)
|
||||
asyncio.create_task(self.do_send_message(payload))
|
||||
|
||||
async def do_send_message(self, payload: PrivateMessageSendRequest):
|
||||
"""Асинхронно отправляет сообщение и обновляет UI."""
|
||||
token = await get_current_access_token()
|
||||
if not token:
|
||||
self.show_notification("Ошибка: сессия не найдена.", is_error=True)
|
||||
return
|
||||
|
||||
success, data = await send_private_message(token, payload)
|
||||
|
||||
if success:
|
||||
# В случае успеха, создаем объект сообщения и добавляем в чат
|
||||
# Используем текущее время, т.к. сервер возвращает время с UTC
|
||||
from datetime import datetime
|
||||
|
||||
new_message = MessageItem(
|
||||
message_id=data.message_id,
|
||||
chat_id=data.chat_id,
|
||||
sender_id=self.current_user_id,
|
||||
content=payload.content,
|
||||
message_type=['text'],
|
||||
is_viewed=True, # Свои сообщения считаем просмотренными
|
||||
created_at=datetime.now(),
|
||||
forward_metadata=None,
|
||||
sender_data=None,
|
||||
media_link=None,
|
||||
updated_at=None
|
||||
)
|
||||
self.chat_view.add_message(new_message)
|
||||
self.chat_view.clear_input() # Очищаем поле ввода
|
||||
else:
|
||||
self.show_notification(f"Ошибка отправки: {data}", is_error=True)
|
||||
|
||||
def close_chat_view(self):
|
||||
"""Закрывает виджет чата и возвращается к списку."""
|
||||
self.content_stack.setCurrentWidget(self.chat_list_view)
|
||||
|
||||
Reference in New Issue
Block a user