""" Post details dialog showing favorites, boosts, and other interaction details """ from PySide6.QtWidgets import ( QDialog, QVBoxLayout, QHBoxLayout, QLabel, QTextEdit, QTabWidget, QListWidget, QListWidgetItem, QDialogButtonBox, QWidget, QGroupBox, QPushButton ) from PySide6.QtCore import Qt, Signal, QThread from PySide6.QtGui import QFont from typing import List, Dict, Any, Optional from activitypub.client import ActivityPubClient from models.user import User from audio.sound_manager import SoundManager class FetchDetailsThread(QThread): """Background thread for fetching post interaction details""" details_loaded = Signal(dict) # Emitted with details data details_failed = Signal(str) # Emitted with error message def __init__(self, client: ActivityPubClient, post_id: str): super().__init__() self.client = client self.post_id = post_id def run(self): """Fetch favorites and boosts in background""" try: details = { 'favourited_by': [], 'reblogged_by': [] } # Fetch who favorited this post try: favourited_by_data = self.client.get_status_favourited_by(self.post_id) details['favourited_by'] = favourited_by_data except Exception as e: print(f"Failed to fetch favorites: {e}") # Fetch who boosted this post try: reblogged_by_data = self.client.get_status_reblogged_by(self.post_id) details['reblogged_by'] = reblogged_by_data except Exception as e: print(f"Failed to fetch boosts: {e}") self.details_loaded.emit(details) except Exception as e: self.details_failed.emit(str(e)) class PostDetailsDialog(QDialog): """Dialog showing detailed post interaction information""" def __init__(self, post, client: ActivityPubClient, sound_manager: SoundManager, parent=None): super().__init__(parent) self.post = post self.client = client self.sound_manager = sound_manager self.setWindowTitle("Post Details") self.setModal(True) self.resize(600, 500) self.setup_ui() self.load_details() def setup_ui(self): """Setup the post details UI""" layout = QVBoxLayout(self) # Post content section content_group = QGroupBox("Post Content") content_group.setAccessibleName("Post Content") content_layout = QVBoxLayout(content_group) # Author info author_label = QLabel(f"@{self.post.account.username} ({self.post.account.display_name or self.post.account.username})") author_label.setAccessibleName("Post Author") author_font = QFont() author_font.setBold(True) author_label.setFont(author_font) content_layout.addWidget(author_label) # Post content content_text = QTextEdit() content_text.setAccessibleName("Post Content") content_text.setPlainText(self.post.get_content_text()) content_text.setReadOnly(True) content_text.setMaximumHeight(100) # Enable keyboard navigation in read-only text content_text.setTextInteractionFlags(Qt.TextSelectableByKeyboard | Qt.TextSelectableByMouse) content_layout.addWidget(content_text) # Stats stats_text = f"Replies: {self.post.replies_count} | Boosts: {self.post.reblogs_count} | Favorites: {self.post.favourites_count}" stats_label = QLabel(stats_text) stats_label.setAccessibleName("Post Statistics") content_layout.addWidget(stats_label) layout.addWidget(content_group) # Tabs for interaction details self.tabs = QTabWidget() self.tabs.setAccessibleName("Interaction Details") # Favorites tab self.favorites_list = QListWidget() self.favorites_list.setAccessibleName("Users Who Favorited") # Add fake header for single-item navigation fake_header = QListWidgetItem("Users who favorited this post:") fake_header.setFlags(Qt.ItemIsEnabled) # Not selectable self.favorites_list.addItem(fake_header) self.tabs.addTab(self.favorites_list, f"Favorites ({self.post.favourites_count})") # Boosts tab self.boosts_list = QListWidget() self.boosts_list.setAccessibleName("Users Who Boosted") # Add fake header for single-item navigation fake_header = QListWidgetItem("Users who boosted this post:") fake_header.setFlags(Qt.ItemIsEnabled) # Not selectable self.boosts_list.addItem(fake_header) self.tabs.addTab(self.boosts_list, f"Boosts ({self.post.reblogs_count})") layout.addWidget(self.tabs) # Loading indicator self.status_label = QLabel("Loading interaction details...") self.status_label.setAccessibleName("Loading Status") layout.addWidget(self.status_label) # Button box button_box = QDialogButtonBox(QDialogButtonBox.Close) button_box.setAccessibleName("Dialog Buttons") button_box.rejected.connect(self.reject) layout.addWidget(button_box) def load_details(self): """Load detailed interaction information""" if not self.client or not hasattr(self.post, 'id'): self.status_label.setText("Cannot load details: No post ID or API client") return # Start background fetch self.fetch_thread = FetchDetailsThread(self.client, self.post.id) self.fetch_thread.details_loaded.connect(self.on_details_loaded) self.fetch_thread.details_failed.connect(self.on_details_failed) self.fetch_thread.start() def on_details_loaded(self, details: dict): """Handle successful details loading""" self.status_label.setText("") # Populate favorites list favourited_by = details.get('favourited_by', []) if favourited_by: for account_data in favourited_by: try: user = User.from_api_dict(account_data) display_name = user.display_name or user.username item_text = f"@{user.username} ({display_name})" item = QListWidgetItem(item_text) item.setData(Qt.UserRole, user) self.favorites_list.addItem(item) except Exception as e: print(f"Error parsing favorite user: {e}") else: item = QListWidgetItem("No one has favorited this post yet") self.favorites_list.addItem(item) # Populate boosts list reblogged_by = details.get('reblogged_by', []) if reblogged_by: for account_data in reblogged_by: try: user = User.from_api_dict(account_data) display_name = user.display_name or user.username item_text = f"@{user.username} ({display_name})" item = QListWidgetItem(item_text) item.setData(Qt.UserRole, user) self.boosts_list.addItem(item) except Exception as e: print(f"Error parsing boost user: {e}") else: item = QListWidgetItem("No one has boosted this post yet") self.boosts_list.addItem(item) # Update tab titles with actual counts actual_favorites = len(favourited_by) actual_boosts = len(reblogged_by) self.tabs.setTabText(0, f"Favorites ({actual_favorites})") self.tabs.setTabText(1, f"Boosts ({actual_boosts})") # Play success sound self.sound_manager.play_success() def on_details_failed(self, error_message: str): """Handle details loading failure""" self.status_label.setText(f"Failed to load details: {error_message}") # Add error items to lists error_item_fav = QListWidgetItem(f"Error loading favorites: {error_message}") self.favorites_list.addItem(error_item_fav) error_item_boost = QListWidgetItem(f"Error loading boosts: {error_message}") self.boosts_list.addItem(error_item_boost) # Play error sound self.sound_manager.play_error()