diff --git a/app/core/models/search_models.py b/app/core/models/search_models.py new file mode 100644 index 0000000..cccd671 --- /dev/null +++ b/app/core/models/search_models.py @@ -0,0 +1,26 @@ +from uuid import UUID +from datetime import datetime +from typing import Optional, List, Any +from pydantic import BaseModel, Field + + +class UserSearchResult(BaseModel): + user_id: UUID + login: str + full_name: Optional[str] = None + custom_name: Optional[str] = None + created_at: datetime = Field(..., description="Дата регистрации") + profile: Optional[Any] = Field(None, description="Модель как у /profile/{user_id}") + + +class SearchData(BaseModel): + users: List[UserSearchResult] + groups: List[Any] = [] + channels: List[Any] = [] + messages: List[Any] = [] + + +class SearchResponse(BaseModel): + status: str + data: SearchData + diff --git a/app/core/services/search_service.py b/app/core/services/search_service.py new file mode 100644 index 0000000..6ff315f --- /dev/null +++ b/app/core/services/search_service.py @@ -0,0 +1,42 @@ +import httpx +from app.core import config +from app.core.localizer import localizer +from app.core.http_client import authorized_get +from app.core.models.search_models import SearchResponse, SearchData + + +async def search_by_query(login: str, token: str, query: str): + """ + Поиск по строке: пользователи (префикс), далее можно расширить под группы/каналы/сообщения. + + :return: tuple (ok: bool, data: SearchData | str) + """ + url = f"{config.BASE_URL}/v1/feed/search" + headers = {"Authorization": f"Bearer {token}"} + params = {"query": query} + + try: + response = await authorized_get(url, login=login, access_token=token, headers=headers, params=params) + + print("response.status_code", response.status_code) + if response.status_code == 200: + data = response.json() + if data.get("status") == "fine": + model = SearchResponse(**data) + return True, model.data + return False, data.get("detail", localizer.translate("Произошла ошибка")) + + if response.status_code in [401, 403]: + error_data = response.json() + return False, error_data.get("detail", localizer.translate("Недостаточно прав или неавторизован")) + + if response.status_code == 422: + return False, localizer.translate("Некорректные параметры запроса") + + return False, f"{localizer.translate('Неожиданный статус')}: {response.status_code}" + + except httpx.RequestError as e: + return False, f"{localizer.translate('Сетевая ошибка')}: {e}" + except Exception as e: + return False, f"{localizer.translate('Внутренняя ошибка')}: {e}" + diff --git a/app/ui/views/yobble_home_view.py b/app/ui/views/yobble_home_view.py index 022cf7f..c8bb9f8 100644 --- a/app/ui/views/yobble_home_view.py +++ b/app/ui/views/yobble_home_view.py @@ -20,6 +20,7 @@ from app.ui.views.chat_view import ChatView 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 app.core.services.search_service import search_by_query class YobbleHomeView(QWidget): REQUIRED_PERMISSIONS = { @@ -271,6 +272,8 @@ class YobbleHomeView(QWidget): self.search_input.setPlaceholderText("Поиск…") self.search_input.hide() top_bar_layout.addWidget(self.search_input, 1) + # Запуск поиска по Enter + self.search_input.returnPressed.connect(self.on_search_submit) # Новые кнопки справа self.search_button = QPushButton("🔍") @@ -664,6 +667,38 @@ class YobbleHomeView(QWidget): self.search_button.show() self.notification_button.show() + def on_search_submit(self): + """Обработчик Enter в поле поиска.""" + query = (self.search_input.text() or "").strip() + if len(query) < 1: + self.show_notification(localizer.translate("Введите минимум 1 символ"), is_error=True) + return + # Запускаем асинхронный поиск + asyncio.ensure_future(self._do_search(query)) + + async def _do_search(self, query: str): + """Вызов серверного поиска и показ краткого результата.""" + # Получаем текущие учётные данные + login = self.username + token = await get_current_access_token() + if not token: + self.show_notification(localizer.translate("Не найден токен авторизации"), is_error=True) + return + + ok, data_or_error = await search_by_query(login, token, query) + if not ok: + self.show_notification(str(data_or_error), is_error=True) + return + + data = data_or_error + users_cnt = len(getattr(data, 'users', []) or []) + groups_cnt = len(getattr(data, 'groups', []) or []) + channels_cnt = len(getattr(data, 'channels', []) or []) + messages_cnt = len(getattr(data, 'messages', []) or []) + self.show_notification( + localizer.translate(f"Найдено: пользователи {users_cnt}, беседы {groups_cnt}, паблики {channels_cnt}, сообщения {messages_cnt}") + ) + def handle_notification_click(self): """Пустышка для кнопки уведомлений.""" show_themed_messagebox(