Updated Protocol Page / Update Flow / Connection Type

This commit is contained in:
Your Name 2025-04-25 20:07:40 +01:00
parent f0fadbdd3a
commit 9b6deffa61
3 changed files with 296 additions and 237 deletions

View file

@ -1,5 +1,5 @@
from PyQt6.QtWidgets import QComboBox,QButtonGroup, QLineEdit,QMainWindow, QLabel, QWidget, QVBoxLayout, QStackedWidget, QApplication, QPushButton, QTextEdit, QFrame, QHBoxLayout, QVBoxLayout, QScrollArea, QSystemTrayIcon, QMessageBox, QGridLayout, QCheckBox, QStackedLayout, QGroupBox
from PyQt6.QtGui import QIcon, QPixmap,QIcon, QPixmap, QTransform, QPainter, QColor, QFont
from PyQt6.QtGui import QIcon, QPixmap,QIcon, QPixmap, QTransform, QPainter, QColor, QFont, QPen
from PyQt6 import QtGui
from PyQt6 import QtCore
from PyQt6.QtCore import Qt,QSize,QThread,pyqtSignal, QTimer, QPointF, QRect, QMutex, QMutexLocker, QObject
@ -12,7 +12,7 @@ import traceback
import random
import subprocess
from typing import Union
from core.Errors import CommandNotFoundError, MissingSubscriptionError, InvalidSubscriptionError, ProfileActivationError, UnsupportedApplicationVersionError, FileIntegrityError, ProfileModificationError, ProfileStateConflictError
from core.Errors import UnknownConnectionTypeError, CommandNotFoundError, MissingSubscriptionError, InvalidSubscriptionError, ProfileActivationError, UnsupportedApplicationVersionError, FileIntegrityError, ProfileModificationError, ProfileStateConflictError
from core.controllers.ApplicationVersionController import ApplicationVersionController
from core.controllers.ApplicationController import ApplicationController
from core.controllers.ClientController import ClientController
@ -77,10 +77,8 @@ class WorkerThread(QThread):
self.get_subscription()
elif self.action == 'DISABLE_PROFILE':
self.disable_profile()
elif self.action == 'SYNC':
elif self.action == 'SYNC' or self.action == 'SYNC_TOR':
self.sync()
elif self.action == 'SYNC_TOR':
self.tor_sync()
elif self.action == 'GET_CONNECTION':
self.get_connection()
elif self.action == 'SET_CONNECTION':
@ -116,7 +114,7 @@ class WorkerThread(QThread):
def download_update(self):
self.text_output.emit("Starting update process...")
ClientController.__update(client_observer=client_observer)
ClientController.update(client_observer=client_observer)
client_observer.subscribe('update_progressing', lambda event: self.text_output.emit(f"Downloading: {event.meta.get('progress'):.1f}%"))
client_observer.subscribe('updated', lambda event: self.text_output.emit("Update process completed"))
@ -230,7 +228,10 @@ class WorkerThread(QThread):
def sync(self):
try:
ConfigurationController.set_connection('system')
if self.action == 'SYNC_TOR':
ConfigurationController.set_connection('tor')
else:
ConfigurationController.set_connection('system')
ClientController.sync(client_observer=client_observer, connection_observer=connection_observer)
locations = LocationController.get_all()
print(locations)
@ -240,18 +241,6 @@ class WorkerThread(QThread):
print(e)
self.sync_output.emit([], False, False, [], False)
def tor_sync(self):
try:
ConfigurationController.set_connection('tor')
self.text_output.emit('Syncing in progress...')
ClientController.sync(client_observer=client_observer, connection_observer=connection_observer)
locations = LocationController.get_all()
all_location_codes = [location.country_name for location in locations]
self.sync_output.emit(all_location_codes, True, True, locations, False)
except ConnectionError:
self.sync_output.emit([], False, False, [], False)
except CommandNotFoundError:
self.sync_output.emit([], False, False, [], True)
def get_connection(self):
@ -337,7 +326,10 @@ class CustomWindow(QMainWindow):
self.current_profile_id = None
self.connection_manager = ConnectionManager()
current_connection = ConfigurationController.get_connection()
current_connection = self.get_current_connection()
if current_connection is None:
ConfigurationController.set_connection('system')
current_connection = 'system'
self.is_tor_mode = current_connection == 'tor'
self.is_animating = False
self.animation_step = 0
@ -416,6 +408,13 @@ class CustomWindow(QMainWindow):
self.check_logging()
self.init_ui()
def get_current_connection(self):
try:
return ConfigurationController.get_connection()
except UnknownConnectionTypeError:
return None
def _setup_gui_directory(self):
os.makedirs(self.gui_dir, exist_ok=True)
@ -739,26 +738,24 @@ class CustomWindow(QMainWindow):
self.menu_page = MenuPage(self.page_stack, self)
self.editor_page = EditorPage(self.page_stack, self)
self.pages = [MenuPage(self.page_stack, self),
ProtocolPage(self.page_stack, self),
WireGuardPage(self.page_stack, self),
ResidentialPage(self.page_stack, self),
HidetorPage(self.page_stack, self),
lokinetPage(self.page_stack, self),
OpenPage(self.page_stack, self),
TorPage(self.page_stack, self),
TorLocationPage(self.page_stack, self),
PaymentPage(self.page_stack, self),
LocationPage(self.page_stack, self),
BrowserPage(self.page_stack, self),
ScreenPage(self.page_stack, self),
ResumePage(self.page_stack, self),
IdPage(self.page_stack, self),
InstallSystemPackage(self.page_stack, self),
WelcomePage(self.page_stack, self),
PaymentConfirmed(self.page_stack, self),
EditorPage(self.page_stack, self),
Settings(self.page_stack, self),
ConnectionPage(self.page_stack, self)]
ProtocolPage(self.page_stack, self),
LocationPage(self.page_stack, self),
BrowserPage(self.page_stack, self),
ScreenPage(self.page_stack, self),
ResumePage(self.page_stack, self),
WireGuardPage(self.page_stack, self),
ResidentialPage(self.page_stack, self),
HidetorPage(self.page_stack, self),
InstallSystemPackage(self.page_stack, self),
WelcomePage(self.page_stack, self),
PaymentPage(self.page_stack, self),
PaymentConfirmed(self.page_stack, self),
IdPage(self.page_stack, self),
TorPage(self.page_stack, self),
EditorPage(self.page_stack, self),
Settings(self.page_stack, self),
ConnectionPage(self.page_stack, self),
SyncScreen(self.page_stack, self)]
for page in self.pages:
self.page_stack.addWidget(page)
# Conectar la señal currentChanged al método page_changed
@ -801,7 +798,7 @@ class CustomWindow(QMainWindow):
self.disable_marquee()
def check_first_launch(self):
config_file = os.path.join(Constants.HV_CONFIG_HOME, '.first_launch')
config_file = os.path.join(self.gui_dir, "first_launch.txt")
os.makedirs(Constants.HV_CONFIG_HOME, exist_ok=True)
@ -875,9 +872,7 @@ class CustomWindow(QMainWindow):
elif isinstance(current_page, ResumePage):
self.update_status('Choose a name for your profile')
current_page.eliminacion()
elif isinstance(current_page, ProtocolPage):
if not self.connection_manager.is_synced():
self.update_status('Choose a sync method')
elif isinstance(current_page, IdPage):
pass
elif isinstance(current_page, LocationPage):
@ -1026,8 +1021,6 @@ class Worker(QObject):
self.update_signal.emit("Application version file integrity could not be verified.", False, None, None, None)
except ProfileModificationError:
self.update_signal.emit("WireGuard configuration could not be attached.", False, None, None, None)
#except OSError:
# self.update_signal.emit("Connection could not be established.", False, None, None, None)
except ProfileStateConflictError:
self.update_signal.emit("The profile is already being enabled...", False, None, None, None)
except CommandNotFoundError as e :
@ -1111,79 +1104,23 @@ class MenuPage(Page):
self.is_system_connected = False
self.profile_button_map = {}
self.update_button = QPushButton(self)
self.update_button.setGeometry(750, 530, 25, 25)
self.update_button.setObjectName("update_button")
update_icon = QtGui.QIcon(os.path.join(self.btn_path, "available-updates.png"))
self.update_button.setIcon(update_icon)
self.update_button.setIconSize(self.update_button.size())
self.update_button.clicked.connect(self.check_updates)
#self.label.setStyleSheet("background-color: rgba(0, 255, 0, 51);")
self.create_interface_elements() # Establecer el color de fondo y el color del texto utilizando hojas de estilo
def check_updates(self):
self.update_status.update_status("Checking for updates...")
self.worker = WorkerThread('CHECK_FOR_UPDATE')
self.worker.text_output.connect(self.update_status.update_status)
self.worker.finished.connect(self.on_update_check_finished)
self.worker.start()
def on_update_check_finished(self, result):
if result:
def on_update_check_finished(self):
reply = QMessageBox.question(self, 'Update Available',
'An update is available. Would you like to download it?',
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
if reply == QMessageBox.StandardButton.Yes:
self.worker = WorkerThread('DOWNLOAD_UPDATE')
self.worker.text_output.connect(self.update_status.update_status)
self.worker.update_finished.connect(self.on_update_download_finished)
self.worker.start()
else:
self.update_status.update_status("Update cancelled by user.")
else:
self.update_status.update_status("No updates available.")
def on_update_download_finished(self, result: dict):
if result["status"] and result["path"]:
self.countdown_timer = QTimer()
self.countdown_timer.setInterval(1000)
self.countdown_seconds = 3
self.new_app_path = result["path"]
def update_countdown():
if self.countdown_seconds > 0:
self.update_status.update_status(f"New version downloaded. Restarting in {self.countdown_seconds} second{'s' if self.countdown_seconds > 1 else ''}...")
self.countdown_seconds -= 1
else:
self.countdown_timer.stop()
self.restart_application()
def restart_application():
QApplication.quit()
current_app = Constants.APPIMAGE_PATH
if os.path.exists(self.new_app_path):
try:
os.remove(current_app)
os.execv(self.new_app_path, [self.new_app_path] + sys.argv[1:])
except PermissionError:
self.update_status.update_status(f"Permission denied: Unable to remove {current_app}")
else:
self.update_status.update_status(f"Error: New application not found at {self.new_app_path}")
self.restart_application = restart_application
self.countdown_timer.timeout.connect(update_countdown)
self.countdown_timer.start()
atexit.register(restart_application)
else:
self.update_status.update_status("Failed to download update.")
def create_interface_elements(self):
for icon_name, function, position in [
@ -1580,11 +1517,6 @@ class MenuPage(Page):
self.page_stack.currentWidget().add_selected_profile(editar_profile)
def launch(self):
pass
@ -1648,7 +1580,10 @@ class MenuPage(Page):
if len(self.profile_info) == 6:
self.update_status.update_status('Maximum number of profiles reached!')
return
self.page_stack.setCurrentIndex(self.page_stack.indexOf(self.page_stack.findChild(ProtocolPage)))
if not self.connection_manager.is_synced():
self.page_stack.setCurrentIndex(self.page_stack.indexOf(self.page_stack.findChild(SyncScreen)))
else:
self.page_stack.setCurrentIndex(self.page_stack.indexOf(self.page_stack.findChild(ProtocolPage)))
@ -2517,138 +2452,12 @@ class ProtocolPage(Page):
self.coming_soon_label.setGeometry(210, 50, 200, 40)
self.coming_soon_label.setStyleSheet("font-size: 22px;")
self.sync_label = QLabel("Sync finds out what plans, countries, \n& browsers are available", self)
self.sync_label.setGeometry(100, 120,500,60)
self.sync_label.setStyleSheet("font-size: 20px;")
self.button_sync = QPushButton(self)
self.button_sync.setObjectName("sync_button")
self.button_sync.setGeometry(100, 250,150,60)
self.button_sync.setIconSize(self.button_sync.size())
icon = QIcon(os.path.join(self.btn_path, "sync_button.png"))
self.button_sync.setIcon(icon)
self.button_sync.clicked.connect(self.handle_core_action_sync_button)
self.tor_button_sync = QPushButton(self)
self.tor_button_sync.setObjectName("tor_sync")
self.tor_button_sync.setGeometry(300, 250,150,60)
self.tor_button_sync.setIconSize(self.tor_button_sync.size())
tor_icon = QIcon(os.path.join(self.btn_path, "tor_sync.png"))
self.tor_button_sync.setIcon(tor_icon)
self.tor_button_sync.clicked.connect(lambda: self.handle_core_action_sync_button(True))
self.button_sync.setVisible(True)
self.tor_button_sync.setVisible(True)
self.coming_soon_label.setVisible(False)
self.title.setGeometry(585, 40, 185, 40); self.title.setText("Pick a Protocol")
self.display.setGeometry(QtCore.QRect(0, 50, 580, 435))#relacion 4:3
self.create_interface_elements()
def sync_button(self, toggle: bool, sync_complete: bool=False):
self.button_sync.setDisabled(toggle)
self.tor_button_sync.setDisabled(toggle)
if sync_complete:
for button in self.buttons:
button.setDisabled(toggle)
self.button_sync.setVisible(False)
self.tor_button_sync.setVisible(False)
self.sync_label.setVisible(False)
def handle_core_action_sync_button(self, tor: bool = False):
self.sync_button(True)
if tor:
self.main_window.set_toggle_state(True)
self.sync_button(True)
self.update_status.update_status('Syncing through Tor in progress...')
self.worker_thread = WorkerThread('SYNC_TOR')
self.worker_thread.sync_output.connect(self.update_output)
self.worker_thread.text_output.connect(self.update_text_output)
self.worker_thread.start()
return
self.update_status.update_status('Syncing in progress...')
self.worker_thread = WorkerThread('SYNC')
self.worker_thread.sync_output.connect(self.update_output)
self.worker_thread.start()
def update_text_output(self, text):
self.update_status.update_status(text)
def update_output(self, available_locations, status, is_tor, locations, is_os_error):
if is_os_error:
install_page = self.page_stack.findChild(InstallSystemPackage)
install_page.configure(package_name='tor', distro='debian', is_sync=True)
self.page_stack.setCurrentIndex(self.page_stack.indexOf(install_page))
self.sync_button(False)
return
if status is False:
self.sync_button(False)
self.update_status.update_status('An error occurred during sync')
return
self.update_status.update_status('Sync complete')
self.connection_manager.set_synced(True)
available_locations_list = []
self.connection_manager.store_locations(locations)
grid_positions = [
(395, 90, 185, 75),
(585, 90, 185, 75),
(395, 195, 185, 75),
(585, 195, 185, 75),
(395, 300, 185, 75),
(585, 300, 185, 75),
(395, 405, 185, 75),
(585, 405, 185, 75)
]
list1 = []
for i, location in enumerate(available_locations):
position_index = i % len(grid_positions)
list1.append((QPushButton, location, grid_positions[position_index]))
for item in list1:
available_locations_list.append(item)
self.sync_button(False, True)
location_page = self.find_location_page()
hidetor_page = self.find_hidetor_page()
print(f' the available locations list is {available_locations_list}')
if location_page:
location_page.create_interface_elements(available_locations_list)
if hidetor_page:
hidetor_page.create_interface_elements(available_locations_list)
def find_location_page(self):
for i in range(self.page_stack.count()):
page = self.page_stack.widget(i)
if isinstance(page, LocationPage):
return page
return None
def find_hidetor_page(self):
for i in range(self.page_stack.count()):
page = self.page_stack.widget(i)
if isinstance(page, HidetorPage):
return page
return None
def create_interface_elements(self):
self.buttonGroup = QButtonGroup(self)
self.buttons = []
@ -2668,6 +2477,10 @@ class ProtocolPage(Page):
self.buttonGroup.addButton(boton, j)
boton.clicked.connect(lambda _, page=page_class, protocol=icon_name: self.show_protocol(page, protocol))
def enable_protocol_buttons(self):
for button in self.buttons:
button.setDisabled(False)
def update_swarp_json(self):
self.update_status.write_data({"protocol": self.selected_protocol_icon})
@ -5724,6 +5537,252 @@ class ClickableValueLabel(QLabel):
self.setStyleSheet(self.default_style)
class SyncScreen(Page):
def __init__(self, page_stack, main_window=None, parent=None):
super().__init__("SyncScreen", page_stack, main_window, parent)
self.main_window = main_window
self.btn_path = main_window.btn_path
self.update_status = main_window
self.connection_manager = main_window.connection_manager
self.is_tor_mode = main_window.is_tor_mode
self.heading_label = QLabel("You're about to fetch data from\nSimplified Privacy.", self)
self.heading_label.setGeometry(15, 80, 750, 80)
self.heading_label.setStyleSheet("font-size: 36px; font-weight: bold; color: white;")
self.data_description = QLabel("This data includes what plans, countries,\nbrowsers, and version upgrades are\navailable. As well as coordinating billing.", self)
self.data_description.setGeometry(22, 190, 750, 90)
self.data_description.setStyleSheet("font-size: 26px; font-weight: bold; color: #ffff00;")
self.instructions = QLabel("Use the toggle in the bottom right to\ndecide if you want to use Tor or not.\nThen hit \"Next\"", self)
self.instructions.setGeometry(22, 345, 750, 100)
self.instructions.setStyleSheet("font-size: 30px; font-weight: bold; color: white;")
self.arrow_label = QLabel(self)
self.arrow_label.setGeometry(520, 400, 180, 130)
arrow_pixmap = QPixmap(os.path.join(self.btn_path, "arrow-down.png"))
self.arrow_label.setPixmap(arrow_pixmap)
self.arrow_label.raise_()
self.button_go.setVisible(True)
self.button_back.setVisible(True)
self.button_go.clicked.connect(self.perform_sync)
self.button_back.clicked.connect(self.go_back)
def go_back(self):
self.page_stack.setCurrentIndex(self.page_stack.indexOf(self.page_stack.findChild(MenuPage)))
def perform_sync(self):
self.button_go.setEnabled(False)
self.button_back.setEnabled(False)
self.update_status.update_status('Syncing in progress...')
if self.main_window.get_current_connection() == 'tor':
self.worker_thread = WorkerThread('SYNC_TOR')
else:
self.worker_thread = WorkerThread('SYNC')
self.worker_thread.sync_output.connect(self.update_output)
self.worker_thread.start()
def update_output(self, available_locations, status, is_tor, locations, is_os_error):
if is_os_error:
install_page = self.page_stack.findChild(InstallSystemPackage)
install_page.configure(package_name='tor', distro='debian', is_sync=True)
self.page_stack.setCurrentIndex(self.page_stack.indexOf(install_page))
self.button_go.setEnabled(True)
self.button_back.setEnabled(True)
return
if status is False:
self.button_go.setEnabled(True)
self.button_back.setEnabled(True)
self.update_status.update_status('An error occurred during sync')
return
self.update_status.update_status('Sync complete')
self.connection_manager.set_synced(True)
update_available = ClientController.can_be_updated()
if not update_available:
menu_page = self.page_stack.findChild(MenuPage)
menu_page.on_update_check_finished()
available_locations_list = []
self.connection_manager.store_locations(locations)
grid_positions = [
(395, 90, 185, 75),
(585, 90, 185, 75),
(395, 195, 185, 75),
(585, 195, 185, 75),
(395, 300, 185, 75),
(585, 300, 185, 75),
(395, 405, 185, 75),
(585, 405, 185, 75)
]
list1 = []
for i, location in enumerate(available_locations):
position_index = i % len(grid_positions)
list1.append((QPushButton, location, grid_positions[position_index]))
for item in list1:
available_locations_list.append(item)
location_page = self.find_location_page()
hidetor_page = self.find_hidetor_page()
protocol_page = self.find_protocol_page()
if location_page:
location_page.create_interface_elements(available_locations_list)
if hidetor_page:
hidetor_page.create_interface_elements(available_locations_list)
if protocol_page:
protocol_page.enable_protocol_buttons()
self.page_stack.setCurrentIndex(self.page_stack.indexOf(self.page_stack.findChild(ProtocolPage)))
def find_location_page(self):
for i in range(self.page_stack.count()):
page = self.page_stack.widget(i)
if isinstance(page, LocationPage):
return page
return None
def find_hidetor_page(self):
for i in range(self.page_stack.count()):
page = self.page_stack.widget(i)
if isinstance(page, HidetorPage):
return page
return None
def find_protocol_page(self):
for i in range(self.page_stack.count()):
page = self.page_stack.widget(i)
if isinstance(page, ProtocolPage):
return page
return None
class WelcomePage(Page):
def __init__(self, page_stack, main_window=None, parent=None):
super().__init__("Welcome", page_stack, main_window, parent)
self.btn_path = main_window.btn_path
self.update_status = main_window
self.ui_elements = []
self.button_next.clicked.connect(self.go_to_install)
self.button_next.setVisible(True)
self._setup_welcome_ui()
self._setup_stats_display()
def _setup_welcome_ui(self):
welcome_title = QLabel("Welcome to HydraVeilVPN", self)
welcome_title.setGeometry(20, 45, 760, 60)
welcome_title.setStyleSheet("""
font-size: 32px;
font-weight: bold;
color: cyan;
""")
welcome_title.setAlignment(Qt.AlignmentFlag.AlignCenter)
welcome_msg = QLabel(
"Before we begin your journey, we need to set up a few essential components. "
"Click 'Next' to take you to the installation page.", self)
welcome_msg.setGeometry(40, 100, 720, 80)
welcome_msg.setWordWrap(True)
welcome_msg.setStyleSheet("""
font-size: 16px;
color: cyan;
""")
welcome_msg.setAlignment(Qt.AlignmentFlag.AlignCenter)
def _setup_stats_display(self):
stats_container = QWidget(self)
stats_container.setGeometry(70, 200, 730, 240)
stats_title = QLabel(stats_container)
stats_title.setText("Features & Services")
stats_title.setGeometry(190, 10, 300, 30)
stats_title.setStyleSheet("""
font-size: 22px;
font-weight: bold;
color: cyan;
""")
grid_widget = QWidget(stats_container)
grid_widget.setGeometry(0, 70, 700, 190)
grid_layout = QGridLayout(grid_widget)
grid_layout.setSpacing(30)
stats = [
("Browsers", "browsers_mini.png", "10 Supported Browsers", True),
("WireGuard", "wireguard_mini.png", "6 Global Locations", True),
("Proxy", "just proxy_mini.png", "5 Proxy Locations", True),
("Tor", "toricon_mini.png", "Tor Network Access", True)
]
for i, (title, icon_name, count, available) in enumerate(stats):
row = i // 2
col = i % 2
stat_widget = QWidget()
stat_layout = QHBoxLayout(stat_widget)
stat_layout.setContentsMargins(20, 10, 2, 10)
stat_layout.setSpacing(15)
icon_label = QLabel()
icon_path = os.path.join(self.btn_path, icon_name)
icon_label.setPixmap(QPixmap(icon_path).scaled(30, 30, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation))
icon_label.setFixedSize(30, 30)
stat_layout.addWidget(icon_label)
text_widget = QWidget()
text_layout = QVBoxLayout(text_widget)
text_layout.setSpacing(5)
text_layout.setContentsMargins(0, 0, 30, 0)
title_widget = QWidget()
title_layout = QHBoxLayout(title_widget)
title_layout.setContentsMargins(0, 0, 0, 0)
title_layout.setSpacing(10)
title_label = QLabel(title)
title_label.setStyleSheet("font-size: 16px; font-weight: bold; color: #2c3e50;")
title_layout.addWidget(title_label)
status_indicator = QLabel("")
status_indicator.setStyleSheet("color: #2ecc71; font-size: 16px;")
title_layout.addWidget(status_indicator)
title_layout.addStretch()
text_layout.addWidget(title_widget)
count_label = QLabel(count)
count_label.setStyleSheet("color: #34495e;")
text_layout.addWidget(count_label)
stat_layout.addWidget(text_widget, stretch=1)
stat_widget.setStyleSheet("""
QWidget {
background-color: transparent;
border-radius: 10px;
}
""")
grid_layout.addWidget(stat_widget, row, col)
def go_to_install(self):
install_page = self.page_stack.findChild(InstallSystemPackage)
install_page.configure(package_name='all', distro='debian')
self.page_stack.setCurrentIndex(self.page_stack.indexOf(install_page))
if __name__ == "__main__":
app = QApplication(sys.argv)
window = CustomWindow()

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 211 KiB

After

Width:  |  Height:  |  Size: 32 KiB