remove "tray"

This commit is contained in:
Fengqing Liu
2025-10-19 17:15:28 +11:00
parent d466f46d6f
commit a321da91f8
31 changed files with 3 additions and 262 deletions

View File

@@ -73,7 +73,7 @@ src/
├── api/ # External API (HTTP client, GraphQL client)
├── websocket/ # Real-time updates (websocket connection, pool)
├── web/ # Web GUI (app, gui_manager, api/)
│ └── managers/ # Individual UI managers (status, console, channels, campaigns, inventory, login, settings, cache, tray, broadcaster)
│ └── managers/ # Individual UI managers (status, console, channels, campaigns, inventory, login, settings, cache, broadcaster)
├── services/ # Business logic services (channel, inventory, watch, maintenance, message_handlers)
├── core/ # Core client (Twitch client)
├── exceptions.py # Custom exceptions
@@ -127,7 +127,7 @@ Note: The services layer is complete and handles all business logic that was pre
**src/web/gui_manager.py** - Web GUI:
- `WebGUIManager`: Main GUI coordinator
- Composes individual managers for different UI concerns (status, console, channels, campaigns, inventory, login, settings, cache, tray)
- Composes individual managers for different UI concerns (status, console, channels, campaigns, inventory, login, settings, cache)
- Uses `WebSocketBroadcaster` for real-time Socket.IO updates
- Pure asyncio, no tkinter dependency
@@ -236,7 +236,7 @@ The application uses a web-based interface accessible via browser:
### Web GUI Components
**src/web/gui_manager.py** - WebGUIManager class:
- Managers: StatusManager, ConsoleOutputManager, ChannelListManager, CampaignProgressManager, InventoryManager, LoginFormManager, SettingsManager, CacheManager, TrayManager
- Managers: StatusManager, ConsoleOutputManager, ChannelListManager, CampaignProgressManager, InventoryManager, LoginFormManager, SettingsManager, CacheManager
- Uses WebSocketBroadcaster to push real-time updates to connected clients via Socket.IO
- Pure async/await implementation

View File

@@ -49,12 +49,6 @@
"settings": "Indstillinger",
"help": "Hjælp"
},
"tray": {
"notification_title": "Mined Belønning",
"minimize": "Minimer til systembakken",
"show": "Vis",
"quit": "Afslut"
},
"login": {
"name": "Login formular",
"labels": "Status:\nBruger ID:",
@@ -132,8 +126,6 @@
"general": {
"name": "General",
"autostart": "Automatisk start: ",
"tray": "Automatisk start i systembakken: ",
"tray_notifications": "Bakkemeddelelser: ",
"priority_only": "Kun prioritet: ",
"dark_mode": "Mørk tilstand: ",
"proxy": "Proxy (kræver genstart):"

View File

@@ -49,12 +49,6 @@
"settings": "Einstellungen",
"help": "Hilfe"
},
"tray": {
"notification_title": "Drop abgeholt",
"minimize": "Minimiere ins System Tray",
"show": "Anzeigen",
"quit": "Beenden"
},
"login": {
"name": "Login",
"labels": "Status:\nBenutzer ID:",
@@ -132,7 +126,6 @@
"general": {
"name": "Allgemein",
"autostart": "Autostart: ",
"tray": "Autostart ins System Tray: ",
"priority_only": "Nur Priorität: ",
"dark_mode": "Dunkler Modus: ",
"proxy": "Proxy (Erfordert Neustart):"

View File

@@ -49,12 +49,6 @@
"settings": "Configuración",
"help": "Ayuda"
},
"tray": {
"notification_title": "Drop minado",
"minimize": "Minimizar a la bandeja",
"show": "Mostrar",
"quit": "Salir"
},
"login": {
"name": "Inicio de sesión",
"labels": "Estado:\nUsuario:",
@@ -131,18 +125,9 @@
"settings": {
"general": {
"name": "Ajustes generales",
"autostart": "Ejecutar al iniciar el sistema: ",
"tray": "Ejecutar en la bandeja del sistema: ",
"tray_notifications": "Mostrar notificaciones: ",
"dark_mode": "Modo oscuro: ",
"priority_mode": "Modo de prioridad: ",
"proxy": "Proxy (requiere reinicio):"
},
"priority_modes": {
"priority_only": "Solo juegos preferidos",
"ending_soonest": "Terminando pronto",
"low_availability": "Poca disponibilidad primero"
},
"game_name": "Nombre del juego",
"priority": "Lista de juegos preferidos",
"exclude": "Lista de juegos excluidos",

View File

@@ -49,12 +49,6 @@
"settings": "Paramètres",
"help": "Aide"
},
"tray": {
"notification_title": "Drop miné",
"minimize": "Réduire dans la barre des tâches",
"show": "Afficher",
"quit": "Quitter"
},
"login": {
"name": "Formulaire de connexion",
"labels": "Statut :\nIdentifiant utilisateur :",
@@ -132,8 +126,6 @@
"general": {
"name": "Général",
"autostart": "Démarrage automatique :",
"tray": "Démarrage automatique dans la barre des tâches :",
"tray_notifications": "Notifications dans la barre des tâches :",
"dark_mode": "Mode sombre : ",
"priority_mode": "Mode de priorité :",
"proxy": "Proxy (nécessite un redémarrage) :"

View File

@@ -49,12 +49,6 @@
"settings": "Parameter",
"help": "Bantuan"
},
"tray": {
"notification_title": "Tambang drop",
"minimize": "Meminimalkan di bilah tugas",
"show": "Lihat",
"quit": "Keluar"
},
"login": {
"name": "Formulir masuk",
"labels": "Status :\nID Pengguna :",
@@ -131,9 +125,6 @@
"settings": {
"general": {
"name": "Umum",
"autostart": "Mulai secara otomatis :",
"tray": "Mulai otomatis di bilah tugas :",
"tray_notifications": "Pemberitahuan bilah tugas :",
"dark_mode": "Mode gelap: ",
"priority_only": "Prioritas saja :",
"proxy": "Proksi (harus dimulai ulang) :"

View File

@@ -49,12 +49,6 @@
"settings": "Impostazioni",
"help": "Aiuto"
},
"tray": {
"notification_title": "Premio Ottenuto",
"minimize": "Minimizza nella barra delle applicazioni",
"show": "Mostra",
"quit": "Esci"
},
"login": {
"name": "Dettagli Login",
"labels": "Stato:\nID Utente:",
@@ -131,9 +125,6 @@
"settings": {
"general": {
"name": "Generale",
"autostart": "Avvio automatico: ",
"tray": "Avvio automatico nella barra delle applicazioni: ",
"tray_notifications": "Notifiche: ",
"dark_mode": "Modalità scura: ",
"priority_only": "Solo priorità: ",
"proxy": "Proxy (richiede il riavvio):"

View File

@@ -49,12 +49,6 @@
"settings": "Instellingen",
"help": "Help"
},
"tray": {
"notification_title": "Drop verkregen",
"minimize": "Minimaliseren naar taakbalk",
"show": "Weergeven",
"quit": "Verlaten"
},
"login": {
"name": "Login",
"labels": "Status:\nGebruiker ID:",
@@ -131,8 +125,6 @@
"settings": {
"general": {
"name": "Algemeen",
"autostart": "Autostart: ",
"tray": "Automatisch starten in het taakbalk: ",
"priority_only": "Alleen prioriteit: ",
"dark_mode": "Donkere modus: ",
"proxy": "Proxy (Vereist opnieuw opstarten):"

View File

@@ -49,12 +49,6 @@
"settings": "Ustawienia",
"help": "Pomoc"
},
"tray": {
"notification_title": "Drop odebrany",
"minimize": "Zminimalizuj",
"show": "Pokaż",
"quit": "Wyjdź"
},
"login": {
"name": "Logowanie",
"labels": "Status:\nIdentyfikator:",
@@ -132,8 +126,6 @@
"general": {
"name": "Ogólne",
"autostart": "Autostart: ",
"tray": "Autostart z zasobnika: ",
"tray_notifications": "Powiadomienia z zasobnika: ",
"dark_mode": "Tryb ciemny: ",
"priority_mode": "Tryb priorytetu: ",
"proxy": "Proxy (wymaga restartu):"

View File

@@ -49,12 +49,6 @@
"settings": "Configurações",
"help": "Ajuda"
},
"tray": {
"notification_title": "Drop coletado",
"minimize": "Minimizar para barra de tarefas",
"show": "Mostrar",
"quit": "Sair"
},
"login": {
"name": "Tela de login",
"labels": "Status:\nUser ID:",
@@ -132,8 +126,6 @@
"general": {
"name": "Geral",
"autostart": "Auto-início: ",
"tray": "Auto-iniciar na área de notificação: ",
"tray_notifications": "Notificações: ",
"dark_mode": "Modo escuro: ",
"priority_only": "Apenas com prioridade: ",
"proxy": "Proxy (requer o reinício do app):"

View File

@@ -49,12 +49,6 @@
"settings": "Setări",
"help": "Ajutor"
},
"tray": {
"notification_title": "Drop minat",
"minimize": "Minimizați în bara de activități",
"show": "Arată",
"quit": "Renunțare"
},
"login": {
"name": "Formular de conectare",
"labels": "Statut :\nID-ul de utilizator :",
@@ -132,8 +126,6 @@
"general": {
"name": "General",
"autostart": "Pornire automată :",
"tray": "Pornire automată în bara de activități :",
"tray_notifications": "Notificări în bara de activități :",
"dark_mode": "Mod întunecat :",
"priority_only": "Doar cu prioritate :",
"proxy": "Proxy (necesită repornire) :"

View File

@@ -49,12 +49,6 @@
"settings": "Ayarlar",
"help": "Yardım"
},
"tray": {
"notification_title": "Drop alındı",
"minimize": "Sistem tepsisine küçült",
"show": "Göster",
"quit": ık"
},
"login": {
"name": "Giriş Yap",
"labels": "Durum:\nKullanıcı ID:",
@@ -131,8 +125,6 @@
"settings": {
"general": {
"name": "Genel",
"autostart": "Otomatik başlatma: ",
"tray": "Sistem tepsisine otomatik başlatma: ",
"priority_only": "Yalnızca öncelik: ",
"dark_mode": "Karanlık mod: ",
"proxy": "Proxy (Yeniden başlatma gerektirir):"

View File

@@ -42,12 +42,6 @@
"settings": "Nastavení",
"help": "Nápověda"
},
"tray": {
"notification_title": "Začít sbírat dropy",
"minimize": "Minimalizovat",
"show": "Zobrazit",
"quit": "Ukončit"
},
"login": {
"name": "Přihlášení k službě Twitch",
"labels": "Uživatelské ID:",
@@ -125,7 +119,6 @@
"general": {
"name": "Nastavení",
"autostart": "Automatické spuštění: ",
"tray": "Automaticky spusti minimalizovaně: ",
"priority_only": "Pouze prioritní: ",
"dark_mode": "Tmavý režim: ",
"proxy": "Proxy:"

View File

@@ -49,12 +49,6 @@
"settings": "Настройки",
"help": "Помощь"
},
"tray": {
"notification_title": "Drop получено",
"minimize": "Свернуть в трей",
"show": "Показать",
"quit": "Закрыть"
},
"login": {
"name": "Авторизация",
"labels": "Статус:\nID пользователя:",
@@ -131,9 +125,6 @@
"settings": {
"general": {
"name": "Общие",
"autostart": "Автозапуск: ",
"tray": "Автозапуск свёрнутым: ",
"tray_notifications": "Всплывающие уведомления: ",
"dark_mode": "Тёмный режим: ",
"priority_mode": "Режим приоритета: ",
"proxy": "Прокси (Требуется перезапуск):"

View File

@@ -49,12 +49,6 @@
"settings": "Налаштування",
"help": "Інформація"
},
"tray": {
"notification_title": "Дроп отримано",
"minimize": "Згорнути в трей",
"show": "Показати",
"quit": "Вийти"
},
"login": {
"name": "Форма для входу",
"labels": "Стан:\nІдентифікатор користувача:",
@@ -131,9 +125,6 @@
"settings": {
"general": {
"name": "Основні",
"autostart": "Автозапуск: ",
"tray": "Автозапуск у треї: ",
"tray_notifications": "Сповіщення: ",
"dark_mode": "Темний режим: ",
"priority_mode": "Режим пріоритету: ",
"proxy": "Проксі (потребує перезапуску):"

View File

@@ -49,12 +49,6 @@
"settings": "الإعدادات",
"help": "مساعدة"
},
"tray": {
"notification_title": "تم الحصول على هذا الإسقاط",
"minimize": "تصغير الى الدرج",
"show": "عرض",
"quit": "خروج"
},
"login": {
"name": "تسجيل الدخول و معلومات عن الحساب",
"labels": "الحالة \u279C\nالمستخدم ID \u279C",
@@ -131,9 +125,6 @@
"settings": {
"general": {
"name": "عام",
"autostart": "تشغيل تلقائي ",
"tray": "تشغيل تلقائي في الدرج ",
"tray_notifications": "إشعارات الدرج ",
"dark_mode": "الوضع الداكن: ",
"priority_only": "الأولوية فقط ",
"proxy": "بروكسي (يتطلب إعادة التشغيل) "

View File

@@ -49,12 +49,6 @@
"settings": "設定",
"help": "ヘルプ"
},
"tray": {
"notification_title": "ドロップを取得しました",
"minimize": "トレイに最小化",
"show": "表示",
"quit": "終了"
},
"login": {
"name": "ログインフォーム",
"labels": "ステータス:\nユーザーID",
@@ -131,9 +125,6 @@
"settings": {
"general": {
"name": "一般",
"autostart": "自動起動:",
"tray": "トレイに自動起動:",
"tray_notifications": "トレイ通知:",
"dark_mode": "ダークモード:",
"priority_only": "優先のみ:",
"proxy": "プロキシ(再起動が必要):"

View File

@@ -49,12 +49,6 @@
"settings": "设置",
"help": "帮助"
},
"tray": {
"notification_title": "开始掉宝活动",
"minimize": "窗口最小化",
"show": "掉宝活动",
"quit": "退出"
},
"login": {
"name": "登录Twitch账号",
"labels": "状态:\n用户ID:",
@@ -131,9 +125,6 @@
"settings": {
"general": {
"name": "功能设置",
"autostart": "开机自启: ",
"tray": "开机自启并最小化: ",
"tray_notifications": "启用托盘通知: ",
"dark_mode": "深色模式: ",
"priority_mode": "掉宝模式: ",
"proxy": "代理:"

View File

@@ -49,12 +49,6 @@
"settings": "設置",
"help": "幫助"
},
"tray": {
"notification_title": "開始掉寶活動",
"minimize": "最小化到系統列",
"show": "開啟視窗",
"quit": "離開"
},
"login": {
"name": "登入 Twitch",
"labels": "狀態:\n用戶ID",
@@ -131,9 +125,6 @@
"settings": {
"general": {
"name": "基本設定",
"autostart": "開機時自動啟動:",
"tray": "開機時以最小化自動啟動:",
"tray_notifications": "啟用系統列通知:",
"dark_mode": "深色模式:",
"priority_mode": "掉寶模式:",
"proxy": "代理伺服器(需要重新啟動):"

View File

@@ -203,7 +203,6 @@ if __name__ == "__main__":
if exit_status != 0:
logger.warning("Application terminated with error - showing error state")
# Application terminated with error
client.gui.tray.change_icon("error")
client.print(_("status", "terminated"))
client.gui.status.update(_("gui", "status", "terminated"))
# notify the user about the closure
@@ -220,13 +219,4 @@ if __name__ == "__main__":
sys.exit(exit_status)
asyncio.run(main())
# try:
# # use lock_file to check if we're not already running
# success, file = lock_file(LOCK_PATH)
# if not success:
# # already running - exit
# sys.exit(3)
# asyncio.run(main())
# finally:
# file.close()

View File

@@ -17,9 +17,7 @@ class SettingsFile(TypedDict):
language: str
dark_mode: bool
games_to_watch: list[str]
autostart_tray: bool
connection_quality: int
tray_notifications: bool
minimum_refresh_interval_minutes: int
@@ -27,10 +25,8 @@ default_settings: SettingsFile = {
"proxy": URL(),
"games_to_watch": [],
"dark_mode": False,
"autostart_tray": False,
"connection_quality": 1,
"language": DEFAULT_LANG,
"tray_notifications": True,
"minimum_refresh_interval_minutes": 30,
}
@@ -38,7 +34,6 @@ default_settings: SettingsFile = {
class Settings:
# from args
log: bool
tray: bool
dump: bool
# args properties
debug_ws: int
@@ -49,9 +44,7 @@ class Settings:
language: str
dark_mode: bool
games_to_watch: list[str]
autostart_tray: bool
connection_quality: int
tray_notifications: bool
minimum_refresh_interval_minutes: int
PASSTHROUGH = ("_settings", "_args", "_altered")

View File

@@ -235,13 +235,11 @@ class Twitch:
if self.settings.dump:
self.close()
continue
self.gui.tray.change_icon("idle")
self.gui.status.update(_("gui", "status", "idle"))
self.stop_watching()
# clear the flag and wait until it's set again
self._state_change.clear()
elif self._state is State.INVENTORY_FETCH:
self.gui.tray.change_icon("maint")
# ensure the websocket is running
await self.websocket.start()
await self.fetch_inventory()
@@ -553,7 +551,6 @@ class Twitch:
self.print(_("status", "no_channel"))
self.change_state(State.IDLE)
elif self._state is State.EXIT:
self.gui.tray.change_icon("pickaxe")
self.gui.status.update(_("gui", "status", "exiting"))
# we've been requested to exit the application
break

View File

@@ -20,7 +20,6 @@ from .translator import (
GUISettingsGeneral,
GUIStatus,
GUITabs,
GUITray,
GUIWebsocket,
LoginMessages,
StatusMessages,
@@ -38,7 +37,6 @@ __all__ = [
"ErrorMessages",
"GUIStatus",
"GUITabs",
"GUITray",
"GUILoginForm",
"GUIWebsocket",
"GUIProgress",

View File

@@ -67,13 +67,6 @@ class GUITabs(TypedDict):
help: str
class GUITray(TypedDict):
notification_title: str
minimize: str
show: str
quit: str
class GUILoginForm(TypedDict):
name: str
labels: str
@@ -161,8 +154,6 @@ class GUIInventory(TypedDict):
class GUISettingsGeneral(TypedDict):
name: str
autostart: str
tray: str
tray_notifications: str
dark_mode: str
priority_mode: str
proxy: str
@@ -202,7 +193,6 @@ class GUIMessages(TypedDict):
output: str
status: GUIStatus
tabs: GUITabs
tray: GUITray
login: GUILoginForm
websocket: GUIWebsocket
progress: GUIProgress
@@ -280,12 +270,6 @@ default_translation: Translation = {
"settings": "Settings",
"help": "Help",
},
"tray": {
"notification_title": "Mined Drop",
"minimize": "Minimize to Tray",
"show": "Show",
"quit": "Quit",
},
"login": {
"name": "Login Form",
"labels": "Status:\nUser ID:",
@@ -363,8 +347,6 @@ default_translation: Translation = {
"general": {
"name": "General",
"autostart": "Autostart: ",
"tray": "Autostart into tray: ",
"tray_notifications": "Tray notifications: ",
"dark_mode": "Dark mode: ",
"priority_mode": "Priority mode: ",
"proxy": "Proxy (requires restart):",

View File

@@ -152,7 +152,6 @@ class BaseDrop:
self._twitch.print(
_("status", "claimed_drop").format(drop=claim_text.replace("\n", " "))
)
self._twitch.gui.tray.notify(claim_text, _("gui", "tray", "notification_title"))
else:
logger.error(f"Drop claim has potentially failed! Drop ID: {self.id}")
return result

View File

@@ -121,7 +121,6 @@ class WatchService:
channel: The channel to start watching
update_status: Whether to print status message and update status bar
"""
self._twitch.gui.tray.change_icon("active")
self._twitch.gui.channels.set_watching(channel)
self._twitch.watching_channel.set(channel)

View File

@@ -71,7 +71,6 @@ class SettingsUpdate(BaseModel):
games_to_watch: list[str] | None = None
dark_mode: bool | None = None
proxy: str | None = None
tray_notifications: bool | None = None
connection_quality: int | None = None
minimum_refresh_interval_minutes: int | None = None

View File

@@ -16,7 +16,6 @@ from src.web.managers.inventory import InventoryManager
from src.web.managers.login import LoginFormManager
from src.web.managers.settings import SettingsManager
from src.web.managers.status import StatusManager, WebsocketStatusManager
from src.web.managers.tray import TrayIconStub
if TYPE_CHECKING:
@@ -53,7 +52,6 @@ class WebGUIManager:
self.channels = ChannelListManager(self._broadcaster, self)
self.inv = InventoryManager(self._broadcaster, ImageCache(self))
self.login = LoginFormManager(self._broadcaster, self)
self.tray = TrayIconStub(self._broadcaster)
self.settings = SettingsManager(self._broadcaster, twitch.settings)
# Selected channel tracking (set by web client)

View File

@@ -10,7 +10,6 @@ This package contains all component managers for the web-based GUI:
- InventoryManager: Drop campaigns and inventory management
- LoginFormManager: Authentication and OAuth flow handling
- SettingsManager: Application settings configuration
- TrayIconStub: System tray stub (browser notifications in web mode)
- ImageCache: Minimal image caching for campaign artwork
"""
@@ -23,7 +22,6 @@ from src.web.managers.inventory import InventoryManager
from src.web.managers.login import LoginData, LoginFormManager
from src.web.managers.settings import SettingsManager
from src.web.managers.status import StatusManager, WebsocketStatusManager
from src.web.managers.tray import TrayIconStub
__all__ = [
@@ -37,6 +35,5 @@ __all__ = [
"LoginFormManager",
"LoginData",
"SettingsManager",
"TrayIconStub",
"ImageCache",
]

View File

@@ -37,7 +37,6 @@ class SettingsManager:
"games_to_watch": list(self._settings.games_to_watch),
"games_available": self._available_games,
"proxy": str(self._settings.proxy),
"tray_notifications": self._settings.tray_notifications,
"connection_quality": self._settings.connection_quality,
"minimum_refresh_interval_minutes": self._settings.minimum_refresh_interval_minutes,
}
@@ -56,8 +55,6 @@ class SettingsManager:
self._settings.connection_quality = settings_data["connection_quality"]
if "proxy" in settings_data:
self._settings.proxy = settings_data["proxy"]
if "tray_notifications" in settings_data:
self._settings.tray_notifications = settings_data["tray_notifications"]
if "minimum_refresh_interval_minutes" in settings_data:
self._settings.minimum_refresh_interval_minutes = settings_data[
"minimum_refresh_interval_minutes"

View File

@@ -1,51 +0,0 @@
"""Tray icon stub for web-based GUI (no system tray in browser)."""
from __future__ import annotations
import asyncio
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from src.web.managers.broadcaster import WebSocketBroadcaster
class TrayIconStub:
"""Stub implementation for system tray icon functionality.
In web mode, traditional system tray operations are not applicable.
This class provides a compatible interface that translates tray
operations into browser notifications and UI indicators.
"""
def __init__(self, broadcaster: WebSocketBroadcaster):
self._broadcaster = broadcaster
def change_icon(self, icon: str):
"""Change tray icon (translated to UI indicator in web mode).
Args:
icon: Icon name/identifier to change to
"""
# Broadcast icon change for potential UI indicators
asyncio.create_task(self._broadcaster.emit("tray_icon_change", {"icon": icon}))
def notify(self, message: str, title: str):
"""Send a system notification (translated to browser notification).
Args:
message: Notification message body
title: Notification title
"""
# Send browser notification
asyncio.create_task(
self._broadcaster.emit("notification", {"title": title, "message": message})
)
def minimize(self):
"""Minimize to tray (no-op in web mode)."""
pass
def restore(self):
"""Restore from tray (no-op in web mode)."""
pass