patch chat

This commit is contained in:
unknown 2025-09-29 03:12:01 +03:00
parent 229cab0dde
commit 54e4d3542e
3 changed files with 155 additions and 59 deletions

View File

@ -106,6 +106,9 @@ class ChatListView(QWidget):
if chat.chat_type == "self": if chat.chat_type == "self":
companion_name = "Избранное" companion_name = "Избранное"
elif chat.chat_data and 'login' in chat.chat_data: elif chat.chat_data and 'login' in chat.chat_data:
if chat.chat_data['full_name']:
companion_name = chat.chat_data['full_name']
else:
companion_name = chat.chat_data['login'] companion_name = chat.chat_data['login']
else: else:
companion_name = "Неизвестный" companion_name = "Неизвестный"

View File

@ -5,8 +5,9 @@ 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) 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):
@ -18,7 +19,7 @@ class ChatView(QWidget):
theme_manager.theme_changed.connect(self.update_theme) theme_manager.theme_changed.connect(self.update_theme)
def init_ui(self): def init_ui(self):
"""Инициализирует пользовательский интерфейс.""" """Базовая раскладка: список сообщений + поле ввода."""
main_layout = QVBoxLayout(self) main_layout = QVBoxLayout(self)
main_layout.setSpacing(0) main_layout.setSpacing(0)
main_layout.setContentsMargins(0, 0, 0, 0) main_layout.setContentsMargins(0, 0, 0, 0)
@ -32,7 +33,7 @@ class ChatView(QWidget):
input_layout.setContentsMargins(10, 10, 10, 10) input_layout.setContentsMargins(10, 10, 10, 10)
self.message_input = QLineEdit() self.message_input = QLineEdit()
self.message_input.setPlaceholderText("Введите сообщение...") self.message_input.setPlaceholderText("Напишите сообщение…")
self.send_button = QPushButton("Отправить") self.send_button = QPushButton("Отправить")
@ -42,22 +43,22 @@ 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)
# --- Подключение сигналов --- # wire events
self.send_button.clicked.connect(self._on_send) self.send_button.clicked.connect(self._on_send)
self.message_input.returnPressed.connect(self._on_send) self.message_input.returnPressed.connect(self._on_send)
def _on_send(self): def _on_send(self):
"""Обработчик нажатия кнопки отправки.""" """Забрать текст и пробросить через сигнал контроллеру."""
message_text = self.message_input.text().strip() message_text = self.message_input.text().strip()
if message_text: if message_text:
self.send_message_requested.emit(message_text) self.send_message_requested.emit(message_text)
def clear_input(self): def clear_input(self):
"""Очищает поле ввода.""" """Очистить поле ввода после отправки."""
self.message_input.clear() self.message_input.clear()
def update_theme(self): def update_theme(self):
"""Обновляет стили в соответствии с темой.""" """Применить палитру темы к виджетам."""
palette = theme_manager.get_current_palette() palette = theme_manager.get_current_palette()
self.setStyleSheet(f"background-color: {palette['primary']};") self.setStyleSheet(f"background-color: {palette['primary']};")
self.message_list.setStyleSheet(f""" self.message_list.setStyleSheet(f"""
@ -93,19 +94,38 @@ class ChatView(QWidget):
""") """)
def add_message(self, message: MessageItem): def add_message(self, message: MessageItem):
"""Добавляет сообщение в список.""" """Добавить одно сообщение в список."""
is_own = message.sender_id == self.current_user_id is_own = message.sender_id == self.current_user_id
print("debug message", message)
# Имя отправителя для входящих (best-effort)
sender_name = None
if not is_own and getattr(message, 'sender_data', None):
sd = message.sender_data
try:
if isinstance(sd, dict):
sender_name = sd.get('display_name') or sd.get('full_name') or sd.get('name') or sd.get('username')
else:
sender_name = (
getattr(sd, 'display_name', None)
or getattr(sd, 'full_name', None)
or getattr(sd, 'name', None)
or getattr(sd, 'username', None)
)
except Exception:
sender_name = None
bubble = MessageBubbleWidget( bubble = MessageBubbleWidget(
text=message.content, text=message.content,
timestamp=message.created_at.strftime('%H:%M'), timestamp=message.created_at.strftime('%H:%M'),
is_own=is_own is_own=is_own,
sender_name=sender_name
) )
item = QListWidgetItem(self.message_list) item = QListWidgetItem(self.message_list)
item.setSizeHint(bubble.sizeHint()) item.setSizeHint(bubble.sizeHint())
# Выравнивание сообщения # Выравнивание айтема по стороне
if is_own: if is_own:
item.setTextAlignment(Qt.AlignRight) item.setTextAlignment(Qt.AlignRight)
else: else:
@ -116,9 +136,9 @@ class ChatView(QWidget):
self.message_list.scrollToBottom() self.message_list.scrollToBottom()
def populate_history(self, messages: list[MessageItem]): def populate_history(self, messages: list[MessageItem]):
"""Заполняет список сообщениями из истории.""" """Отрисовать историю сообщений (по возрастанию времени)."""
self.message_list.clear() self.message_list.clear()
# Сортируем сообщения по дате (от старых к новым)
messages.sort(key=lambda m: m.created_at) messages.sort(key=lambda m: m.created_at)
for message in messages: for message in messages:
self.add_message(message) self.add_message(message)

View File

@ -1,58 +1,131 @@
from PySide6.QtWidgets import QWidget, QVBoxLayout, QLabel from PySide6.QtWidgets import QWidget, QVBoxLayout, QLabel, QHBoxLayout
from PySide6.QtGui import QPixmap, QPainter, QColor, QBrush
from PySide6.QtCore import Qt from PySide6.QtCore import Qt
from app.core.theme import theme_manager 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): class MessageBubbleWidget(QWidget):
"""Инициализирует пользовательский интерфейс.""" def __init__(
self.layout = QVBoxLayout(self) self,
self.layout.setSpacing(2) text: str,
timestamp: str,
is_own: bool,
sender_name: str | None = None,
avatar_path: str | None = None,
):
super().__init__()
self.is_own = is_own
self.sender_name = sender_name
self.avatar_path = avatar_path
self._build_ui(text, timestamp)
self.update_theme()
# React to theme changes
try:
theme_manager.theme_changed.connect(lambda *_: self.update_theme())
except Exception:
pass
def _build_ui(self, text: str, timestamp: str):
# Main row controls left/right alignment
main_layout = QHBoxLayout(self)
main_layout.setContentsMargins(6, 2, 6, 2)
main_layout.setSpacing(8)
# Avatar placeholder (visible for incoming)
self.avatar_label = QLabel()
self._set_avatar(self.avatar_path)
# Stack with optional sender name and the bubble
stack_layout = QVBoxLayout()
stack_layout.setSpacing(2)
stack_layout.setContentsMargins(0, 0, 0, 0)
# Sender name only for incoming messages (if provided)
self.name_label = QLabel(self.sender_name or "")
self.name_label.setVisible(bool(self.sender_name) and not self.is_own)
# Bubble frame holds text and timestamp
self.bubble_frame = QWidget()
self.bubble_frame.setObjectName("Bubble")
bubble_layout = QVBoxLayout(self.bubble_frame)
bubble_layout.setContentsMargins(12, 8, 12, 6)
bubble_layout.setSpacing(3)
self.text_label = QLabel(text) self.text_label = QLabel(text)
self.text_label.setWordWrap(True) self.text_label.setWordWrap(True)
self.timestamp_label = QLabel(timestamp) self.timestamp_label = QLabel(timestamp)
self.timestamp_label.setAlignment(Qt.AlignRight)
self.layout.addWidget(self.text_label) bubble_layout.addWidget(self.text_label)
self.layout.addWidget(self.timestamp_label) bubble_layout.addWidget(self.timestamp_label, 0, Qt.AlignRight if self.is_own else Qt.AlignLeft)
self.is_own = is_own stack_layout.addWidget(self.name_label)
self.setObjectName("MessageBubble") stack_layout.addWidget(self.bubble_frame)
self.update_theme()
# Arrange horizontally based on ownership: right for own, left for others
if self.is_own:
main_layout.addStretch(1)
main_layout.addLayout(stack_layout)
# Reserve place for avatar on the right, hidden for own
self.avatar_label.setVisible(False)
main_layout.addWidget(self.avatar_label)
else:
main_layout.addWidget(self.avatar_label)
main_layout.addLayout(stack_layout)
main_layout.addStretch(1)
# Limit bubble width for readability
self.bubble_frame.setMaximumWidth(420)
def _set_avatar(self, image_path: str | None):
size = 32
if image_path:
pixmap = QPixmap(image_path)
if pixmap.isNull():
pixmap = QPixmap(size, size)
else:
pixmap = QPixmap(size, size)
if pixmap.width() != size or pixmap.height() != size:
pixmap = pixmap.scaled(size, size, Qt.KeepAspectRatioByExpanding, Qt.SmoothTransformation)
# Draw a simple circular placeholder if no image
if not image_path:
pixmap.fill(Qt.transparent)
painter = QPainter(pixmap)
painter.setRenderHint(QPainter.Antialiasing)
painter.setBrush(QBrush(QColor("#e0e0e0")))
painter.setPen(Qt.NoPen)
painter.drawEllipse(0, 0, size, size)
painter.end()
self.avatar_label.setPixmap(pixmap)
self.avatar_label.setFixedSize(size, size)
self.avatar_label.setScaledContents(True)
def update_theme(self): def update_theme(self):
"""Обновляет стили виджета в соответствии с текущей темой."""
palette = theme_manager.get_current_palette() palette = theme_manager.get_current_palette()
if self.is_own: if self.is_own:
bg_color = palette['accent'] bg_color = palette["accent"]
text_color = "#ffffff" text_color = "#ffffff"
timestamp_color = "#dddddd" timestamp_color = "#e6e6e6"
alignment = Qt.AlignRight name_color = palette["text_secondary"]
ts_align = Qt.AlignRight
else: else:
bg_color = palette['secondary'] bg_color = palette["secondary"]
text_color = palette['text'] text_color = palette["text"]
timestamp_color = palette['text_secondary'] timestamp_color = palette["text_secondary"]
alignment = Qt.AlignLeft name_color = palette["text_secondary"]
ts_align = Qt.AlignLeft
self.setStyleSheet(f""" # Style only the bubble frame
#MessageBubble {{ self.bubble_frame.setStyleSheet(
background-color: {bg_color}; f"#Bubble {{ background-color: {bg_color}; border-radius: 10px; }}"
border-radius: 10px; )
padding: 8px 12px;
}}
""")
self.text_label.setStyleSheet(f"color: {text_color}; font-size: 10pt;") self.text_label.setStyleSheet(f"color: {text_color}; font-size: 10pt;")
self.timestamp_label.setStyleSheet(f"color: {timestamp_color}; font-size: 8pt;") self.timestamp_label.setStyleSheet(f"color: {timestamp_color}; font-size: 8pt;")
self.name_label.setStyleSheet(f"color: {name_color}; font-size: 8pt; font-weight: 600;")
# Устанавливаем выравнивание для всего layout # Keep timestamp alignment in sync with side
parent_layout = self.parentWidget().layout() if self.parentWidget() else None self.timestamp_label.setAlignment(ts_align)
if parent_layout:
# Этот способ не сработает напрямую, выравнивание нужно делать в QListWidgetItem
pass