patch chat
This commit is contained in:
parent
96fe4d0b91
commit
229cab0dde
@ -2,7 +2,7 @@ DEBUG = True
|
|||||||
API_SCHEME = "https"
|
API_SCHEME = "https"
|
||||||
API_HOST = "api.yobble.org"
|
API_HOST = "api.yobble.org"
|
||||||
BASE_URL = f"{API_SCHEME}://{API_HOST}"
|
BASE_URL = f"{API_SCHEME}://{API_HOST}"
|
||||||
APP_VERSION = "0.2_home_screen"
|
APP_VERSION = "0.3_chat"
|
||||||
APP_NAME = "yobble messenger"
|
APP_NAME = "yobble messenger"
|
||||||
APP_HEADER = f"{APP_NAME}"
|
APP_HEADER = f"{APP_NAME}"
|
||||||
if DEBUG: APP_HEADER=f"{APP_HEADER} ({APP_VERSION})"
|
if DEBUG: APP_HEADER=f"{APP_HEADER} ({APP_VERSION})"
|
||||||
|
|||||||
@ -56,3 +56,23 @@ class PrivateChatHistoryData(BaseModel):
|
|||||||
class PrivateChatHistoryResponse(BaseModel):
|
class PrivateChatHistoryResponse(BaseModel):
|
||||||
status: str
|
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
|
import httpx
|
||||||
from app.core import config
|
from app.core import config
|
||||||
from app.core.localizer import localizer
|
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
|
from uuid import UUID
|
||||||
|
|
||||||
async def get_private_chats(token: str, offset: int = 0, limit: int = 20):
|
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}"
|
return False, f"Ошибка сети: {e}"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return False, f"Произошла ошибка: {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.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.core.models.chat_models import MessageItem
|
||||||
from app.ui.widgets.message_bubble_widget import MessageBubbleWidget
|
from app.ui.widgets.message_bubble_widget import MessageBubbleWidget
|
||||||
from app.core.theme import theme_manager
|
from app.core.theme import theme_manager
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
class ChatView(QWidget):
|
class ChatView(QWidget):
|
||||||
|
# Сигнал, который отправляет текст сообщения для отправки
|
||||||
|
send_message_requested = Signal(str)
|
||||||
|
|
||||||
def __init__(self, chat_id: UUID, current_user_id: UUID):
|
def __init__(self, chat_id: UUID, current_user_id: UUID):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.chat_id = chat_id
|
self.chat_id = chat_id
|
||||||
@ -39,6 +42,20 @@ class ChatView(QWidget):
|
|||||||
main_layout.addWidget(self.message_list)
|
main_layout.addWidget(self.message_list)
|
||||||
main_layout.addLayout(input_layout)
|
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):
|
def update_theme(self):
|
||||||
"""Обновляет стили в соответствии с темой."""
|
"""Обновляет стили в соответствии с темой."""
|
||||||
palette = theme_manager.get_current_palette()
|
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.database import get_current_access_token
|
||||||
from app.core.localizer import localizer
|
from app.core.localizer import localizer
|
||||||
from app.ui.views.chat_view import ChatView
|
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
|
from uuid import UUID
|
||||||
|
|
||||||
class YobbleHomeView(QWidget):
|
class YobbleHomeView(QWidget):
|
||||||
@ -497,6 +498,8 @@ class YobbleHomeView(QWidget):
|
|||||||
|
|
||||||
self.chat_view = ChatView(chat_id, self.current_user_id)
|
self.chat_view = ChatView(chat_id, self.current_user_id)
|
||||||
self.chat_view.populate_history(data.items)
|
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)
|
self.content_stack.removeWidget(self.chat_view_container)
|
||||||
@ -509,6 +512,51 @@ class YobbleHomeView(QWidget):
|
|||||||
else:
|
else:
|
||||||
self.show_notification(f"Не удалось загрузить историю: {data}", is_error=True)
|
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):
|
def close_chat_view(self):
|
||||||
"""Закрывает виджет чата и возвращается к списку."""
|
"""Закрывает виджет чата и возвращается к списку."""
|
||||||
self.content_stack.setCurrentWidget(self.chat_list_view)
|
self.content_stack.setCurrentWidget(self.chat_list_view)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user