patch chat
This commit is contained in:
		
							parent
							
								
									229cab0dde
								
							
						
					
					
						commit
						54e4d3542e
					
				@ -106,6 +106,9 @@ class ChatListView(QWidget):
 | 
			
		||||
            if chat.chat_type == "self":
 | 
			
		||||
                companion_name = "Избранное"
 | 
			
		||||
            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']
 | 
			
		||||
            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