From 09d7178ea0f051bee6dcc886d45672f069941d07 Mon Sep 17 00:00:00 2001 From: David Rodenkirchen Date: Mon, 26 Aug 2024 16:38:32 +0200 Subject: [PATCH] add edit profile page and default profile picture --- src/EzLanManager.py | 2 +- src/ez_lan_manager/assets/img/anon_pfp.png | 0 src/ez_lan_manager/pages/EditProfile.py | 239 +++++++++++++++++++++ src/ez_lan_manager/pages/__init__.py | 3 +- 4 files changed, 242 insertions(+), 2 deletions(-) create mode 100644 src/ez_lan_manager/assets/img/anon_pfp.png create mode 100644 src/ez_lan_manager/pages/EditProfile.py diff --git a/src/EzLanManager.py b/src/EzLanManager.py index 1542e7c..86395b4 100644 --- a/src/EzLanManager.py +++ b/src/EzLanManager.py @@ -109,7 +109,7 @@ if __name__ == "__main__": Page( name="EditProfile", page_url="edit-profile", - build=lambda: pages.PlaceholderPage(placeholder_name="Profil bearbeiten"), + build=pages.EditProfilePage, guard=logged_in_guard ), Page( diff --git a/src/ez_lan_manager/assets/img/anon_pfp.png b/src/ez_lan_manager/assets/img/anon_pfp.png new file mode 100644 index 0000000..e69de29 diff --git a/src/ez_lan_manager/pages/EditProfile.py b/src/ez_lan_manager/pages/EditProfile.py new file mode 100644 index 0000000..a14f49d --- /dev/null +++ b/src/ez_lan_manager/pages/EditProfile.py @@ -0,0 +1,239 @@ +from datetime import date, datetime +from asyncio import sleep +from hashlib import sha256 +from typing import Optional + +from from_root import from_root +from rio import Column, Component, event, Text, TextStyle, Button, Color, Row, TextInput, Image, TextInputChangeEvent, NoFileSelectedError +from email_validator import validate_email, EmailNotValidError + +from src.ez_lan_manager import ConfigurationService, UserService +from src.ez_lan_manager.components.MainViewContentBox import MainViewContentBox +from src.ez_lan_manager.pages import BasePage +from src.ez_lan_manager.types.SessionStorage import SessionStorage +from src.ez_lan_manager.types.User import User + + +class EditProfilePage(Component): + @staticmethod + def optional_date_to_str(d: Optional[date]) -> str: + if not d: + return "" + return d.strftime("%d.%m.%Y") + + @event.on_populate + async def on_populate(self) -> None: + await self.session.set_title(f"{self.session[ConfigurationService].get_lan_info().name} - Profil bearbeiten") + + def on_email_changed(self, change_event: TextInputChangeEvent) -> None: + try: + validate_email(change_event.text, check_deliverability=False) + self.email_input.is_valid = True + except EmailNotValidError: + self.email_input.is_valid = False + + def on_birthday_changed(self, change_event: TextInputChangeEvent) -> None: + if len(change_event.text) == 0: + self.birthday_input.is_valid = True + return + try: + day, month, year = change_event.text.split(".") + year = int(year) + if year < 1900 or year > datetime.now().year - 12: + raise ValueError + date(day=int(day), month=int(month), year=year) + self.birthday_input.is_valid = True + except (ValueError, TypeError, IndexError): + self.birthday_input.is_valid = False + + async def upload_new_pfp(self) -> None: + try: + new_pfp = await self.session.file_chooser(file_extensions=("png", "jpg", "jpeg"), multiple=False) + except NoFileSelectedError: + return + + if new_pfp.size_in_bytes > 2 * 1_000_000: + await self.display_save_result_animation(False, optional_text="Bild zu groß! (> 2MB)") + return + + image_data = await new_pfp.read_bytes() + self.session[UserService].set_profile_picture(self.session[SessionStorage].user_id, image_data) + self.pfp_image_container.image = image_data + await self.display_save_result_animation(True) + + async def display_save_result_animation(self, success: bool, optional_text: Optional[str] = None) -> None: + self.saved_text.text = "" + if success: + self.saved_text.style = TextStyle( + fill=self.session.theme.success_color, + font_size=0.9 + ) + t = "Gespeichert!" if not optional_text else optional_text + for c in t: + self.saved_text.text = self.saved_text.text + c + await self.saved_text.force_refresh() + await sleep(0.08) + else: + self.saved_text.style = TextStyle( + fill=self.session.theme.danger_color, + font_size=0.9 + ) + t = "Fehler!" if not optional_text else optional_text + for c in t: + self.saved_text.text = self.saved_text.text + c + await self.saved_text.force_refresh() + await sleep(0.08) + + async def on_save_pressed(self) -> None: + if not all((self.email_input.is_valid, self.birthday_input.is_valid)): + await self.display_save_result_animation(False) + return + + if len(self.new_pw_1_input.text.strip()) > 0: + if self.new_pw_1_input.text.strip() != self.new_pw_2_input.text.strip(): + await self.display_save_result_animation(False) + return + + user: User = self.session[UserService].get_user(self.session[SessionStorage].user_id) + user.user_mail = self.email_input.text + + if len(self.birthday_input.text) == 0: + user.user_birth_day = None + else: + day, month, year = self.birthday_input.text.split(".") + user.user_birth_day = date(day=int(day), month=int(month), year=int(year)) + + user.user_first_name = self.first_name_input.text + user.user_last_name = self.last_name_input.text + if len(self.new_pw_1_input.text.strip()) > 0: + user.user_password = sha256(self.new_pw_1_input.text.encode(encoding="utf-8")).hexdigest() + + self.session[UserService].update_user(user) + await self.display_save_result_animation(True) + + + + def build(self) -> Component: + user = self.session[UserService].get_user(self.session[SessionStorage].user_id) + pfp = self.session[UserService].get_profile_picture(user.user_id) + + self.saved_text = Text( + "", + margin_top=2, + margin_bottom=1, + align_x=0.1 + ) + + self.email_input = TextInput( + label="E-Mail Adresse", + text=user.user_mail, + margin_left=1, + margin_right=1, + margin_bottom=1, + grow_x=True, + on_change=self.on_email_changed + ) + self.first_name_input = TextInput( + label="Vorname", + text=user.user_first_name, + margin_left=1, + margin_right=1, + grow_x=True + ) + self.last_name_input = TextInput( + label="Nachname", + text=user.user_last_name, + margin_right=1, + grow_x=True + ) + self.birthday_input = TextInput( + label="Geburtstag (TT.MM.JJJJ)", + text=self.optional_date_to_str(user.user_birth_day), + margin_left=1, + margin_right=1, + margin_bottom=1, + grow_x=True, + on_change=self.on_birthday_changed + ) + self.new_pw_1_input = TextInput( + label="Neues Passwort setzen", + text="", + margin_left=1, + margin_right=1, + margin_bottom=1, + grow_x=True, + is_secret=True + ) + self.new_pw_2_input = TextInput( + label="Neues Passwort wiederholen", + text="", + margin_left=1, + margin_right=1, + margin_bottom=1, + grow_x=True, + is_secret=True + ) + + self.pfp_image_container = Image( + from_root("src/ez_lan_manager/assets/img/anon_pfp.png") if pfp is None else pfp, + align_x=0.5, + min_width=10, + min_height=10, + margin_top=1, + margin_bottom=1 + ) + + return BasePage( + content=Column( + MainViewContentBox( + content=Column( + self.pfp_image_container, + Button( + content=Text( + "Neues Bild hochladen", + style=TextStyle(fill=Color.from_hex("02dac5"), font_size=0.9) + ), + align_x=0.5, + margin_bottom=1, + shape="rectangle", + style="major", + color="primary", + on_press=self.upload_new_pfp + ), + Row( + TextInput(label="Deine User-ID", text=user.user_id, is_sensitive=False, margin_left=1, grow_x=False), + TextInput(label="Dein Nickname", text=user.user_name, is_sensitive=False, margin_left=1, margin_right=1, grow_x=True), + margin_bottom=1 + ), + self.email_input, + Row( + self.first_name_input, + self.last_name_input, + margin_bottom=1 + ), + self.birthday_input, + self.new_pw_1_input, + self.new_pw_2_input, + + Row( + self.saved_text, + Button( + content=Text( + "Speichern", + style=TextStyle(fill=self.session.theme.success_color, font_size=0.9), + align_x=0.2 + ), + align_x=0.9, + margin_top=2, + margin_bottom=1, + shape="rectangle", + style="major", + color="primary", + on_press=self.on_save_pressed + ), + ) + ) + ), + align_y=0, + ) + ) diff --git a/src/ez_lan_manager/pages/__init__.py b/src/ez_lan_manager/pages/__init__.py index f4a41d1..6d6c35c 100644 --- a/src/ez_lan_manager/pages/__init__.py +++ b/src/ez_lan_manager/pages/__init__.py @@ -2,4 +2,5 @@ from .BasePage import BasePage from .NewsPage import NewsPage from .PlaceholderPage import PlaceholderPage from .Logout import LogoutPage -from.Account import AccountPage +from .Account import AccountPage +from .EditProfile import EditProfilePage