patch chat
This commit is contained in:
parent
229cab0dde
commit
54e4d3542e
@ -106,7 +106,10 @@ class ChatListView(QWidget):
|
||||
if chat.chat_type == "self":
|
||||
companion_name = "Избранное"
|
||||
elif chat.chat_data and 'login' in chat.chat_data:
|
||||
companion_name = chat.chat_data['login']
|
||||
if chat.chat_data['full_name']:
|
||||
companion_name = chat.chat_data['full_name']
|
||||
else:
|
||||
companion_name = chat.chat_data['login']
|
||||
else:
|
||||
companion_name = "Неизвестный"
|
||||
|
||||
|
||||
@ -5,8 +5,9 @@ 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):
|
||||
@ -18,7 +19,7 @@ class ChatView(QWidget):
|
||||
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)
|
||||
@ -32,7 +33,7 @@ class ChatView(QWidget):
|
||||
input_layout.setContentsMargins(10, 10, 10, 10)
|
||||
|
||||
self.message_input = QLineEdit()
|
||||
self.message_input.setPlaceholderText("Введите сообщение...")
|
||||
self.message_input.setPlaceholderText("Напишите сообщение…")
|
||||
|
||||
self.send_button = QPushButton("Отправить")
|
||||
|
||||
@ -42,22 +43,22 @@ class ChatView(QWidget):
|
||||
main_layout.addWidget(self.message_list)
|
||||
main_layout.addLayout(input_layout)
|
||||
|
||||
# --- Подключение сигналов ---
|
||||
# wire events
|
||||
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"""
|
||||
@ -93,19 +94,38 @@ class ChatView(QWidget):
|
||||
""")
|
||||
|
||||
def add_message(self, message: MessageItem):
|
||||
"""Добавляет сообщение в список."""
|
||||
"""Добавить одно сообщение в список."""
|
||||
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(
|
||||
text=message.content,
|
||||
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.setSizeHint(bubble.sizeHint())
|
||||
|
||||
# Выравнивание сообщения
|
||||
# Выравнивание айтема по стороне
|
||||
if is_own:
|
||||
item.setTextAlignment(Qt.AlignRight)
|
||||
else:
|
||||
@ -116,9 +136,9 @@ class ChatView(QWidget):
|
||||
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)
|
||||
|
||||
|
||||
@ -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 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)
|
||||
class MessageBubbleWidget(QWidget):
|
||||
def __init__(
|
||||
self,
|
||||
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.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)
|
||||
bubble_layout.addWidget(self.text_label)
|
||||
bubble_layout.addWidget(self.timestamp_label, 0, Qt.AlignRight if self.is_own else Qt.AlignLeft)
|
||||
|
||||
self.is_own = is_own
|
||||
self.setObjectName("MessageBubble")
|
||||
self.update_theme()
|
||||
stack_layout.addWidget(self.name_label)
|
||||
stack_layout.addWidget(self.bubble_frame)
|
||||
|
||||
# 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):
|
||||
"""Обновляет стили виджета в соответствии с текущей темой."""
|
||||
palette = theme_manager.get_current_palette()
|
||||
|
||||
if self.is_own:
|
||||
bg_color = palette['accent']
|
||||
bg_color = palette["accent"]
|
||||
text_color = "#ffffff"
|
||||
timestamp_color = "#dddddd"
|
||||
alignment = Qt.AlignRight
|
||||
timestamp_color = "#e6e6e6"
|
||||
name_color = palette["text_secondary"]
|
||||
ts_align = Qt.AlignRight
|
||||
else:
|
||||
bg_color = palette['secondary']
|
||||
text_color = palette['text']
|
||||
timestamp_color = palette['text_secondary']
|
||||
alignment = Qt.AlignLeft
|
||||
bg_color = palette["secondary"]
|
||||
text_color = palette["text"]
|
||||
timestamp_color = palette["text_secondary"]
|
||||
name_color = palette["text_secondary"]
|
||||
ts_align = Qt.AlignLeft
|
||||
|
||||
self.setStyleSheet(f"""
|
||||
#MessageBubble {{
|
||||
background-color: {bg_color};
|
||||
border-radius: 10px;
|
||||
padding: 8px 12px;
|
||||
}}
|
||||
""")
|
||||
# Style only the bubble frame
|
||||
self.bubble_frame.setStyleSheet(
|
||||
f"#Bubble {{ background-color: {bg_color}; border-radius: 10px; }}"
|
||||
)
|
||||
self.text_label.setStyleSheet(f"color: {text_color}; font-size: 10pt;")
|
||||
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
|
||||
parent_layout = self.parentWidget().layout() if self.parentWidget() else None
|
||||
if parent_layout:
|
||||
# Этот способ не сработает напрямую, выравнивание нужно делать в QListWidgetItem
|
||||
pass
|
||||
# Keep timestamp alignment in sync with side
|
||||
self.timestamp_label.setAlignment(ts_align)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user