vpn/app/main.py
2026-09-23 20:25:29 +00:00

134 lines
4.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from __future__ import annotations
import asyncio
import flet as ft
from vpn_native_bridge import NativeBridge
from app.models import PaymentInfo
from app.screens.safe_area import safe_area
from app.screens.settings import build_settings_screen
from app.screens.vpn import build_vpn_screen
from app.screens.white_lists import build_white_lists_screen
from app.services.billing import month_key, payment_banner
from app.services.controller import RuntimeController
from app.services.settings_registry import SettingsRegistry
from app.services.state_store import StateStore
from app.theme import apply_accent
def main(page: ft.Page) -> None:
page.title = "VPN App"
page.padding = 0
page.scroll = ft.ScrollMode.AUTO
page.theme_mode = ft.ThemeMode.SYSTEM
store = StateStore()
persisted = store.load()
native_bridge = (
NativeBridge()
if page.platform in {ft.PagePlatform.ANDROID, ft.PagePlatform.ANDROID_TV}
else None
)
controller = RuntimeController(native_bridge=native_bridge)
registry = SettingsRegistry()
payment = PaymentInfo(
monthly_price_rub=int(persisted.get("monthly_price_rub", 0)),
payment_day=int(persisted.get("payment_day", 1)),
)
banner = ft.Ref[ft.Banner]()
def dismiss_payment(_: ft.ControlEvent) -> None:
state = store.load()
state["payment_banner_dismissed"] = month_key()
store.save(state)
page.close(banner.current)
def maybe_show_payment() -> None:
if payment.monthly_price_rub <= 0:
return
if persisted.get("payment_banner_dismissed") == month_key():
return
message = payment_banner(payment)
if message:
page.open(
ft.Banner(
ref=banner,
leading=ft.Icon(ft.Icons.INFO_OUTLINE),
content=ft.Text(message),
actions=[
ft.TextButton("Не показывать в этом месяце", on_click=dismiss_payment)
],
)
)
def set_theme(key: str) -> None:
apply_accent(page, key)
vpn_screen = build_vpn_screen(page, controller, registry, set_theme)
white_lists_screen = build_white_lists_screen(
page, controller, registry, lambda _: set_theme("freeturn")
)
settings_screen = build_settings_screen(page, registry)
def open_settings(_: ft.ControlEvent) -> None:
dialog = ft.AlertDialog(
modal=True,
title=ft.Text("Настройки"),
content=ft.Container(settings_screen, width=460, height=520),
actions=[ft.TextButton("Закрыть", on_click=lambda _: page.close(dialog))],
)
page.open(dialog)
def tab_changed(event: ft.ControlEvent) -> None:
index = event.control.selected_index
set_theme("freeturn" if index == 1 else "basic")
page.appbar = ft.AppBar(
title=ft.Text("VPN App"),
actions=[ft.IconButton(ft.Icons.SETTINGS_OUTLINED, on_click=open_settings)],
)
tabs = ft.Tabs(
content=ft.Column(
[
ft.TabBar(
tabs=[ft.Tab(label="VPN"), ft.Tab(label="Белые списки")],
scrollable=False,
),
ft.TabBarView(
controls=[vpn_screen, white_lists_screen],
expand=True,
),
],
expand=True,
),
length=2,
expand=True,
selected_index=0,
on_change=tab_changed,
)
page.add(
safe_area(
ft.Column(
[tabs],
expand=True,
)
)
)
apply_accent(page, "basic")
maybe_show_payment()
if native_bridge is not None:
async def restore_native_state() -> None:
while True:
await controller.refresh()
if controller.engine_kind.value == "freeturn":
tabs.selected_index = 1
set_theme("freeturn")
page.update()
await asyncio.sleep(2)
page.run_task(restore_native_state)
if __name__ == "__main__":
ft.run(main)