Compare commits

..

No commits in common. "229cab0dde142783fea24525be94b6a88834329c" and "63c07305da866c1980478ca2b074f4ff374fe05a" have entirely different histories.

10 changed files with 34 additions and 461 deletions

View File

@ -45,20 +45,13 @@ class MainController(QStackedWidget):
def handle_login_success(self, username: str): def handle_login_success(self, username: str):
"""Обрабатывает успешный вход в систему.""" """Обрабатывает успешный вход в систему."""
set_last_login(username) set_last_login(username)
session = get_session(username)
user_id = session["user_id"] if session else None
if not user_id:
self.show_login()
self.notification_requested.emit("Не удалось получить ID пользователя из сессии.", True)
return
if self.login_view: if self.login_view:
self.login_view.close() self.login_view.close()
self.removeWidget(self.login_view) self.removeWidget(self.login_view)
self.login_view = None self.login_view = None
self.yobble_home_view = YobbleHomeView(username=username, current_user_id=user_id) self.yobble_home_view = YobbleHomeView(username=username)
# Подключаем сигналы к слотам в YobbleHomeView # Подключаем сигналы к слотам в YobbleHomeView
self.notification_requested.connect(self.yobble_home_view.show_notification) self.notification_requested.connect(self.yobble_home_view.show_notification)

View File

@ -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.3_chat" APP_VERSION = "0.2_home_screen"
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})"

View File

@ -28,7 +28,6 @@ def init_db():
cursor.execute(''' cursor.execute('''
CREATE TABLE IF NOT EXISTS sessions ( CREATE TABLE IF NOT EXISTS sessions (
login TEXT PRIMARY KEY, login TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
access_token TEXT NOT NULL, access_token TEXT NOT NULL,
refresh_token TEXT NOT NULL, refresh_token TEXT NOT NULL,
created_at TIMESTAMP NOT NULL created_at TIMESTAMP NOT NULL
@ -51,12 +50,10 @@ def init_db():
conn.commit() conn.commit()
conn.close() conn.close()
def add_session(login, access_token, refresh_token, user_id=None, update_existing=False): def add_session(login, access_token, refresh_token, update_existing=False):
"""Добавляет новую сессию или обновляет существующую.""" """Добавляет новую сессию или обновляет существующую."""
conn = get_connection() conn = get_connection()
cursor = conn.cursor() cursor = conn.cursor()
print("ffff", login, access_token, refresh_token, user_id, update_existing)
if update_existing: if update_existing:
# Обновляем существующую сессию по access_token # Обновляем существующую сессию по access_token
@ -68,9 +65,9 @@ def add_session(login, access_token, refresh_token, user_id=None, update_existin
else: else:
# Вставляем новую или заменяем существующую по логину # Вставляем новую или заменяем существующую по логину
cursor.execute(''' cursor.execute('''
INSERT OR REPLACE INTO sessions (login, user_id, access_token, refresh_token, created_at) INSERT OR REPLACE INTO sessions (login, access_token, refresh_token, created_at)
VALUES (?, ?, ?, ?, ?) VALUES (?, ?, ?, ?)
''', (login, user_id, access_token, refresh_token, datetime.now())) ''', (login, access_token, refresh_token, datetime.now()))
conn.commit() conn.commit()
conn.close() conn.close()

View File

@ -44,35 +44,4 @@ class PrivateChatListData(BaseModel):
class PrivateChatListResponse(BaseModel): class PrivateChatListResponse(BaseModel):
status: str status: str
data: PrivateChatListData data: PrivateChatListData
# history
class PrivateChatHistoryData(BaseModel):
items: List[MessageItem]
has_more: bool
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

@ -25,8 +25,7 @@ async def login(login, password):
add_session( add_session(
login=login, login=login,
access_token=token_data["access_token"], access_token=token_data["access_token"],
refresh_token=token_data["refresh_token"], refresh_token=token_data["refresh_token"]
user_id=token_data["user_id"]
) )
return True, localizer.translate("Успешный вход") return True, localizer.translate("Успешный вход")
else: else:

View File

@ -1,12 +1,7 @@
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 ( from app.core.models.chat_models import PrivateChatListResponse, PrivateChatListData
PrivateChatListResponse, PrivateChatListData,
PrivateChatHistoryResponse, PrivateChatHistoryData,
PrivateMessageSendRequest, PrivateMessageSendResponse
)
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):
""" """
@ -51,91 +46,3 @@ async def get_private_chats(token: str, offset: int = 0, limit: int = 20):
return False, f"{localizer.translate('Ошибка сети')}: {e}" return False, f"{localizer.translate('Ошибка сети')}: {e}"
except Exception as e: except Exception as e:
return False, f"{localizer.translate('Произошла ошибка')}: {e}" return False, f"{localizer.translate('Произошла ошибка')}: {e}"
async def get_chat_history(token: str, chat_id: UUID, before_message_id: int = None, limit: int = 30):
"""
Получает историю сообщений для указанного приватного чата.
:param token: Токен доступа
:param chat_id: ID чата
:param before_message_id: ID сообщения для пагинации (загрузка более старых)
:param limit: Количество сообщений для загрузки
:return: Кортеж (успех: bool, данные: PrivateChatHistoryData | str)
"""
url = f"{config.BASE_URL}/v1/chat/private/history"
headers = {"Authorization": f"Bearer {token}"}
params = {"chat_id": str(chat_id), "limit": limit}
if before_message_id:
params["before_message_id"] = before_message_id
try:
async with httpx.AsyncClient(http2=True) as client:
response = await client.get(url, headers=headers, params=params)
if response.status_code == 200:
data = response.json()
if data.get("status") == "fine":
response_model = PrivateChatHistoryResponse(**data)
return True, response_model.data
else:
return False, data.get("detail", "Неизвестная ошибка ответа")
elif response.status_code in [401, 403]:
error_data = response.json()
return False, error_data.get("detail", "Ошибка аутентификации или доступа")
elif response.status_code == 422:
return False, "Некорректные параметры запроса"
else:
return False, f"Ошибка сервера: {response.status_code}"
except httpx.RequestError as e:
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,18 +1,14 @@
from PySide6.QtWidgets import QWidget, QListWidget, QVBoxLayout, QListWidgetItem from PySide6.QtWidgets import QWidget, QListWidget, QVBoxLayout, QListWidgetItem
from PySide6.QtCore import Qt, QSize, Signal from PySide6.QtCore import Qt, QSize
from typing import List from typing import List
from app.core.models.chat_models import PrivateChatListItem from app.core.models.chat_models import PrivateChatListItem
from app.ui.widgets.chat_list_item_widget import ChatListItemWidget from app.ui.widgets.chat_list_item_widget import ChatListItemWidget
from app.core.theme import theme_manager from app.core.theme import theme_manager
from datetime import datetime from datetime import datetime
from uuid import UUID
class ChatListView(QWidget): class ChatListView(QWidget):
chat_selected = Signal(UUID)
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.chat_items_map = {}
self.init_ui() self.init_ui()
self.update_theme() self.update_theme()
theme_manager.theme_changed.connect(self.update_theme) theme_manager.theme_changed.connect(self.update_theme)
@ -25,18 +21,11 @@ class ChatListView(QWidget):
self.chat_list = QListWidget() self.chat_list = QListWidget()
self.chat_list.setSpacing(2) self.chat_list.setSpacing(2)
self.chat_list.itemClicked.connect(self.on_chat_item_clicked)
layout.addWidget(self.chat_list) layout.addWidget(self.chat_list)
# Изначальное состояние # Изначальное состояние
self.show_placeholder_message("Загрузка чатов...") self.show_placeholder_message("Загрузка чатов...")
def on_chat_item_clicked(self, item: QListWidgetItem):
"""Обработчик клика по элементу списка чатов."""
chat_id = self.chat_items_map.get(id(item))
if chat_id:
self.chat_selected.emit(chat_id)
def update_theme(self): def update_theme(self):
"""Обновляет стили в соответствии с темой.""" """Обновляет стили в соответствии с темой."""
palette = theme_manager.get_current_palette() palette = theme_manager.get_current_palette()
@ -92,7 +81,6 @@ class ChatListView(QWidget):
Заполняет список чатов данными, полученными от сервера. Заполняет список чатов данными, полученными от сервера.
""" """
self.chat_list.clear() self.chat_list.clear()
self.chat_items_map.clear()
if not chat_items: if not chat_items:
self.show_placeholder_message("У вас пока нет чатов") self.show_placeholder_message("У вас пока нет чатов")
@ -129,6 +117,3 @@ class ChatListView(QWidget):
list_item.setSizeHint(item_widget.sizeHint()) list_item.setSizeHint(item_widget.sizeHint())
self.chat_list.addItem(list_item) self.chat_list.addItem(list_item)
self.chat_list.setItemWidget(list_item, item_widget) self.chat_list.setItemWidget(list_item, item_widget)
# Сохраняем ID чата в словаре
self.chat_items_map[id(list_item)] = chat.chat_id

View File

@ -1,124 +0,0 @@
from PySide6.QtWidgets import QWidget, QVBoxLayout, QListWidget, QLineEdit, QPushButton, QHBoxLayout, QListWidgetItem
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
self.current_user_id = current_user_id
self.init_ui()
self.update_theme()
theme_manager.theme_changed.connect(self.update_theme)
def init_ui(self):
"""Инициализирует пользовательский интерфейс."""
main_layout = QVBoxLayout(self)
main_layout.setSpacing(0)
main_layout.setContentsMargins(0, 0, 0, 0)
self.message_list = QListWidget()
self.message_list.setSpacing(10)
self.message_list.setWordWrap(True)
input_layout = QHBoxLayout()
input_layout.setSpacing(10)
input_layout.setContentsMargins(10, 10, 10, 10)
self.message_input = QLineEdit()
self.message_input.setPlaceholderText("Введите сообщение...")
self.send_button = QPushButton("Отправить")
input_layout.addWidget(self.message_input)
input_layout.addWidget(self.send_button)
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()
self.setStyleSheet(f"background-color: {palette['primary']};")
self.message_list.setStyleSheet(f"""
QListWidget {{
background-color: {palette['primary']};
border: none;
padding: 10px;
}}
""")
self.message_input.setStyleSheet(f"""
QLineEdit {{
background-color: {palette['secondary']};
color: {palette['text']};
border: 1px solid {palette['border']};
border-radius: 15px;
padding: 5px 15px;
font-size: 10pt;
}}
""")
self.send_button.setStyleSheet(f"""
QPushButton {{
background-color: {palette['accent']};
color: #ffffff;
border: none;
border-radius: 15px;
padding: 5px 15px;
font-size: 10pt;
font-weight: bold;
}}
QPushButton:hover {{
background-color: #4a8ac0;
}}
""")
def add_message(self, message: MessageItem):
"""Добавляет сообщение в список."""
is_own = message.sender_id == self.current_user_id
bubble = MessageBubbleWidget(
text=message.content,
timestamp=message.created_at.strftime('%H:%M'),
is_own=is_own
)
item = QListWidgetItem(self.message_list)
item.setSizeHint(bubble.sizeHint())
# Выравнивание сообщения
if is_own:
item.setTextAlignment(Qt.AlignRight)
else:
item.setTextAlignment(Qt.AlignLeft)
self.message_list.addItem(item)
self.message_list.setItemWidget(item, bubble)
self.message_list.scrollToBottom()
def populate_history(self, messages: list[MessageItem]):
"""Заполняет список сообщениями из истории."""
self.message_list.clear()
# Сортируем сообщения по дате (от старых к новым)
messages.sort(key=lambda m: m.created_at)
for message in messages:
self.add_message(message)

View File

@ -15,10 +15,6 @@ from app.ui.views.chat_list_view import ChatListView
from app.core.services.auth_service import get_user_role 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.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): class YobbleHomeView(QWidget):
REQUIRED_PERMISSIONS = { REQUIRED_PERMISSIONS = {
@ -26,17 +22,15 @@ class YobbleHomeView(QWidget):
1: "music.access" # Музыка 1: "music.access" # Музыка
} }
def __init__(self, username: str, current_user_id: UUID): def __init__(self, username: str):
super().__init__() super().__init__()
self.username = username self.username = username
self.current_user_id = current_user_id
self.setWindowTitle(f"Yobble Home - {username}") self.setWindowTitle(f"Yobble Home - {username}")
self.setMinimumSize(360, 640) self.setMinimumSize(360, 640)
self.permission_cache = set() self.permission_cache = set()
self.permissions_preloaded = False self.permissions_preloaded = False
self.permissions_preloaded = False self.permissions_preloaded = False
self.permissions_preloaded_last = 0.0 self.permissions_preloaded_last = 0.0
self.chat_view = None # Placeholder for the chat view
# --- Основной макет --- # --- Основной макет ---
# Используем QHBoxLayout, чтобы можно было разместить меню и контент рядом # Используем QHBoxLayout, чтобы можно было разместить меню и контент рядом
@ -243,14 +237,6 @@ class YobbleHomeView(QWidget):
top_bar_layout = QHBoxLayout(top_bar_widget) top_bar_layout = QHBoxLayout(top_bar_widget)
top_bar_layout.setContentsMargins(10, 5, 10, 5) top_bar_layout.setContentsMargins(10, 5, 10, 5)
self.back_button = QPushButton("")
self.back_button.setObjectName("BackButton")
self.back_button.setFocusPolicy(Qt.NoFocus)
self.back_button.setCursor(Qt.PointingHandCursor)
self.back_button.clicked.connect(self.close_chat_view)
self.back_button.hide()
top_bar_layout.addWidget(self.back_button)
self.burger_menu_button = QPushButton("") self.burger_menu_button = QPushButton("")
self.burger_menu_button.setObjectName("BurgerMenuButton") self.burger_menu_button.setObjectName("BurgerMenuButton")
self.burger_menu_button.setFocusPolicy(Qt.NoFocus) self.burger_menu_button.setFocusPolicy(Qt.NoFocus)
@ -279,6 +265,16 @@ class YobbleHomeView(QWidget):
top_bar_layout.addWidget(self.notification_button) top_bar_layout.addWidget(self.notification_button)
self.notification_button.clicked.connect(self.handle_notification_click) self.notification_button.clicked.connect(self.handle_notification_click)
# --- Временные кнопки для теста ---
# self.test_ok_button = QPushButton("Test OK")
# self.test_ok_button.clicked.connect(lambda: self.show_notification("Операция прошла успешно", is_error=False))
# top_bar_layout.addWidget(self.test_ok_button)
# self.test_err_button = QPushButton("Test Error")
# self.test_err_button.clicked.connect(lambda: self.show_notification("Произошла ошибка при обновлении", is_error=True))
# top_bar_layout.addWidget(self.test_err_button)
# --- Конец временного кода ---
return top_bar_widget return top_bar_widget
def create_bottom_bar(self): def create_bottom_bar(self):
@ -372,16 +368,11 @@ class YobbleHomeView(QWidget):
# Чаты # Чаты
self.chat_list_view = ChatListView() self.chat_list_view = ChatListView()
self.chat_list_view.chat_selected.connect(self.open_chat_view)
self.content_stack.addWidget(self.chat_list_view) self.content_stack.addWidget(self.chat_list_view)
# Профиль # Профиль
self.content_stack.addWidget(QLabel("Контент Профиля")) self.content_stack.addWidget(QLabel("Контент Профиля"))
# Placeholder для открытого чата
self.chat_view_container = QWidget()
self.content_stack.addWidget(self.chat_view_container)
def update_chat_list(self, chat_data): def update_chat_list(self, chat_data):
""" """
Слот для обновления списка чатов. Слот для обновления списка чатов.
@ -438,6 +429,19 @@ class YobbleHomeView(QWidget):
raise ConnectionError(data) raise ConnectionError(data)
return success, data return success, data
# def update_current_tab_content(self):
# """Обновляет контент текущей активной вкладки после предзагрузки прав."""
# current_index = self.content_stack.currentIndex()
# # Проверяем, требует ли текущая вкладка прав доступа
# if current_index in self.REQUIRED_PERMISSIONS:
# permission_code = self.REQUIRED_PERMISSIONS[current_index]
# if permission_code in self.permission_cache:
# self.show_real_content(current_index)
# else:
# self.show_denied(current_index)
async def check_permissions_and_switch(self, index, permission_code): async def check_permissions_and_switch(self, index, permission_code):
"""Асинхронно проверяет права и переключает вкладку.""" """Асинхронно проверяет права и переключает вкладку."""
self.preload_permissions_first = False self.preload_permissions_first = False
@ -476,101 +480,6 @@ class YobbleHomeView(QWidget):
titles = ["Лента", "Музыка", "Чаты", "Лицо"] titles = ["Лента", "Музыка", "Чаты", "Лицо"]
self.title_label.setText(titles[index]) self.title_label.setText(titles[index])
def open_chat_view(self, chat_id: UUID):
"""Открывает виджет чата, загружая его историю."""
print(f"Opening chat: {chat_id}")
asyncio.create_task(self.load_and_display_chat(chat_id))
async def load_and_display_chat(self, chat_id: UUID):
"""Асинхронно загружает историю и отображает чат."""
token = await get_current_access_token()
if not token:
self.show_notification("Ошибка: сессия не найдена.", is_error=True)
return
self.show_notification("Загрузка истории...", is_error=False, duration=1000)
success, data = await get_chat_history(token, chat_id)
if success:
if self.chat_view:
self.content_stack.removeWidget(self.chat_view)
self.chat_view.deleteLater()
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)
self.content_stack.addWidget(self.chat_view)
self.content_stack.setCurrentWidget(self.chat_view)
self.bottom_bar.hide()
self.burger_menu_button.hide()
self.back_button.show()
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)
self.bottom_bar.show()
self.burger_menu_button.show()
self.back_button.hide()
if self.chat_view:
self.content_stack.removeWidget(self.chat_view)
self.chat_view.deleteLater()
self.chat_view = None
# Возвращаем плейсхолдер
self.chat_view_container = QWidget()
self.content_stack.addWidget(self.chat_view_container)
def show_error_message(self, message): def show_error_message(self, message):
"""Показывает всплывающее уведомление об ошибке.""" """Показывает всплывающее уведомление об ошибке."""
self.show_notification(message, is_error=True) self.show_notification(message, is_error=True)
@ -706,10 +615,6 @@ class YobbleHomeView(QWidget):
color: {title_color}; color: {title_color};
background: transparent; background: transparent;
}} }}
#BackButton {{
font-size: 28px;
font-weight: bold;
}}
#TitleLabel {{ #TitleLabel {{
font-size: 18px; font-size: 18px;
font-weight: bold; font-weight: bold;

View File

@ -1,58 +0,0 @@
from PySide6.QtWidgets import QWidget, QVBoxLayout, QLabel
from PySide6.QtCore import Qt
from app.core.theme import theme_manager
class MessageBubbleWidget(QWidget):
def __init__(self, text: str, timestamp: str, is_own: bool):
super().__init__()
self.init_ui(text, timestamp, is_own)
self.update_theme()
def init_ui(self, text: str, timestamp: str, is_own: bool):
"""Инициализирует пользовательский интерфейс."""
self.layout = QVBoxLayout(self)
self.layout.setSpacing(2)
self.text_label = QLabel(text)
self.text_label.setWordWrap(True)
self.timestamp_label = QLabel(timestamp)
self.timestamp_label.setAlignment(Qt.AlignRight)
self.layout.addWidget(self.text_label)
self.layout.addWidget(self.timestamp_label)
self.is_own = is_own
self.setObjectName("MessageBubble")
self.update_theme()
def update_theme(self):
"""Обновляет стили виджета в соответствии с текущей темой."""
palette = theme_manager.get_current_palette()
if self.is_own:
bg_color = palette['accent']
text_color = "#ffffff"
timestamp_color = "#dddddd"
alignment = Qt.AlignRight
else:
bg_color = palette['secondary']
text_color = palette['text']
timestamp_color = palette['text_secondary']
alignment = Qt.AlignLeft
self.setStyleSheet(f"""
#MessageBubble {{
background-color: {bg_color};
border-radius: 10px;
padding: 8px 12px;
}}
""")
self.text_label.setStyleSheet(f"color: {text_color}; font-size: 10pt;")
self.timestamp_label.setStyleSheet(f"color: {timestamp_color}; font-size: 8pt;")
# Устанавливаем выравнивание для всего layout
parent_layout = self.parentWidget().layout() if self.parentWidget() else None
if parent_layout:
# Этот способ не сработает напрямую, выравнивание нужно делать в QListWidgetItem
pass