diff --git a/app/routes/auth.py b/app/routes/auth.py index 9aeee6a..0ee5f7d 100644 --- a/app/routes/auth.py +++ b/app/routes/auth.py @@ -205,21 +205,26 @@ def register(): return redirect(url_for('main.dashboard')) if request.method == 'POST': + # Build form data from request to preserve state across re-renders + form_data = dict(request.form) + form_data['games'] = request.form.getlist('games') + # Validate CAPTCHA first captcha_answer = request.form.get('captcha_answer', '') if not verify_captcha(captcha_answer): flash('Incorrect CAPTCHA answer. Please try again.', 'danger') - captcha = generate_captcha() # Generate new captcha + captcha = generate_captcha() + # Clear password fields only on CAPTCHA failure + form_data.pop('password', None) + form_data.pop('confirm_password', None) return render_template( 'pages/register.html', esport_games=ESPORT_GAMES, - captcha=captcha + captcha=captcha, + form_data=form_data, ) # Validate input with marshmallow schema - # Convert MultiDict to dict with proper lists for checkbox fields - form_data = dict(request.form) - form_data['games'] = request.form.getlist('games') register_schema = RegisterSchema() try: validated = register_schema.load(form_data) @@ -228,10 +233,14 @@ def register(): for msg in messages: flash(f'{field}: {msg}', 'danger') captcha = generate_captcha() + # Clear password fields on validation failure + form_data.pop('password', None) + form_data.pop('confirm_password', None) return render_template( 'pages/register.html', esport_games=ESPORT_GAMES, - captcha=captcha + captcha=captcha, + form_data=form_data, ) username = validated['username'] @@ -242,25 +251,30 @@ def register(): selected_games = validated.get('games', []) discord_username = validated.get('discord_username') discord_user_id = validated.get('discord_user_id') - trn_username = request.form.get('trn_username', '').strip() or None league_os_profile = validated.get('league_os_profile') if User.query.filter_by(username=username).first(): flash('Username already exists.', 'danger') captcha = generate_captcha() + form_data.pop('password', None) + form_data.pop('confirm_password', None) return render_template( 'pages/register.html', esport_games=ESPORT_GAMES, - captcha=captcha + captcha=captcha, + form_data=form_data, ) if User.query.filter_by(email=email).first(): flash('Email already registered.', 'danger') captcha = generate_captcha() + form_data.pop('password', None) + form_data.pop('confirm_password', None) return render_template( 'pages/register.html', esport_games=ESPORT_GAMES, - captcha=captcha + captcha=captcha, + form_data=form_data, ) hashed_password = hash_password(password) @@ -299,9 +313,14 @@ def register(): flash('Your account has been created! You can now log in.', 'success') return redirect(url_for('auth.login')) - # Generate CAPTCHA only for GET requests + # GET request — render empty form captcha = generate_captcha() - return render_template('pages/register.html', esport_games=ESPORT_GAMES, captcha=captcha) + return render_template( + 'pages/register.html', + esport_games=ESPORT_GAMES, + captcha=captcha, + form_data={}, + ) @auth_bp.route('/discord/login') diff --git a/app/templates/pages/register.html b/app/templates/pages/register.html index 0f2c032..9110ec4 100644 --- a/app/templates/pages/register.html +++ b/app/templates/pages/register.html @@ -3,71 +3,39 @@ {% block auth_content %}
+ + +

Personal Information

- +
- +
- +
- +

-

E-Sports Profile

-

Set up your competitive gaming profile for tryouts.

-
- -
- {% for game in esport_games %} - - {% endfor %} -
- Select all games you're signing in for. -
+ +

Discord Connection

+

Connect your Discord account to automatically fill your gamertags from your connected game accounts.

- - {% set discord_suggestions = session.get('discord_oauth', {}).get('gamertag_suggestions', {}) %} - {% for game in esport_games %} -
- - - - Your in-game name or username for {{ game }}. - {% if game in discord_suggestions %} - Pre-filled from Discord connections. - {% endif %} - -
- {% endfor %} - -
- - - Your public Tracker Network profile name. Others can click it to view your stats. -
- - {% set discord_data = session.get('discord_oauth') %} {% if discord_data %}
-
{% if discord_data.avatar %} @@ -92,27 +60,76 @@ - Your Discord account is connected. Gamertags from your linked game accounts have been pre-filled. + Discord connected. Game connections have been used to pre-fill your profile below.
{% else %}
- -

Connect your Discord account to automatically fill your profile and gamertags from your connected game accounts (Steam, Battle.net, Xbox, etc.).

Connect Discord Account - You can also enter your Discord username manually below. + Connect to pre-fill your gamertags from Steam, Battle.net, Xbox, etc.
- +
{% endif %} +
+ + +

E-Sports Profile

+

Set up your competitive gaming profile for tryouts.

+
- - + +
+ {% set selected_games = form_data.get('games', []) %} + {% set auto_select = session.get('discord_oauth', {}).get('auto_select_games', []) %} + {% for game in esport_games %} + {% set is_checked = game in selected_games or (not form_data and game in auto_select) %} + + {% endfor %} +
+ Select all games you're signing in for. +
+ + + {% set discord_suggestions = session.get('discord_oauth', {}).get('gamertag_suggestions', {}) %} + {% for game in esport_games %} + {% set game_id = game|replace(' ', '_') %} + {% set form_gamertag = form_data.get('gamertag_' ~ game, '') if form_data else '' %} + {% set suggested = discord_suggestions.get(game, '') %} + {% set gamertag_val = form_gamertag if form_gamertag else suggested %} + {% set game_checked = (form_data and game in selected_games) or (not form_data and game in auto_select) %} + {% set is_visible = gamertag_val or game_checked %} +
+ + + + Your in-game name for {{ game }}. + {% if suggested and not form_gamertag %} + Pre-filled from Discord connections. + {% endif %} + +
+ {% endfor %} + +
+ + Connect your League OS profile for organized play.
@@ -120,16 +137,18 @@

Security

- +
- +
- +
@@ -143,13 +162,6 @@ function toggleGamertagInput(checkbox) { var group = document.getElementById('gamertag_group_' + game); if (group) { group.style.display = checkbox.checked ? 'block' : 'none'; - // Clear the input when unchecking (unless it was pre-filled from Discord) - if (!checkbox.checked) { - var input = group.querySelector('input[type="text"]'); - if (input && !input.dataset.discordPrefilled) { - input.value = ''; - } - } } } @@ -157,16 +169,7 @@ function toggleGamertagInput(checkbox) { document.addEventListener('DOMContentLoaded', function() { var checkboxes = document.querySelectorAll('input[name="games"]'); checkboxes.forEach(function(checkbox) { - var game = checkbox.value.replace(/ /g, '_'); - var group = document.getElementById('gamertag_group_' + game); - if (group) { - group.style.display = checkbox.checked ? 'block' : 'none'; - // Mark Discord-prefilled inputs so they aren't cleared on uncheck - var input = group.querySelector('input[type="text"]'); - if (input && input.value) { - input.dataset.discordPrefilled = 'true'; - } - } + toggleGamertagInput(checkbox); }); }); diff --git a/app/validators.py b/app/validators.py index 907a5b6..d0acd13 100644 --- a/app/validators.py +++ b/app/validators.py @@ -213,10 +213,6 @@ class RegisterSchema(StripMixin): allow_none=True, load_default=None, ) - trn_username = fields.String( - allow_none=True, - load_default=None, - ) league_os_profile = fields.String( validate=validate.Length(max=256), allow_none=True,