patch chat

This commit is contained in:
unknown 2025-09-29 02:53:52 +03:00
parent 96fe4d0b91
commit 229cab0dde
5 changed files with 139 additions and 5 deletions

View File

@ -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})"

View File

@ -56,3 +56,23 @@ class PrivateChatHistoryData(BaseModel):
class PrivateChatHistoryResponse(BaseModel):
status: str
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

View File

@ -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}"

View File

@ -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()

View File

@ -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)