add translate
This commit is contained in:
		
							parent
							
								
									d72239b3ab
								
							
						
					
					
						commit
						99029541d3
					
				@ -1,17 +1,23 @@
 | 
			
		||||
from PySide6.QtWidgets import QStackedWidget
 | 
			
		||||
from PySide6.QtCore import Signal
 | 
			
		||||
 | 
			
		||||
from app.ui.views.login_view import LoginView
 | 
			
		||||
from app.ui.views.yobble_home_view import YobbleHomeView # <--- Изменено
 | 
			
		||||
from app.ui.views.yobble_home_view import YobbleHomeView
 | 
			
		||||
from typing import Optional
 | 
			
		||||
from threading import Thread
 | 
			
		||||
import time  # эмуляция задержки от сервера
 | 
			
		||||
import time
 | 
			
		||||
import asyncio
 | 
			
		||||
from app.core.database import get_last_login, get_session, set_last_login
 | 
			
		||||
 | 
			
		||||
class MainController(QStackedWidget):
 | 
			
		||||
    # Сигнал для показа уведомлений из любого потока
 | 
			
		||||
    #      (message, is_error)
 | 
			
		||||
    notification_requested = Signal(str, bool)
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        super().__init__()
 | 
			
		||||
        self.login_view: Optional[LoginView] = None
 | 
			
		||||
        self.yobble_home_view: Optional[YobbleHomeView] = None # <--- Изменено
 | 
			
		||||
        self.yobble_home_view: Optional[YobbleHomeView] = None
 | 
			
		||||
 | 
			
		||||
        self.init_app()
 | 
			
		||||
 | 
			
		||||
@ -41,29 +47,33 @@ class MainController(QStackedWidget):
 | 
			
		||||
            self.removeWidget(self.login_view)
 | 
			
		||||
            self.login_view = None
 | 
			
		||||
 | 
			
		||||
        # Отображаем новый главный экран
 | 
			
		||||
        self.yobble_home_view = YobbleHomeView(username=username)
 | 
			
		||||
        
 | 
			
		||||
        # Подключаем сигнал к слоту в YobbleHomeView
 | 
			
		||||
        self.notification_requested.connect(self.yobble_home_view.show_notification)
 | 
			
		||||
        
 | 
			
		||||
        self.addWidget(self.yobble_home_view)
 | 
			
		||||
        self.setCurrentWidget(self.yobble_home_view)
 | 
			
		||||
        self.yobble_home_view.show()
 | 
			
		||||
 | 
			
		||||
        # 🔹 Оставляем фоновое обновление на будущее
 | 
			
		||||
        Thread(target=self.update_data_from_server, args=(username,), daemon=True).start()
 | 
			
		||||
 | 
			
		||||
    def update_data_from_server(self, username: str):
 | 
			
		||||
        """
 | 
			
		||||
        В фоновом режиме обновляет данные с сервера.
 | 
			
		||||
        Эмулирует задержку и предзагружает права доступа.
 | 
			
		||||
        """
 | 
			
		||||
        # Эмуляция запроса
 | 
			
		||||
        time.sleep(2)
 | 
			
		||||
        print(f"[Sync] Обновляем данные для пользователя: {username}")
 | 
			
		||||
 | 
			
		||||
        if self.yobble_home_view:
 | 
			
		||||
            print("[Sync] Запускаем предзагрузку прав доступа...")
 | 
			
		||||
            try:
 | 
			
		||||
                # Запускаем асинхронную функцию в текущем потоке
 | 
			
		||||
                asyncio.run(self.yobble_home_view.preload_permissions()) # TODO добавить из sqlite
 | 
			
		||||
                print("[Sync] Предзагрузка прав доступа завершена.")
 | 
			
		||||
                # `preload_permissions` теперь возвращает кортеж (успех, данные)
 | 
			
		||||
                # asyncio.run() выполняет async функцию и возвращает её результат
 | 
			
		||||
                asyncio.run(self.yobble_home_view.preload_permissions())
 | 
			
		||||
 | 
			
		||||
            except Exception as e:
 | 
			
		||||
                print(f"[Sync] Ошибка во время предзагрузки прав: {e}")
 | 
			
		||||
                error_message = f"Ошибка предзагрузки: {e}"
 | 
			
		||||
                print(f"[Sync] {error_message}")
 | 
			
		||||
                # Отправляем сигнал вместо прямого вызова
 | 
			
		||||
                self.notification_requested.emit(error_message, True)
 | 
			
		||||
 | 
			
		||||
@ -119,18 +119,18 @@ async def refresh_token(access_token: str, refresh_token: str):
 | 
			
		||||
                    )
 | 
			
		||||
                    return True, token_data
 | 
			
		||||
                else:
 | 
			
		||||
                    return False, data.get("detail", "Unknown error")
 | 
			
		||||
                    return False, data.get("detail", localizer.translate("Unknown error"))
 | 
			
		||||
 | 
			
		||||
            elif response.status_code == 401:
 | 
			
		||||
                return False, "Refresh token is invalid or expired"
 | 
			
		||||
                return False, localizer.translate("Refresh token is invalid or expired")
 | 
			
		||||
            
 | 
			
		||||
            else:
 | 
			
		||||
                return False, f"Server error: {response.status_code}"
 | 
			
		||||
                return False, f"{localizer.translate('Server error')}: {response.status_code}"
 | 
			
		||||
 | 
			
		||||
    except httpx.RequestError as e:
 | 
			
		||||
        return False, f"Network error: {e}"
 | 
			
		||||
        return False, f"{localizer.translate('Network error')}: {e}"
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        return False, f"An error occurred: {e}"
 | 
			
		||||
        return False, f"{localizer.translate('An error occurred')}: {e}"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def get_user_role(access_token: str, login: str):
 | 
			
		||||
@ -147,13 +147,13 @@ async def get_user_role(access_token: str, login: str):
 | 
			
		||||
 | 
			
		||||
            if response.status_code == 200:
 | 
			
		||||
                data = response.json()
 | 
			
		||||
                return (True, data['data']) if data.get("status") == "fine" else (False, data.get("detail", "Unknown error"))
 | 
			
		||||
                return (True, data['data']) if data.get("status") == "fine" else (False, data.get("detail", localizer.translate("Unknown error")))
 | 
			
		||||
 | 
			
		||||
            elif response.status_code == 401:
 | 
			
		||||
                # Токен истек, пытаемся обновить
 | 
			
		||||
                session = get_session(login)
 | 
			
		||||
                if not session or not session['refresh_token']:
 | 
			
		||||
                    return False, "No refresh token found"
 | 
			
		||||
                    return False, localizer.translate("No refresh token found")
 | 
			
		||||
 | 
			
		||||
                refresh_success, refresh_data = await refresh_token(access_token, session['refresh_token'])
 | 
			
		||||
                
 | 
			
		||||
@ -165,16 +165,16 @@ async def get_user_role(access_token: str, login: str):
 | 
			
		||||
                    
 | 
			
		||||
                    if response.status_code == 200:
 | 
			
		||||
                        data = response.json()
 | 
			
		||||
                        return (True, data['data']) if data.get("status") == "fine" else (False, "Failed to get role after refresh")
 | 
			
		||||
                        return (True, data['data']) if data.get("status") == "fine" else (False, localizer.translate("Failed to get role after refresh"))
 | 
			
		||||
                
 | 
			
		||||
                # Если обновление не удалось, выходим из системы
 | 
			
		||||
                logout(access_token)
 | 
			
		||||
                return False, "Session expired, please log in again"
 | 
			
		||||
                return False, localizer.translate("Session expired, please log in again")
 | 
			
		||||
 | 
			
		||||
            else:
 | 
			
		||||
                return False, f"Server error: {response.status_code}"
 | 
			
		||||
                return False, f"{localizer.translate('Server error')}: {response.status_code}"
 | 
			
		||||
 | 
			
		||||
    except httpx.RequestError as e:
 | 
			
		||||
        return False, f"Network error: {e}"
 | 
			
		||||
        return False, f"{localizer.translate('Network error')}: {e}"
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        return False, f"An error occurred: {e}"
 | 
			
		||||
        return False, f"{localizer.translate('An error occurred')}: {e}"
 | 
			
		||||
 | 
			
		||||
@ -30,9 +30,20 @@
 | 
			
		||||
  "Регистрация в данный момент отключена.": "Registration is currently disabled.",
 | 
			
		||||
  "Данные не прошли валидацию. Проверьте длину логина и пароля.": "Data validation failed. Check username and password length.",
 | 
			
		||||
 | 
			
		||||
  "Загрузка...": "Загрузка...",
 | 
			
		||||
  "Загрузка...": "Loading...",
 | 
			
		||||
  "Доступ запрещен": "Access denied",
 | 
			
		||||
 | 
			
		||||
  "Ошибка предзагрузки": "Preload error",
 | 
			
		||||
  "Unknown error": "Unknown error",
 | 
			
		||||
  "Refresh token is invalid or expired": "Refresh token is invalid or expired",
 | 
			
		||||
  "Server error": "Server error",
 | 
			
		||||
  "Network error": "Network error",
 | 
			
		||||
  "An error occurred": "An error occurred",
 | 
			
		||||
  "No refresh token found": "No refresh token found",
 | 
			
		||||
  "Failed to get role after refresh": "Failed to get role after refresh",
 | 
			
		||||
  "Session expired, please log in again": "Session expired, please log in again",
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  "Login must be between 3 and 32 characters long": "Login must be between 3 and 32 characters long",
 | 
			
		||||
  "Login must not contain whitespace characters": "Login must not contain whitespace characters",
 | 
			
		||||
  "Login must not start with an underscore": "Login must not start with an underscore",
 | 
			
		||||
 | 
			
		||||
@ -30,9 +30,19 @@
 | 
			
		||||
  "Регистрация в данный момент отключена.": "Регистрация в данный момент отключена.",
 | 
			
		||||
  "Данные не прошли валидацию. Проверьте длину логина и пароля.": "Данные не прошли валидацию. Проверьте длину логина и пароля.",
 | 
			
		||||
 | 
			
		||||
  "Загрузка...": "Loading...",
 | 
			
		||||
  "Загрузка...": "Загрузка...",
 | 
			
		||||
  "Доступ запрещен": "Доступ запрещен",
 | 
			
		||||
 | 
			
		||||
  "Ошибка предзагрузки": "Ошибка предзагрузки",
 | 
			
		||||
  "Unknown error": "Неизвестная ошибка",
 | 
			
		||||
  "Refresh token is invalid or expired": "Токен обновления недействителен или просрочен",
 | 
			
		||||
  "Server error": "Ошибка сервера",
 | 
			
		||||
  "Network error": "Ошибка сети",
 | 
			
		||||
  "An error occurred": "Произошла ошибка",
 | 
			
		||||
  "No refresh token found": "Токен обновления не найден",
 | 
			
		||||
  "Failed to get role after refresh": "Не удалось получить роль после обновления",
 | 
			
		||||
  "Session expired, please log in again": "Сессия истекла, пожалуйста, войдите снова",
 | 
			
		||||
 | 
			
		||||
  "Login must be between 3 and 32 characters long": "Логин должен быть от 3 до 32 символов",
 | 
			
		||||
  "Login must not contain whitespace characters": "Логин не должен содержать пробелы",
 | 
			
		||||
  "Login must not start with an underscore": "Логин не должен начинаться с символа подчёркивания",
 | 
			
		||||
 | 
			
		||||
@ -141,17 +141,17 @@ class YobbleHomeView(QWidget):
 | 
			
		||||
    def show_notification(self, message, is_error=True, duration=3000):
 | 
			
		||||
        """Показывает всплывающее уведомление."""
 | 
			
		||||
        self.notification_label.setText(message)
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        self.notification_widget.setProperty("is_error", is_error)
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        self.notification_widget.style().unpolish(self.notification_widget)
 | 
			
		||||
        self.notification_widget.style().polish(self.notification_widget)
 | 
			
		||||
 | 
			
		||||
        # Позиционирование
 | 
			
		||||
        self.notification_widget.adjustSize()
 | 
			
		||||
        x = (self.width() - self.notification_widget.width()) / 2
 | 
			
		||||
        y = 30 # Отступ сверху
 | 
			
		||||
        self.notification_widget.move(int(x), y)
 | 
			
		||||
        y = self.height() - self.notification_widget.height() - self.bottom_bar.height() - 15 # Отступ снизу
 | 
			
		||||
        self.notification_widget.move(int(x), int(y))
 | 
			
		||||
        
 | 
			
		||||
        self.notification_widget.show()
 | 
			
		||||
        self.notification_widget.raise_() # Поднять поверх всех
 | 
			
		||||
@ -222,7 +222,8 @@ class YobbleHomeView(QWidget):
 | 
			
		||||
        if hasattr(self, 'notification_widget') and self.notification_widget.isVisible():
 | 
			
		||||
            self.notification_widget.adjustSize()
 | 
			
		||||
            x = (self.width() - self.notification_widget.width()) / 2
 | 
			
		||||
            self.notification_widget.move(int(x), 30)
 | 
			
		||||
            y = self.height() - self.notification_widget.height() - self.bottom_bar.height() - 15
 | 
			
		||||
            self.notification_widget.move(int(x), int(y))
 | 
			
		||||
 | 
			
		||||
    def update_styles(self):
 | 
			
		||||
        """Обновляет стили компонента при смене темы."""
 | 
			
		||||
@ -394,27 +395,28 @@ class YobbleHomeView(QWidget):
 | 
			
		||||
        access_token = await get_current_access_token()
 | 
			
		||||
        if not access_token:
 | 
			
		||||
            print("[Permissions] Preload failed: No access token.")
 | 
			
		||||
            # Поскольку мы уже в async-контексте, который будет запущен
 | 
			
		||||
            # в основном потоке через QMetaObject.invokeMethod или сигнал,
 | 
			
		||||
            # можно вызвать напрямую, но сигнал надежнее.
 | 
			
		||||
            # Здесь прямой вызов может быть небезопасен, если async-задача
 | 
			
		||||
            # выполняется в другом потоке. Но т.к. мы используем сигнал
 | 
			
		||||
            # в контроллере, этот код не будет вызван.
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        success, data = await get_user_role(access_token, self.username)
 | 
			
		||||
        if success:
 | 
			
		||||
            user_permissions = set(data.get("user_permissions", []))
 | 
			
		||||
 | 
			
		||||
            # Загружаем ВСЕ права пользователя в кэш
 | 
			
		||||
            self.permission_cache = user_permissions
 | 
			
		||||
            print("self.permission_cache", self.permission_cache)
 | 
			
		||||
 | 
			
		||||
            # for permission_code in self.REQUIRED_PERMISSIONS.values():
 | 
			
		||||
            #     if permission_code in user_permissions:
 | 
			
		||||
            #         self.permission_cache.add(permission_code)
 | 
			
		||||
            
 | 
			
		||||
            self.permissions_preloaded_last = time.time()
 | 
			
		||||
            self.permissions_preloaded = True
 | 
			
		||||
            self.preload_permissions_first = True
 | 
			
		||||
 | 
			
		||||
            print(f"[Permissions] Preloaded. Cache: {self.permission_cache}")
 | 
			
		||||
        else:
 | 
			
		||||
            # В случае ошибки при запросе, выбрасываем исключение,
 | 
			
		||||
            # которое будет поймано в вызывающем потоке.
 | 
			
		||||
            print(f"[Permissions] Preload failed: {data}")
 | 
			
		||||
            raise ConnectionError(data)
 | 
			
		||||
        return success, data
 | 
			
		||||
 | 
			
		||||
    # def update_current_tab_content(self):
 | 
			
		||||
    #     """Обновляет контент текущей активной вкладки после предзагрузки прав."""
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user