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)
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user