UNSTABLE | Backup for tomorrow. Will continue working tomorrow most likely. 00:38 - Session ended.

This commit is contained in:
wheelchairy 2025-03-27 00:41:16 +03:00
parent c01d5954dc
commit c1355b7bf9

View File

@ -5,6 +5,7 @@ from textual.widget import Widget
from textual.reactive import Reactive from textual.reactive import Reactive
from textual.widgets import Input, Button, Label, Static, ContentSwitcher from textual.widgets import Input, Button, Label, Static, ContentSwitcher
from textual.app import ComposeResult, RenderResult from textual.app import ComposeResult, RenderResult
from textual.keys import Keys
from telethon import TelegramClient, events, utils from telethon import TelegramClient, events, utils
import datetime import datetime
import unicodedata import unicodedata
@ -73,14 +74,47 @@ def convert_image_to_ascii(image_path: str, width: int = 50) -> str:
log(f"Ошибка конвертации изображения: {e}") log(f"Ошибка конвертации изображения: {e}")
return "Ошибка загрузки изображения" return "Ошибка загрузки изображения"
class Chat(Widget): class Chat(Static):
"""Класс виджета чата для панели чатов""" """Класс виджета чата для панели чатов"""
username: Reactive[str] = Reactive(" ", recompose=True) DEFAULT_CSS = """
msg: Reactive[str] = Reactive(" ", recompose=True) Chat {
peer_id: Reactive[int] = Reactive(0) width: 100%;
is_selected: Reactive[bool] = Reactive(False) height: auto;
is_focused: Reactive[bool] = Reactive(False) min-height: 3;
padding: 1 2;
border: solid $accent;
margin: 1 0;
background: $surface;
}
Chat:hover {
background: $accent 20%;
}
Chat.-selected {
background: $accent 30%;
}
Chat:focus {
background: $accent 40%;
border: double $accent;
}
.chat-avatar {
width: 3;
height: 3;
content-align: center middle;
border: solid $accent;
}
.chat-content {
width: 100%;
margin-left: 1;
}
.chat-name {
color: $text;
text-style: bold;
}
.chat-message {
color: $text-muted;
}
"""
def __init__( def __init__(
self, self,
@ -89,25 +123,64 @@ class Chat(Widget):
classes: str | None = None, classes: str | None = None,
disabled: bool = False disabled: bool = False
) -> None: ) -> None:
super().__init__( super().__init__(name=name, id=id, classes=classes, disabled=disabled)
name=str(name), self.can_focus = True
id=id, self._username = ""
classes=classes, self._msg = ""
disabled=disabled self._peer_id = 0
) self._is_selected = False
def on_mount(self) -> None: def on_mount(self) -> None:
self.switcher = self.screen.query_one(Horizontal).query_one("#dialog_switcher", ContentSwitcher) self.switcher = self.screen.query_one("#dialog_switcher", ContentSwitcher)
def on_click(self) -> None: @property
def username(self) -> str:
return self._username
@username.setter
def username(self, value: str) -> None:
self._username = value
self.refresh()
@property
def msg(self) -> str:
return self._msg
@msg.setter
def msg(self, value: str) -> None:
self._msg = value
self.refresh()
@property
def peer_id(self) -> int:
return self._peer_id
@peer_id.setter
def peer_id(self, value: int) -> None:
self._peer_id = value
@property
def is_selected(self) -> bool:
return self._is_selected
@is_selected.setter
def is_selected(self, value: bool) -> None:
self._is_selected = value
self.set_class(value, "-selected")
def on_focus(self) -> None:
# Снимаем выделение со всех чатов # Снимаем выделение со всех чатов
for chat in self.screen.query(Chat): for chat in self.screen.query(Chat):
chat.is_selected = False chat.is_selected = False
chat.is_focused = False
# Выделяем текущий чат # Выделяем текущий чат
self.is_selected = True self.is_selected = True
self.is_focused = True
# Прокручиваем к этому чату
self.screen.chat_container.scroll_to(self, animate=False)
def on_click(self) -> None:
self.focus()
dialog_id = f"dialog-{str(self.peer_id)}" dialog_id = f"dialog-{str(self.peer_id)}"
try: try:
@ -116,24 +189,41 @@ class Chat(Widget):
chat_id=self.peer_id, chat_id=self.peer_id,
id=dialog_id id=dialog_id
)) ))
except: except Exception as e:
pass log(f"Ошибка открытия диалога: {e}")
return
self.switcher.current = dialog_id self.switcher.current = dialog_id
self.switcher.recompose()
def on_key(self, event: Keys) -> None:
if event.key == "enter":
self.on_click()
elif event.key == "up" and self.id != "chat-1":
# Фокусируемся на предыдущем чате
prev_chat = self.screen.chat_container.query_one(f"#chat-{int(self.id.split('-')[1]) - 1}")
if prev_chat:
prev_chat.focus()
elif event.key == "down":
# Фокусируемся на следующем чате
next_chat = self.screen.chat_container.query_one(f"#chat-{int(self.id.split('-')[1]) + 1}")
if next_chat:
next_chat.focus()
def compose(self) -> ComposeResult: def compose(self) -> ComposeResult:
with Horizontal(classes="chat-item"): """Компонуем виджет чата"""
# Используем ASCII-символы для рамки with Horizontal():
yield Label(f"+---+\n| {safe_ascii(self.username[:1].upper()):1} |\n+---+") # Аватар (первая буква имени)
with Vertical(): first_letter = normalize_text(self._username[:1].upper()) or "?"
yield Label(normalize_text(self.username), id="name") yield Static(first_letter, classes="chat-avatar")
yield Label(normalize_text(self.msg), id="last_msg")
# Контент (имя и сообщение)
def on_mouse_enter(self) -> None: with Vertical(classes="chat-content"):
self.add_class("hover") name = normalize_text(self._username) or "Без названия"
msg = normalize_text(self._msg) or "Нет сообщений"
def on_mouse_leave(self) -> None: msg = msg[:50] + "..." if len(msg) > 50 else msg
self.remove_class("hover")
yield Static(name, classes="chat-name")
yield Static(msg, classes="chat-message")
class Dialog(Widget): class Dialog(Widget):
"""Класс окна диалога""" """Класс окна диалога"""