update: re-added core imports to ticket handling

This commit is contained in:
JOhn 2026-08-22 11:31:07 -04:00
parent 8a0a1fc74d
commit 40f0a467ab
2 changed files with 145 additions and 65 deletions

View file

@ -11,6 +11,7 @@ from PyQt6 import QtCore
from core.controllers.ProfileController import ProfileController from core.controllers.ProfileController import ProfileController
from gui.v2.actions.profile_order import append_profile_to_visual_order from gui.v2.actions.profile_order import append_profile_to_visual_order
from gui.v2.actions.singbox_prereqs import singbox_prereqs_installed
from gui.v2.ui.pages.Page import Page from gui.v2.ui.pages.Page import Page
from gui.v2.ui.pages.browser_page import BrowserPage from gui.v2.ui.pages.browser_page import BrowserPage
from gui.v2.ui.pages.location_page import LocationPage from gui.v2.ui.pages.location_page import LocationPage
@ -20,6 +21,12 @@ from gui.v2.workers.worker_thread import WorkerThread
class FastRegistrationPage(Page): class FastRegistrationPage(Page):
SINGBOX_PROTOCOLS = ("hysteria2", "vless")
PROTOCOLS = ("wireguard", "hysteria2", "vless", "hidetor")
PROTOCOL_BUTTON_ASSETS = {
"hysteria2": "hystria2",
}
def __init__(self, page_stack, main_window, prepared=None): def __init__(self, page_stack, main_window, prepared=None):
super().__init__("FastRegistration", page_stack, main_window) super().__init__("FastRegistration", page_stack, main_window)
self.page_stack = page_stack self.page_stack = page_stack
@ -85,10 +92,12 @@ class FastRegistrationPage(Page):
def initialize_default_selections(self): def initialize_default_selections(self):
if not self.selected_values['location']: if not self.selected_values['location']:
locations = self.connection_manager.get_location_list() locations = self._available_locations_for_protocol()
if locations: if locations:
random_index = random.randint(0, len(locations) - 1) random_index = random.randint(0, len(locations) - 1)
self.selected_values['location'] = locations[random_index] self.selected_values['location'] = locations[random_index]
else:
self._select_valid_location_for_protocol()
if not self.selected_values['browser']: if not self.selected_values['browser']:
browsers = self.connection_manager.get_browser_list() browsers = self.connection_manager.get_browser_list()
@ -167,8 +176,8 @@ class FastRegistrationPage(Page):
label = QLabel("Protocol", self) label = QLabel("Protocol", self)
label.setGeometry(300, 150, 185, 75) label.setGeometry(300, 150, 185, 75)
protocol_image = QPixmap(os.path.join( protocol_image = QPixmap(self._protocol_button_asset(
self.btn_path, f"{self.selected_values['protocol']}_button.png")) self.selected_values['protocol']))
label.setPixmap(protocol_image) label.setPixmap(protocol_image)
label.setScaledContents(True) label.setScaledContents(True)
label.show() label.show()
@ -196,6 +205,10 @@ class FastRegistrationPage(Page):
next_button.setIconSize(next_button.size()) next_button.setIconSize(next_button.size())
self.buttons.append(next_button) self.buttons.append(next_button)
def _protocol_button_asset(self, protocol):
asset_name = self.PROTOCOL_BUTTON_ASSETS.get(protocol, protocol)
return os.path.join(self.btn_path, f"{asset_name}_button.png")
def create_connection_section(self): def create_connection_section(self):
label = QLabel("Connection", self) label = QLabel("Connection", self)
label.setGeometry(150, 250, 185, 75) label.setGeometry(150, 250, 185, 75)
@ -227,6 +240,8 @@ class FastRegistrationPage(Page):
rotated_icon = icon.transformed(transform) rotated_icon = icon.transformed(transform)
prev_button.setIcon(QIcon(rotated_icon)) prev_button.setIcon(QIcon(rotated_icon))
prev_button.setIconSize(prev_button.size()) prev_button.setIconSize(prev_button.size())
if self.selected_values['protocol'] in self.SINGBOX_PROTOCOLS:
prev_button.setDisabled(True)
self.buttons.append(prev_button) self.buttons.append(prev_button)
next_button = QPushButton(self) next_button = QPushButton(self)
@ -236,6 +251,8 @@ class FastRegistrationPage(Page):
next_button.setIcon( next_button.setIcon(
QIcon(os.path.join(self.btn_path, "UP_button.png"))) QIcon(os.path.join(self.btn_path, "UP_button.png")))
next_button.setIconSize(next_button.size()) next_button.setIconSize(next_button.size())
if self.selected_values['protocol'] in self.SINGBOX_PROTOCOLS:
next_button.setDisabled(True)
self.buttons.append(next_button) self.buttons.append(next_button)
def create_location_section(self): def create_location_section(self):
@ -302,7 +319,7 @@ class FastRegistrationPage(Page):
locations = self.connection_manager.get_location_info( locations = self.connection_manager.get_location_info(
self.selected_values['location']) self.selected_values['location'])
if self.selected_values['protocol'] == 'hidetor' and locations and not (hasattr(locations, 'is_proxy_capable') and locations.is_proxy_capable): if locations and not self._location_supports_selected_protocol(locations):
label.hide() label.hide()
else: else:
label.show() label.show()
@ -529,46 +546,34 @@ class FastRegistrationPage(Page):
def update_ui_state_for_connection(self): def update_ui_state_for_connection(self):
is_system_wide = self.selected_values['connection'] == 'system-wide' is_system_wide = self.selected_values['connection'] == 'system-wide'
is_singbox = self.selected_values['protocol'] in self.SINGBOX_PROTOCOLS
for button in self.buttons: for button in self.buttons:
if hasattr(button, 'geometry'): if hasattr(button, 'geometry'):
button_geometry = button.geometry() button_geometry = button.geometry()
if is_singbox and button_geometry.y() == 250 and button_geometry.x() in [115, 340]:
button.setEnabled(False)
if button_geometry.y() == 350: if button_geometry.y() == 350:
if button_geometry.x() in [115, 340, 400, 625]: if button_geometry.x() in [115, 340, 400, 625]:
button.setEnabled(not is_system_wide) button.setEnabled(not is_system_wide)
def show_previous_value(self, key): def show_previous_value(self, key):
if key == 'protocol': if key == 'protocol':
protocols = ['wireguard', 'hidetor'] protocols = list(self.PROTOCOLS)
current_index = protocols.index(self.selected_values[key]) current_index = protocols.index(self.selected_values[key])
previous_index = (current_index - 1) % len(protocols) previous_index = (current_index - 1) % len(protocols)
self.selected_values[key] = protocols[previous_index] self.selected_values[key] = protocols[previous_index]
if self.selected_values[key] == 'wireguard': self._apply_protocol_defaults()
self.selected_values['connection'] = 'browser-only'
else:
self.selected_values['connection'] = 'tor'
loc_info = self.connection_manager.get_location_info(
self.selected_values['location'])
if not (loc_info and hasattr(loc_info, 'is_proxy_capable') and loc_info.is_proxy_capable):
locations = self.connection_manager.get_location_list()
proxy_locations = [loc for loc in locations if (l := self.connection_manager.get_location_info(
loc)) and hasattr(l, 'is_proxy_capable') and l.is_proxy_capable]
if proxy_locations:
self.selected_values['location'] = proxy_locations[0]
elif key == 'connection': elif key == 'connection':
if self.selected_values['protocol'] == 'wireguard': connections = self._connections_for_protocol()
connections = ['browser-only', 'system-wide'] if self.selected_values[key] not in connections:
else: self.selected_values[key] = connections[0]
connections = ['tor', 'just proxy']
current_index = connections.index(self.selected_values[key]) current_index = connections.index(self.selected_values[key])
previous_index = (current_index - 1) % len(connections) previous_index = (current_index - 1) % len(connections)
self.selected_values[key] = connections[previous_index] self.selected_values[key] = connections[previous_index]
self.update_ui_state_for_connection() self.update_ui_state_for_connection()
elif key == 'location': elif key == 'location':
locations = self.connection_manager.get_location_list() locations = self._available_locations_for_protocol()
if self.selected_values['protocol'] == 'hidetor':
locations = [loc for loc in locations if (l := self.connection_manager.get_location_info(
loc)) and hasattr(l, 'is_proxy_capable') and l.is_proxy_capable]
if locations and self.selected_values[key] in locations: if locations and self.selected_values[key] in locations:
current_index = locations.index(self.selected_values[key]) current_index = locations.index(self.selected_values[key])
@ -617,36 +622,21 @@ class FastRegistrationPage(Page):
def show_next_value(self, key): def show_next_value(self, key):
if key == 'protocol': if key == 'protocol':
protocols = ['wireguard', 'hidetor'] protocols = list(self.PROTOCOLS)
current_index = protocols.index(self.selected_values[key]) current_index = protocols.index(self.selected_values[key])
next_index = (current_index + 1) % len(protocols) next_index = (current_index + 1) % len(protocols)
self.selected_values[key] = protocols[next_index] self.selected_values[key] = protocols[next_index]
if self.selected_values[key] == 'wireguard': self._apply_protocol_defaults()
self.selected_values['connection'] = 'browser-only'
else:
self.selected_values['connection'] = 'tor'
loc_info = self.connection_manager.get_location_info(
self.selected_values['location'])
if not (loc_info and hasattr(loc_info, 'is_proxy_capable') and loc_info.is_proxy_capable):
locations = self.connection_manager.get_location_list()
proxy_locations = [loc for loc in locations if (l := self.connection_manager.get_location_info(
loc)) and hasattr(l, 'is_proxy_capable') and l.is_proxy_capable]
if proxy_locations:
self.selected_values['location'] = proxy_locations[0]
elif key == 'connection': elif key == 'connection':
if self.selected_values['protocol'] == 'wireguard': connections = self._connections_for_protocol()
connections = ['browser-only', 'system-wide'] if self.selected_values[key] not in connections:
else: self.selected_values[key] = connections[0]
connections = ['tor', 'just proxy']
current_index = connections.index(self.selected_values[key]) current_index = connections.index(self.selected_values[key])
next_index = (current_index + 1) % len(connections) next_index = (current_index + 1) % len(connections)
self.selected_values[key] = connections[next_index] self.selected_values[key] = connections[next_index]
self.update_ui_state_for_connection() self.update_ui_state_for_connection()
elif key == 'location': elif key == 'location':
locations = self.connection_manager.get_location_list() locations = self._available_locations_for_protocol()
if self.selected_values['protocol'] == 'hidetor':
locations = [loc for loc in locations if (l := self.connection_manager.get_location_info(
loc)) and hasattr(l, 'is_proxy_capable') and l.is_proxy_capable]
if locations and self.selected_values[key] in locations: if locations and self.selected_values[key] in locations:
current_index = locations.index(self.selected_values[key]) current_index = locations.index(self.selected_values[key])
@ -693,6 +683,57 @@ class FastRegistrationPage(Page):
self.create_interface_elements() self.create_interface_elements()
def _connections_for_protocol(self):
protocol = self.selected_values['protocol']
if protocol == 'wireguard':
return ['browser-only', 'system-wide']
if protocol in self.SINGBOX_PROTOCOLS:
return ['system-wide']
return ['tor', 'just proxy']
def _apply_protocol_defaults(self):
protocol = self.selected_values['protocol']
if protocol == 'wireguard':
self.selected_values['connection'] = 'browser-only'
elif protocol in self.SINGBOX_PROTOCOLS:
self.selected_values['connection'] = 'system-wide'
else:
self.selected_values['connection'] = 'tor'
self._select_valid_location_for_protocol()
def _is_enabled_capability(self, value):
return value in (True, 1, "1", "true", "True")
def _location_supports_selected_protocol(self, location_info):
protocol = self.selected_values['protocol']
if protocol == 'hidetor':
return bool(getattr(location_info, 'is_proxy_capable', False))
capability_by_protocol = {
"wireguard": "is_wireguard_capable",
"hysteria2": "is_hysteria2_capable",
"vless": "is_vless_capable",
}
capability_name = capability_by_protocol.get(protocol)
if capability_name is None:
return True
return self._is_enabled_capability(getattr(location_info, capability_name, False))
def _available_locations_for_protocol(self):
locations = self.connection_manager.get_location_list()
return [
loc for loc in locations
if (info := self.connection_manager.get_location_info(loc))
and self._location_supports_selected_protocol(info)
]
def _select_valid_location_for_protocol(self):
locations = self._available_locations_for_protocol()
if not locations:
self.selected_values['location'] = ''
return
if self.selected_values['location'] not in locations:
self.selected_values['location'] = locations[0]
def go_back(self): def go_back(self):
menu_page = self.custom_window.navigator.get_cached("menu") menu_page = self.custom_window.navigator.get_cached("menu")
if menu_page is not None and hasattr(menu_page, 'refresh_menu_buttons'): if menu_page is not None and hasattr(menu_page, 'refresh_menu_buttons'):
@ -734,6 +775,9 @@ class FastRegistrationPage(Page):
if self.selected_values['protocol'] == 'wireguard': if self.selected_values['protocol'] == 'wireguard':
self.create_wireguard_profile(profile_data) self.create_wireguard_profile(profile_data)
elif self.selected_values['protocol'] in self.SINGBOX_PROTOCOLS:
self.create_singbox_profile(profile_data)
return
else: else:
self.create_tor_profile(profile_data) self.create_tor_profile(profile_data)
@ -778,6 +822,58 @@ class FastRegistrationPage(Page):
profile_id, profile_id,
profiles.keys()) profiles.keys())
def create_singbox_profile(self, profile_data):
location_info = self.connection_manager.get_location_info(
profile_data['location'])
if not location_info:
self.update_status.update_status('Invalid location selected')
return
profiles = self._prefetched_profiles if self._prefetched_profiles is not None else ProfileController.get_all()
profile_id = self.get_next_available_profile_id(profiles)
existing_profile_ids = tuple(profiles.keys())
if not singbox_prereqs_installed():
self.show_singbox_prereq_setup(profile_data, profile_id, existing_profile_ids)
return
self.finish_singbox_profile_creation(profile_data, profile_id, existing_profile_ids)
def show_singbox_prereq_setup(self, profile_data, profile_id, existing_profile_ids):
setup_page = self.custom_window.navigator.navigate("networking_setup")
if setup_page is None:
self.update_status.update_status("Singbox prerequisite page is unavailable.")
return
setup_page.configure_for_singbox_profile(
lambda: self.finish_singbox_profile_creation(profile_data, profile_id, existing_profile_ids))
def finish_singbox_profile_creation(self, profile_data, profile_id, existing_profile_ids):
location_info = self.connection_manager.get_location_info(
profile_data['location'])
if not location_info:
self.update_status.update_status('Invalid location selected')
return
profile_data_for_resume = {
'id': profile_id,
'name': profile_data['name'],
'country_code': location_info.country_code,
'code': location_info.code,
'application': '',
'connection_type': profile_data['protocol'],
'resolution': '',
}
self._spawn_create_profile_worker(
'CREATE_SYSTEM_PROFILE', profile_data_for_resume, 'system')
if ProfileController.get(profile_id) is not None:
append_profile_to_visual_order(
getattr(self.update_status, 'gui_config_file', None),
profile_id,
existing_profile_ids)
self.go_back()
def create_tor_profile(self, profile_data): def create_tor_profile(self, profile_data):
location_info = self.connection_manager.get_location_info( location_info = self.connection_manager.get_location_info(
profile_data['location']) profile_data['location'])

View file

@ -1,12 +1,11 @@
import os import os
import shutil
from PyQt6.QtWidgets import QButtonGroup, QMessageBox, QPushButton from PyQt6.QtWidgets import QButtonGroup, QMessageBox, QPushButton
from PyQt6.QtGui import QIcon from PyQt6.QtGui import QIcon
from PyQt6.QtCore import QSize from PyQt6.QtCore import QSize
from PyQt6 import QtCore from PyQt6 import QtCore
from core.Constants import Constants from core.services.prepare_tickets.ticket_tracker import delete_ticket_data
from gui.v2.ui.pages.Page import Page from gui.v2.ui.pages.Page import Page
from gui.v2.ui.popups.message_box import style_message_box, mark_confirm_button from gui.v2.ui.popups.message_box import style_message_box, mark_confirm_button
@ -135,29 +134,14 @@ class TicketCryptoPickerPage(Page):
self.update_status.update_status("Cancelled.") self.update_status.update_status("Cancelled.")
def _restart_payment_after_wipe(self): def _restart_payment_after_wipe(self):
if not self._delete_ticket_data(): if not delete_ticket_data():
self.update_status.update_status("Could not wipe ticket data.")
return return
self.bypass_existing = False self.bypass_existing = False
currency_btn = self.buttonGroup.checkedButton() currency_btn = self.buttonGroup.checkedButton()
if currency_btn: if currency_btn:
self.start_initiate_payment(currency_btn.property('currency')) self.start_initiate_payment(currency_btn.property('currency'))
def _delete_ticket_data(self):
paths = [
Constants.TICKET_TRACKER_PATH,
os.path.join(Constants.HV_TICKETING_CONFIG_HOME, "billing_choices.json"),
]
try:
for path in paths:
if os.path.exists(path):
os.remove(path)
if os.path.isdir(Constants.HV_TICKETING_DATA_HOME):
shutil.rmtree(Constants.HV_TICKETING_DATA_HOME)
return True
except OSError as error:
self.update_status.update_status(f"Could not wipe ticket data: {error}")
return False
def on_error(self, msg): def on_error(self, msg):
self.update_status.update_status(f"Payment error: {msg}") self.update_status.update_status(f"Payment error: {msg}")