230 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			230 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
from PySide6.QtWidgets import QWidget, QListWidget, QVBoxLayout, QListWidgetItem, QAbstractItemView
 | 
						||
from PySide6.QtCore import Qt, QSize, Signal, QLocale
 | 
						||
from typing import List
 | 
						||
from app.core.models.chat_models import PrivateChatListItem
 | 
						||
from app.ui.widgets.chat_list_item_widget import ChatListItemWidget
 | 
						||
from app.core.theme import theme_manager
 | 
						||
from datetime import datetime
 | 
						||
from uuid import UUID
 | 
						||
 | 
						||
class ChatListView(QWidget):
 | 
						||
    chat_selected = Signal(UUID)
 | 
						||
 | 
						||
    def __init__(self, current_user_id: UUID = None):
 | 
						||
        super().__init__()
 | 
						||
        self.current_user_id = current_user_id
 | 
						||
        self.chat_items_map = {}
 | 
						||
        self.init_ui()
 | 
						||
        self.update_theme()
 | 
						||
        theme_manager.theme_changed.connect(self.update_theme)
 | 
						||
 | 
						||
    def init_ui(self):
 | 
						||
        """Инициализирует пользовательский интерфейс."""
 | 
						||
        layout = QVBoxLayout(self)
 | 
						||
        layout.setContentsMargins(0, 0, 0, 0)
 | 
						||
        layout.setSpacing(0)
 | 
						||
 | 
						||
        self.chat_list = QListWidget()
 | 
						||
        self.chat_list.setSpacing(2)
 | 
						||
        # Ensure no default row backgrounds
 | 
						||
        self.chat_list.setAlternatingRowColors(False)
 | 
						||
        self.chat_list.setAutoFillBackground(False)
 | 
						||
        # Disable selection to avoid persistent click highlight
 | 
						||
        self.chat_list.setSelectionMode(QAbstractItemView.NoSelection)
 | 
						||
        self.chat_list.itemClicked.connect(self.on_chat_item_clicked)
 | 
						||
        layout.addWidget(self.chat_list)
 | 
						||
 | 
						||
        # Изначальное состояние
 | 
						||
        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)
 | 
						||
            # Remove focus to avoid blue outline after selection
 | 
						||
            self.chat_list.clearFocus()
 | 
						||
 | 
						||
    def update_theme(self):
 | 
						||
        """Обновляет стили в соответствии с темой."""
 | 
						||
        palette = theme_manager.get_current_palette()
 | 
						||
        self.chat_list.setStyleSheet(f"""
 | 
						||
            QListWidget {{
 | 
						||
                background-color: {palette['primary']};
 | 
						||
                border: none;
 | 
						||
                padding: 5px;
 | 
						||
                outline: 0;
 | 
						||
            }}
 | 
						||
            QListWidget::item {{
 | 
						||
                background-color: transparent;
 | 
						||
                border: none;
 | 
						||
                border-radius: 5px;
 | 
						||
            }}
 | 
						||
            QListWidget::item:hover {{
 | 
						||
                background-color: {palette['hover']};
 | 
						||
                border: none;
 | 
						||
            }}
 | 
						||
            QScrollBar:vertical {{
 | 
						||
                border: none;
 | 
						||
                background: {palette['primary']};
 | 
						||
                width: 8px;
 | 
						||
                margin: 0px 0px 0px 0px;
 | 
						||
            }}
 | 
						||
            QScrollBar::handle:vertical {{
 | 
						||
                background: {palette['secondary']};
 | 
						||
                min-height: 20px;
 | 
						||
                border-radius: 4px;
 | 
						||
            }}
 | 
						||
            QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {{
 | 
						||
                height: 0px;
 | 
						||
            }}
 | 
						||
        """)
 | 
						||
        # Force transparent backgrounds behind text/items
 | 
						||
        existing_styles = self.chat_list.styleSheet()
 | 
						||
        self.chat_list.setStyleSheet(existing_styles + f"""
 | 
						||
            QListWidget::item {{
 | 
						||
                background-color: transparent;
 | 
						||
                border: none;
 | 
						||
                border-radius: 5px;
 | 
						||
            }}
 | 
						||
            QListWidget::item:hover {{
 | 
						||
                background-color: {palette['hover']};
 | 
						||
                border: none;
 | 
						||
            }}
 | 
						||
            QListWidget::item:selected {{
 | 
						||
                background-color: {palette['selected']};
 | 
						||
                border: none;
 | 
						||
            }}
 | 
						||
            QListView::item {{
 | 
						||
                background-color: transparent;
 | 
						||
                border: none;
 | 
						||
                border-radius: 5px;
 | 
						||
            }}
 | 
						||
            QListView::item:hover {{
 | 
						||
                background-color: {palette['hover']};
 | 
						||
                border: none;
 | 
						||
            }}
 | 
						||
            QListView::item:selected {{
 | 
						||
                background-color: {palette['selected']};
 | 
						||
                border: none;
 | 
						||
            }}
 | 
						||
            QListWidget QLabel {{
 | 
						||
                background-color: transparent;
 | 
						||
            }}
 | 
						||
        """)
 | 
						||
        # Обновляем существующие элементы
 | 
						||
        for i in range(self.chat_list.count()):
 | 
						||
            item = self.chat_list.item(i)
 | 
						||
            widget = self.chat_list.itemWidget(item)
 | 
						||
            if isinstance(widget, ChatListItemWidget):
 | 
						||
                widget.update_theme()
 | 
						||
 | 
						||
    def show_placeholder_message(self, text):
 | 
						||
        """Очищает список и показывает одно сообщение (например, "Загрузка..." или "Чатов нет")."""
 | 
						||
        self.chat_list.clear()
 | 
						||
        item = QListWidgetItem(text)
 | 
						||
        item.setTextAlignment(Qt.AlignCenter)
 | 
						||
        # Убираем фон у элемента-заглушки
 | 
						||
        item.setBackground(Qt.transparent)
 | 
						||
        self.chat_list.addItem(item)
 | 
						||
 | 
						||
    def populate_chats(self, chat_items: List[PrivateChatListItem]):
 | 
						||
        """
 | 
						||
        Заполняет список чатов данными, полученными от сервера.
 | 
						||
        """
 | 
						||
        self.chat_list.clear()
 | 
						||
        self.chat_items_map.clear()
 | 
						||
 | 
						||
        if not chat_items:
 | 
						||
            self.show_placeholder_message("У вас пока нет чатов")
 | 
						||
            return
 | 
						||
        
 | 
						||
        # Сортируем чаты по времени последнего сообщения
 | 
						||
        chat_items.sort(key=lambda x: x.last_message.created_at if x.last_message else datetime.min, reverse=True)
 | 
						||
 | 
						||
        for chat in chat_items:
 | 
						||
            # Определяем имя собеседника
 | 
						||
            if chat.chat_type == "self":
 | 
						||
                companion_name = "Избранное"
 | 
						||
            elif chat.chat_data and 'login' in chat.chat_data:
 | 
						||
                # print("=============================")
 | 
						||
                # print("chat.chat_data", chat.chat_data)
 | 
						||
                # print("=============================")
 | 
						||
                if chat.chat_data['custom_name']:
 | 
						||
                    companion_name = "@"+chat.chat_data['login'] + " (" + chat.chat_data['custom_name'] + ")"
 | 
						||
                else:
 | 
						||
                    companion_name = "@"+chat.chat_data['login']
 | 
						||
            else:
 | 
						||
                companion_name = "Неизвестный"
 | 
						||
 | 
						||
            # Получаем текст и время последнего сообщения
 | 
						||
            if chat.last_message and chat.last_message.content:
 | 
						||
                last_msg = chat.last_message.content
 | 
						||
                timestamp = chat.last_message.created_at.strftime('%H:%M')
 | 
						||
            else:
 | 
						||
                last_msg = "Нет сообщений"
 | 
						||
                timestamp = ""
 | 
						||
            
 | 
						||
            # TODO: Заменить на реальное количество непрочитанных сообщений
 | 
						||
            unread_count = 2
 | 
						||
 | 
						||
            # Build display timestamp based on recency
 | 
						||
            if chat.last_message and getattr(chat.last_message, 'created_at', None):
 | 
						||
                ts_utc = chat.last_message.created_at
 | 
						||
                ts_dt = ts_utc.astimezone()
 | 
						||
                now = datetime.now().astimezone()
 | 
						||
                delta_days = (now.date() - ts_dt.date()).days
 | 
						||
                if delta_days == 0:
 | 
						||
                    # Respect system 12/24h preference
 | 
						||
                    time_fmt = QLocale.system().timeFormat(QLocale.ShortFormat)
 | 
						||
                    if ('AP' in time_fmt) or ('ap' in time_fmt) or ('a' in time_fmt):
 | 
						||
                        # 12-hour clock
 | 
						||
                        display_ts = ts_dt.strftime('%I:%M %p').lstrip('0')
 | 
						||
                    else:
 | 
						||
                        # 24-hour clock
 | 
						||
                        display_ts = ts_dt.strftime('%H:%M')
 | 
						||
                elif delta_days == 1:
 | 
						||
                    display_ts = 'Вчера'
 | 
						||
                elif 1 < delta_days < 7:
 | 
						||
                    weekdays = ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс']
 | 
						||
                    display_ts = weekdays[ts_dt.weekday()]
 | 
						||
                elif delta_days < 365:
 | 
						||
                    display_ts = ts_dt.strftime('%b %d')
 | 
						||
                else:
 | 
						||
                    display_ts = ts_dt.strftime('%d.%m.%y')
 | 
						||
            else:
 | 
						||
                display_ts = ''
 | 
						||
            # Создаем кастомный виджет
 | 
						||
            is_outgoing = False
 | 
						||
            is_read = None
 | 
						||
            if chat.last_message:
 | 
						||
                if self.current_user_id is not None:
 | 
						||
                    is_outgoing = (str(chat.last_message.sender_id) == str(self.current_user_id))
 | 
						||
                    if is_outgoing: unread_count = 0
 | 
						||
                is_read = bool(chat.last_message.is_viewed)
 | 
						||
 | 
						||
            item_widget = ChatListItemWidget(companion_name, last_msg, display_ts, unread_count=unread_count, is_outgoing=is_outgoing, is_read=is_read)
 | 
						||
            # Tooltip with full date/time on hover
 | 
						||
            try:
 | 
						||
                if chat.last_message and getattr(chat.last_message, 'created_at', None):
 | 
						||
                    ts_utc = chat.last_message.created_at
 | 
						||
                    ts_local = ts_utc.astimezone()
 | 
						||
                    item_widget.set_timestamp_tooltip(ts_local.strftime('%d.%m.%Y %H:%M:%S'))
 | 
						||
                else:
 | 
						||
                    item_widget.set_timestamp_tooltip("")
 | 
						||
            except Exception:
 | 
						||
                pass
 | 
						||
            
 | 
						||
            # Создаем элемент списка и устанавливаем для него наш виджет
 | 
						||
            list_item = QListWidgetItem(self.chat_list)
 | 
						||
            list_item.setSizeHint(item_widget.sizeHint())
 | 
						||
            self.chat_list.addItem(list_item)
 | 
						||
            self.chat_list.setItemWidget(list_item, item_widget)
 | 
						||
            
 | 
						||
            # Сохраняем ID чата в словаре
 | 
						||
            self.chat_items_map[id(list_item)] = chat.chat_id
 | 
						||
 | 
						||
 | 
						||
 | 
						||
 |