From 707935f80f9fa5423be37b241c51d2008fe14382 Mon Sep 17 00:00:00 2001 From: SimplifiedPrivacy Date: Tue, 20 Oct 2026 18:28:38 -0400 Subject: [PATCH] Improved Status Updates with Notification system for payment prompts --- gui/main_ui.py | 10 +++++++ gui/v2/actions/menu_coordinator.py | 2 +- gui/v2/actions/payment_actions.py | 42 +++++++++++++++++++++--------- gui/v2/ui/pages/menu_page.py | 9 ++++++- gui/v2/ui/pages/payments2.py | 3 +++ 5 files changed, 52 insertions(+), 14 deletions(-) diff --git a/gui/main_ui.py b/gui/main_ui.py index a2616fe..12190a7 100755 --- a/gui/main_ui.py +++ b/gui/main_ui.py @@ -23,6 +23,7 @@ from core.controllers.ProfileController import ProfileController from core.Errors import UnknownConnectionTypeError from core.errors.logger import logger as core_logger from core.models.Result import Result, ResultError +from core.observers.Notifications import Notifications, EventType core_logger.propagate = False @@ -161,8 +162,12 @@ class CustomWindow(QMainWindow): self.sync_button = bottom['sync_button'] self.page_stack.raise_() + # class based notification system: self.observers = setup_observers(self.update_status) + # New EventBus-style notification system: + self.notfication_trigger() + self.update_image() self.set_scroll_speed(7) @@ -753,6 +758,11 @@ class CustomWindow(QMainWindow): self.status_label.setStyleSheet( f"color: rgb(0, 255, 255); font-size: {font_size}px;") + @mainthread + def notfication_trigger(self): + Notifications.subscribe(EventType.STATUS_UPDATE, self.update_status) + + @mainthread # wrapper of ThreadSafetyTool def update_status(self, text, clear=False): """ diff --git a/gui/v2/actions/menu_coordinator.py b/gui/v2/actions/menu_coordinator.py index e716286..ffd2ece 100644 --- a/gui/v2/actions/menu_coordinator.py +++ b/gui/v2/actions/menu_coordinator.py @@ -14,7 +14,7 @@ from core.errors.translate_error import translate_error, get_ignored_exceptions from core.services.payment_phase.payment_state import OverallPaymentCycle # gui -from gui.v2.actions.payment_actions import wipe_expired_subscription +# from gui.v2.actions.payment_actions import wipe_expired_subscription from gui.v2.actions.ticket_actions import dispatch_ticketing from gui.v2.infrastructure.action_registry import register_action from gui.v2.infrastructure.take_plumbing_action import take_action diff --git a/gui/v2/actions/payment_actions.py b/gui/v2/actions/payment_actions.py index 31f49e5..abf57c3 100644 --- a/gui/v2/actions/payment_actions.py +++ b/gui/v2/actions/payment_actions.py @@ -11,6 +11,7 @@ from core.models.enum_models import PaymentType, PaymentCategory from core.models.Result import Result, ResultError from core.errors.logger import logger from core.utils.type_checker import type_checker +from core.observers.Notifications import Notifications, EventType # gui @@ -55,18 +56,25 @@ def reset_individual_billing_cycle() -> Result: return _wipe_and_navigate() -@register_action(ResultError.EXPIRED_SUB) -def wipe_expired_subscription(profile_id: int) -> Result: - """Call core to wipe a subscription, then navigate to the billing page""" - logger.info(f"Wiping the subscription for {profile_id}") +# @register_action(ResultError.EXPIRED_SUB) +# def wipe_expired_subscription(profile_id: int) -> Result: +# """Call core to wipe a subscription, then navigate to the billing page""" +# logger.info(f"Wiping the subscription for {profile_id}") - wiped_result = wipe_subscription(profile_id) - if wiped_result.valid: - logger.info("Successfully wiped") - # also clear the whole billing cycle, so it's ready for an individual purchase, - return _wipe_and_navigate(profile_id) - else: - return wiped_result +# wiped_result = wipe_subscription(profile_id) +# if wiped_result.valid: +# logger.info("Successfully wiped") + + + +# payment_cycle = OverallPaymentCycle() +# payment_cycle.wipe_billing_cycle() + + +# # also clear the whole billing cycle, so it's ready for an individual purchase, +# return _wipe_and_navigate(profile_id) +# else: +# return wiped_result @type_checker @@ -108,6 +116,9 @@ def gui_starts_invoice(invoice_ui_caller: Callable, timer_resume_caller: Callabl else: return return_pre_existing_invoice(billing_code=billing_code, connection_observer=connection_observer) + notification = "Fetching Invoice.." + Notifications.emit(EventType.STATUS_UPDATE, notification) + take_action( action=create_new_invoice, ui_caller=invoice_ui_caller, @@ -149,6 +160,9 @@ def start_polling(billing_code: str, profile_id: int, ui_caller: Callable) -> st # update the global singleton: payment_cycle.already_polling = True + notification = "Checking if Paid.." + Notifications.emit(EventType.STATUS_UPDATE, notification) + # setup the timer with the callback: # it doesn't return result here. It calls the view_callback function. check_paid_timer = CheckPaidTimer( @@ -166,7 +180,7 @@ def start_polling(billing_code: str, profile_id: int, ui_caller: Callable) -> st def stop_current() -> Result: wiped_result = wipe_billing_cycle() - logger.info("Stopping current!") + logger.info("Stopping current billing cycle!") # if not wiped_result.valid: # return wiped_result @@ -176,6 +190,8 @@ def stop_current() -> Result: # if not new_cycle_result.valid: # return new_cycle_result + notification = "Stopped Checking if Paid" + Notifications.emit(EventType.STATUS_UPDATE, notification) return Result(valid=True, error_type=ResultError.NAVIGATE_TO, navigate_to="menu") @@ -194,6 +210,8 @@ def stop_payment_timer() -> Result: if wiped_result.valid: content_to_display = "Stopped! You can resume a different profile's invoice now" + notification = "Stopped Checking if Paid" + Notifications.emit(EventType.STATUS_UPDATE, notification) return Result(valid=True, message=content_to_display) else: error_msg = "Unable to stop the payment timer and wipe the profile inside stop_payment_timer. But didn't get an actual error." diff --git a/gui/v2/ui/pages/menu_page.py b/gui/v2/ui/pages/menu_page.py index 58843e4..40916f7 100755 --- a/gui/v2/ui/pages/menu_page.py +++ b/gui/v2/ui/pages/menu_page.py @@ -145,9 +145,15 @@ class MenuPage(Page): self.refresh_menu_buttons() + def trigger_action_button_visuals(self, desired_status: bool): + """Backend uses direct Notifications to trigger the action button status, without a profile_id""" + if desired_status: + self.action_button.setEnabled(True) + self.action_button.setVisible(True) + def update_action_button_visuals(self, triggered_profile_id: Optional[int] = None): - """Syncs the button art and enabled state with the current external truth.""" + """Syncs the button art with enabled state with the current external truth.""" # Which profile is selected: profile_id = getattr(self.update_status, 'current_profile_id', None) @@ -818,6 +824,7 @@ class MenuPage(Page): Notifications.subscribe(EventType.SESSION_PROFILE_DISABLE, self.turn_off_session_visuals) Notifications.subscribe(EventType.SYSTEM_ENABLE, self.turn_on_system_visuals) Notifications.subscribe(EventType.SYSTEM_DISABLE, self.turn_off_system_visuals) + Notifications.subscribe(EventType.RESUME_ACTIONS, self.trigger_action_button_visuals) take_action( action=evaluate_state_on_startup, diff --git a/gui/v2/ui/pages/payments2.py b/gui/v2/ui/pages/payments2.py index 9bcb79a..2dfb150 100644 --- a/gui/v2/ui/pages/payments2.py +++ b/gui/v2/ui/pages/payments2.py @@ -5,6 +5,7 @@ from core.models.pydantic_models.ServerReply import SubscriptionInvoice from core.models.invoice.TicketInvoice import TicketInvoice from core.models.enum_models import Currency from core.models.Result import Result +from core.observers.Notifications import Notifications, EventType # GUI from gui.v2.actions.payment_actions import ( @@ -233,6 +234,8 @@ class PaymentDetailsPage(Page): def background_fetch_mode(self): """Continue checking if paid, but navigate back to the menu page""" if self.polling: + notification = "Checking if Paid in Background.." + Notifications.emit(EventType.STATUS_UPDATE, notification) self.custom_window.navigator.navigate("menu") else: message = "It was not polling to begin with."