Overhaul Sessioning

This commit was merged in pull request #55.
This commit is contained in:
David Rodenkirchen
2026-02-23 15:15:39 +01:00
parent 57c578a44b
commit b47eefe615
25 changed files with 216 additions and 179 deletions
@@ -1,47 +1,29 @@
from typing import Optional, Callable
from rio import *
from rio import Component, event, Spacer, Card, Column, Text, TextStyle
from src.ezgg_lan_manager import ConfigurationService, UserService, LocalDataService
from src.ezgg_lan_manager.services.ConfigurationService import ConfigurationService
from src.ezgg_lan_manager.services.UserService import UserService
from src.ezgg_lan_manager.components.DesktopNavigationButton import DesktopNavigationButton
from src.ezgg_lan_manager.components.NavigationSponsorBox import NavigationSponsorBox
from src.ezgg_lan_manager.components.UserInfoAndLoginBox import UserInfoAndLoginBox
from src.ezgg_lan_manager.services.LocalDataService import LocalData
from src.ezgg_lan_manager.types.SessionStorage import SessionStorage
from src.ezgg_lan_manager.types.User import User
from src.ezgg_lan_manager.types.UserSession import UserSession
class DesktopNavigation(Component):
user: Optional[User] = None
force_login_box_refresh: list[Callable] = []
@event.on_populate
async def async_init(self) -> None:
self.session[SessionStorage].subscribe_to_logged_in_or_out_event(str(self.__class__), self.async_init)
local_data = self.session[LocalData]
if local_data.stored_session_token:
session_ = self.session[LocalDataService].verify_token(local_data.stored_session_token)
if session_:
self.session.detach(SessionStorage)
self.session.attach(session_)
self.user = await self.session[UserService].get_user(session_.user_id)
try:
# Hack-around, maybe fix in the future
self.force_login_box_refresh[-1]()
except IndexError:
pass
return
if self.session[SessionStorage].user_id:
self.user = await self.session[UserService].get_user(self.session[SessionStorage].user_id)
else:
async def on_populate(self) -> None:
try:
self.user = await self.session[UserService].get_user(self.session[UserSession].user_id)
except KeyError:
self.user = None
def build(self) -> Component:
lan_info = self.session[ConfigurationService].get_lan_info()
user_info_and_login_box = UserInfoAndLoginBox()
self.force_login_box_refresh.append(user_info_and_login_box.force_refresh)
user_info_and_login_box = UserInfoAndLoginBox(state_changed_cb=self.on_populate)
navigation = [
DesktopNavigationButton("News", "./news"),
Spacer(min_height=0.7),
+11 -6
View File
@@ -1,10 +1,13 @@
from rio import Component, TextStyle, Color, TextInput, Button, Text, Rectangle, Column, Row, Spacer, \
EventHandler
import uuid
from rio import Component, TextStyle, Color, TextInput, Button, Text, Rectangle, Column, Row, Spacer, \
EventHandler, Webview
from src.ezgg_lan_manager import RefreshService
from src.ezgg_lan_manager.services.LocalDataService import LocalDataService, LocalData
from src.ezgg_lan_manager.services.UserService import UserService
from src.ezgg_lan_manager.types.SessionStorage import SessionStorage
from src.ezgg_lan_manager.types.User import User
from src.ezgg_lan_manager.types.UserSession import UserSession
class LoginBox(Component):
@@ -26,11 +29,13 @@ class LoginBox(Component):
self.password_input_is_valid = True
self.login_button_is_loading = False
self.is_account_locked = False
await self.session[SessionStorage].set_user_id_and_team_member_flag(user.user_id, user.is_team_member)
token = self.session[LocalDataService].set_session(self.session[SessionStorage])
user_session = UserSession(id=uuid.uuid4(), user_id=user.user_id, is_team_member=user.is_team_member)
self.session.attach(user_session)
token = self.session[LocalDataService].set_session(user_session)
self.session[LocalData].stored_session_token = token
self.session.attach(self.session[LocalData])
self.status_change_cb()
await self.status_change_cb()
await self.session[RefreshService].trigger_refresh()
else:
self.user_name_input_is_valid = False
self.password_input_is_valid = False
@@ -1,11 +1,10 @@
from decimal import Decimal
from functools import partial
from typing import Optional, Callable
from rio import Component, Column, Text, TextStyle, Button, Spacer, event
from src.ezgg_lan_manager import TicketingService
from src.ezgg_lan_manager.types.SessionStorage import SessionStorage
from src.ezgg_lan_manager.types.UserSession import UserSession
class SeatingPlanInfoBox(Component):
@@ -22,11 +21,14 @@ class SeatingPlanInfoBox(Component):
@event.on_populate
async def check_ticket(self) -> None:
if self.session[SessionStorage].user_id:
user_ticket = await self.session[TicketingService].get_user_ticket(self.session[SessionStorage].user_id)
try:
user_id = self.session[UserSession].user_id
user_ticket = await self.session[TicketingService].get_user_ticket(user_id)
self.has_user_ticket = not (user_ticket is None)
self.booking_button_text = "Buchen" if self.has_user_ticket else "Ticket kaufen"
self.force_refresh()
except KeyError:
return
async def purchase_clicked(self):
if self.has_user_ticket:
@@ -35,6 +37,11 @@ class SeatingPlanInfoBox(Component):
self.session.navigate_to("./buy_ticket")
def build(self) -> Component:
try:
user_id = self.session[UserSession].user_id
except KeyError:
user_id = None
if self.override_text:
return Column(Text(self.override_text, margin=1,
style=TextStyle(fill=self.session.theme.neutral_color, font_size=1.4), overflow="wrap",
@@ -75,9 +82,9 @@ class SeatingPlanInfoBox(Component):
grow_y=False,
is_sensitive=not self.is_booking_blocked,
on_press=self.purchase_clicked
) if self.session[SessionStorage].user_id else Text(f"Du musst eingeloggt sein um einen Sitzplatz zu buchen",
margin=1,
style=TextStyle(fill=self.session.theme.neutral_color),
overflow="wrap", justify="center"),
) if user_id is not None else Text(f"Du musst eingeloggt sein um einen Sitzplatz zu buchen",
margin=1,
style=TextStyle(fill=self.session.theme.neutral_color),
overflow="wrap", justify="center"),
min_height=10
)
@@ -4,7 +4,7 @@ from rio import Component, Text, Icon, TextStyle, Rectangle, Spacer, Color, Poin
from typing import Optional, Callable, Literal
from src.ezgg_lan_manager.types.Seat import Seat
from src.ezgg_lan_manager.types.SessionStorage import SessionStorage
from src.ezgg_lan_manager.types.UserSession import UserSession
class SeatPixel(Component):
@@ -14,7 +14,11 @@ class SeatPixel(Component):
seat_orientation: Literal["top", "bottom"]
def determine_color(self) -> Color:
if self.seat.user is not None and self.seat.user.user_id == self.session[SessionStorage].user_id:
try:
user_id = self.session[UserSession].user_id
except KeyError:
user_id = None
if self.seat.user is not None and self.seat.user.user_id == user_id:
return Color.from_hex("800080")
elif self.seat.is_blocked or self.seat.user is not None:
return self.session.theme.danger_color
@@ -1,5 +1,6 @@
from asyncio import sleep, create_task
from decimal import Decimal
from typing import Optional
from rio import Component, Column, Text, TextStyle, Button, Row, ScrollContainer, Spacer, Popup, Table, event, Card
@@ -8,7 +9,7 @@ from src.ezgg_lan_manager.components.CateringOrderItem import CateringOrderItem
from src.ezgg_lan_manager.services.AccountingService import AccountingService
from src.ezgg_lan_manager.services.CateringService import CateringService, CateringError, CateringErrorType
from src.ezgg_lan_manager.types.CateringOrder import CateringOrder, CateringMenuItemsWithAmount
from src.ezgg_lan_manager.types.SessionStorage import SessionStorage
from src.ezgg_lan_manager.types.UserSession import UserSession
POPUP_CLOSE_TIMEOUT_SECONDS = 3
@@ -22,16 +23,21 @@ class ShoppingCartAndOrders(Component):
@event.periodic(5)
async def periodic_refresh_of_orders(self) -> None:
if not self.show_cart and not self.popup_is_shown:
self.orders = await self.session[CateringService].get_orders_for_user(self.session[SessionStorage].user_id)
user_id = self._get_user_id()
if not self.show_cart and not self.popup_is_shown and user_id is not None:
self.orders = await self.session[CateringService].get_orders_for_user(user_id)
async def switch(self) -> None:
self.show_cart = not self.show_cart
self.orders = await self.session[CateringService].get_orders_for_user(self.session[SessionStorage].user_id)
user_id = self._get_user_id()
if user_id is not None:
self.orders = await self.session[CateringService].get_orders_for_user(user_id)
async def on_remove_item(self, list_id: int) -> None:
catering_service = self.session[CateringService]
user_id = self.session[SessionStorage].user_id
user_id = self._get_user_id()
if user_id is None:
return
cart = catering_service.get_cart(user_id)
try:
cart.pop(list_id)
@@ -41,13 +47,16 @@ class ShoppingCartAndOrders(Component):
self.force_refresh()
async def on_empty_cart_pressed(self) -> None:
self.session[CateringService].save_cart(self.session[SessionStorage].user_id, [])
user_id = self._get_user_id()
if user_id is None:
return
self.session[CateringService].save_cart(user_id, [])
self.force_refresh()
async def on_add_item(self, article_id: int) -> None:
catering_service = self.session[CateringService]
user_id = self.session[SessionStorage].user_id
if not user_id:
user_id = self._get_user_id()
if user_id is None:
return
cart = catering_service.get_cart(user_id)
item_to_add = await catering_service.get_menu_item_by_id(article_id)
@@ -68,7 +77,9 @@ class ShoppingCartAndOrders(Component):
self.order_button_loading = True
self.force_refresh()
user_id = self.session[SessionStorage].user_id
user_id = self._get_user_id()
if user_id is None:
return
cart = self.session[CateringService].get_cart(user_id)
show_popup_task = None
if len(cart) < 1:
@@ -90,7 +101,7 @@ class ShoppingCartAndOrders(Component):
else:
show_popup_task = create_task(self.show_popup("Unbekannter Fehler", True))
else:
self.session[CateringService].save_cart(self.session[SessionStorage].user_id, [])
self.session[CateringService].save_cart(user_id, [])
self.order_button_loading = False
if not show_popup_task:
show_popup_task = create_task(self.show_popup("Bestellung erfolgreich aufgegeben!", False))
@@ -133,10 +144,16 @@ class ShoppingCartAndOrders(Component):
)
await dialog.wait_for_close()
def _get_user_id(self) -> Optional[int]:
try:
return self.session[UserSession].user_id
except KeyError:
return None
def build(self) -> Component:
user_id = self.session[SessionStorage].user_id
user_id = self._get_user_id()
catering_service = self.session[CateringService]
cart = catering_service.get_cart(user_id)
cart = catering_service.get_cart(user_id) if user_id is not None else []
if self.show_cart:
cart_container = ScrollContainer(
content=Column(
@@ -2,7 +2,6 @@ from functools import partial
from typing import Callable, Optional
from decimal import Decimal
import rio
from rio import Component, Card, Column, Text, Row, Button, TextStyle, ProgressBar, event, Spacer
from src.ezgg_lan_manager import TicketingService
@@ -22,10 +21,10 @@ class TicketBuyCard(Component):
available_tickets: int = 0
@event.on_populate
async def async_init(self) -> None:
async def on_populate(self) -> None:
self.available_tickets = await self.session[TicketingService].get_available_tickets_for_category(self.category)
def build(self) -> rio.Component:
def build(self) -> Component:
ticket_description_style = TextStyle(
fill=self.session.theme.neutral_color,
font_size=1.2,
@@ -9,8 +9,8 @@ from rio import Component, Column, Button, Color, TextStyle, Text, TextInput, Ro
from src.ezgg_lan_manager.services.UserService import UserService, NameNotAllowedError
from src.ezgg_lan_manager.services.ConfigurationService import ConfigurationService
from src.ezgg_lan_manager.types.SessionStorage import SessionStorage
from src.ezgg_lan_manager.types.User import User
from src.ezgg_lan_manager.types.UserSession import UserSession
class UserEditForm(Component):
@@ -35,8 +35,13 @@ class UserEditForm(Component):
async def on_populate(self) -> None:
await self.session.set_title(f"{self.session[ConfigurationService].get_lan_info().name} - Profil bearbeiten")
if self.is_own_profile:
self.user = await self.session[UserService].get_user(self.session[SessionStorage].user_id)
self.profile_picture = await self.session[UserService].get_profile_picture(self.user.user_id)
try:
user_id = self.session[UserSession].user_id
except KeyError:
self.session.navigate_to("/")
else:
self.user = await self.session[UserService].get_user(user_id)
self.profile_picture = await self.session[UserService].get_profile_picture(self.user.user_id)
else:
self.profile_picture = await self.session[UserService].get_profile_picture(self.user.user_id)
@@ -1,15 +1,18 @@
import logging
from typing import Callable
from rio import Component
from src.ezgg_lan_manager.components.LoginBox import LoginBox
from src.ezgg_lan_manager.components.UserInfoBox import UserInfoBox
from src.ezgg_lan_manager.types.SessionStorage import SessionStorage
from src.ezgg_lan_manager.types.UserSession import UserSession
logger = logging.getLogger(__name__.split(".")[-1])
class UserInfoAndLoginBox(Component):
state_changed_cb: Callable
def build(self) -> Component:
if self.session[SessionStorage].user_id is None:
return LoginBox(status_change_cb=self.force_refresh)
else:
return UserInfoBox(status_change_cb=self.force_refresh)
try:
user_id = self.session[UserSession].user_id
return UserInfoBox(status_change_cb=self.state_changed_cb, user_id=user_id)
except KeyError:
return LoginBox(status_change_cb=self.state_changed_cb)
+13 -16
View File
@@ -6,6 +6,7 @@ from rio import Component, TextStyle, Color, Button, Text, Rectangle, Column, Ro
from src.ezgg_lan_manager.components.UserInfoBoxButton import UserInfoBoxButton
from src.ezgg_lan_manager.services.LocalDataService import LocalData, LocalDataService
from src.ezgg_lan_manager.services.RefreshService import RefreshService
from src.ezgg_lan_manager.services.UserService import UserService
from src.ezgg_lan_manager.services.AccountingService import AccountingService
from src.ezgg_lan_manager.services.TicketingService import TicketingService
@@ -13,7 +14,7 @@ from src.ezgg_lan_manager.services.SeatingService import SeatingService
from src.ezgg_lan_manager.types.Seat import Seat
from src.ezgg_lan_manager.types.Ticket import Ticket
from src.ezgg_lan_manager.types.User import User
from src.ezgg_lan_manager.types.SessionStorage import SessionStorage
from src.ezgg_lan_manager.types.UserSession import UserSession
class StatusButton(Component):
@@ -41,6 +42,7 @@ class StatusButton(Component):
class UserInfoBox(Component):
user_id: int
status_change_cb: EventHandler = None
TEXT_STYLE = TextStyle(fill=Color.from_hex("02dac5"), font_size=0.9)
user: Optional[User] = None
@@ -53,31 +55,26 @@ class UserInfoBox(Component):
return choice(["Guten Tacho", "Tuten Gag", "Servus", "Moinjour", "Hallöchen", "Heyho", "Moinsen"])
async def logout(self) -> None:
await self.session[SessionStorage].clear()
self.session.detach(UserSession)
self.user = None
self.session[LocalDataService].del_session(self.session[LocalData].stored_session_token)
self.session[LocalData].stored_session_token = None
self.session.attach(self.session[LocalData])
self.status_change_cb()
self.session.navigate_to("/")
await self.status_change_cb()
await self.session[RefreshService].trigger_refresh()
@event.on_populate
async def async_init(self) -> None:
if self.session[SessionStorage].user_id:
self.user = await self.session[UserService].get_user(self.session[SessionStorage].user_id)
self.user_balance = await self.session[AccountingService].get_balance(self.user.user_id)
self.user_ticket = await self.session[TicketingService].get_user_ticket(self.user.user_id)
self.user_seat = await self.session[SeatingService].get_user_seat(self.user.user_id)
self.session[AccountingService].add_update_hook(self.update)
async def update(self) -> None:
if not self.user:
self.user = await self.session[UserService].get_user(self.session[SessionStorage].user_id)
if not self.user:
return
self.user = await self.session[UserService].get_user(self.user_id)
self.user_balance = await self.session[AccountingService].get_balance(self.user.user_id)
self.user_ticket = await self.session[TicketingService].get_user_ticket(self.user.user_id)
self.user_seat = await self.session[SeatingService].get_user_seat(self.user.user_id)
self.session[AccountingService].add_update_hook(self.update)
async def update(self) -> None:
self.user_balance = await self.session[AccountingService].get_balance(self.user_id)
self.user_ticket = await self.session[TicketingService].get_user_ticket(self.user_id)
self.user_seat = await self.session[SeatingService].get_user_seat(self.user_id)
def build(self) -> Component:
if not self.user: