223 lines
9.3 KiB
Python
223 lines
9.3 KiB
Python
from PySide6.QtWidgets import QWidget, QListWidget, QVBoxLayout, QListWidgetItem, QAbstractItemView
|
||
from PySide6.QtCore import Qt, QSize, Signal
|
||
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:
|
||
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
|
||
|
||
|
||
|
||
|