Skip to content

Comments

add password protected tournaments#2544

Merged
veloce merged 4 commits intolichess-org:mainfrom
HaonRekcef:tournament-password
Jan 26, 2026
Merged

add password protected tournaments#2544
veloce merged 4 commits intolichess-org:mainfrom
HaonRekcef:tournament-password

Conversation

@HaonRekcef
Copy link
Collaborator

closes #1935

video:

tournament_password.mp4

includes testcases

Copilot AI review requested due to automatic review settings January 18, 2026 22:46
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds support for password-protected tournaments, allowing users to join private tournaments by entering a password/entry code. The implementation includes comprehensive test coverage for the password entry flow, both for successful joins and incorrect password scenarios.

Changes:

  • Added private field to the Tournament data model to identify password-protected tournaments
  • Updated tournament join API to accept an optional password parameter
  • Added password dialog UI that prompts users when joining private tournaments
  • Included comprehensive test cases covering password dialog display, successful joins, and error handling

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
lib/src/model/tournament/tournament.dart Added private boolean field to Tournament model and parsing logic to identify password-protected tournaments
lib/src/model/tournament/tournament_repository.dart Updated join method to accept optional password parameter and include it in the API request body
lib/src/model/tournament/tournament_controller.dart Updated joinOrPause method signature to accept optional password and properly await repository calls
lib/src/view/tournament/tournament_screen.dart Added password dialog and join logic for private tournaments with proper error handling for incorrect passwords
test/view/tournament/tournament_screen_test.dart Added comprehensive test coverage including password dialog display, successful join with correct password, and error handling for incorrect password

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 1759 to 1782
Future<String?> _showPasswordDialog(BuildContext context) {
final TextEditingController controller = TextEditingController();

return showDialog<String>(
context: context,
builder: (context) {
return AlertDialog.adaptive(
title: Text(context.l10n.tournamentEntryCode),
content: TextField(
controller: controller,
decoration: InputDecoration(hintText: context.l10n.password),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(context.l10n.cancel),
),
TextButton(
onPressed: () => Navigator.of(context).pop(controller.text),
child: Text(context.l10n.join),
),
],
);
},
Copy link

Copilot AI Jan 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TextEditingController is created but never explicitly disposed. While this is acceptable for short-lived dialogs and the controller will be garbage collected, consider wrapping the dialog content in a StatefulWidget to properly dispose of the controller following Flutter best practices, similar to how it's done in other parts of the codebase (e.g., chat_screen.dart).

Suggested change
Future<String?> _showPasswordDialog(BuildContext context) {
final TextEditingController controller = TextEditingController();
return showDialog<String>(
context: context,
builder: (context) {
return AlertDialog.adaptive(
title: Text(context.l10n.tournamentEntryCode),
content: TextField(
controller: controller,
decoration: InputDecoration(hintText: context.l10n.password),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(context.l10n.cancel),
),
TextButton(
onPressed: () => Navigator.of(context).pop(controller.text),
child: Text(context.l10n.join),
),
],
);
},
class _PasswordDialog extends StatefulWidget {
const _PasswordDialog();
@override
State<_PasswordDialog> createState() => _PasswordDialogState();
}
class _PasswordDialogState extends State<_PasswordDialog> {
late final TextEditingController _controller;
@override
void initState() {
super.initState();
_controller = TextEditingController();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AlertDialog.adaptive(
title: Text(context.l10n.tournamentEntryCode),
content: TextField(
controller: _controller,
decoration: InputDecoration(hintText: context.l10n.password),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(context.l10n.cancel),
),
TextButton(
onPressed: () => Navigator.of(context).pop(_controller.text),
child: Text(context.l10n.join),
),
],
);
}
}
Future<String?> _showPasswordDialog(BuildContext context) {
return showDialog<String>(
context: context,
builder: (context) => const _PasswordDialog(),

Copilot uses AI. Check for mistakes.
@TBestLittleHelper
Copy link

I think both the code and the UI should consistently call it an "entry code" or similar and avoid naming it password. Since password comes with expectations around encryption etc. Which is not what the code is intended for, it's for giving to others so they can join. It's why the website renamed it back in 2021 lichess-org/lila#9467

@HaonRekcef
Copy link
Collaborator Author

I think both the code and the UI should consistently call it an "entry code" or similar and avoid naming it password. Since password comes with expectations around encryption etc. Which is not what the code is intended for, it's for giving to others so they can join. It's why the website renamed it back in 2021 lichess-org/lila#9467

Good point, makes sense!
I will adjust it later today.

@veloce
Copy link
Contributor

veloce commented Jan 21, 2026

Thanks for the PR. Will review it when I have dealt with older ones.

Copy link
Contributor

@veloce veloce left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice

.read(tournamentControllerProvider(widget.state.id).notifier)
.joinOrPause(entryCode: entryCode);
} catch (e) {
setState(() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better put this in a finally clause.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the join request is successful line 1089 will update the joinOrLeaveProgress (which is better than updating it immediately so that it keeps spinning until the green bar appears) so we only need it for the unsuccessful case.

@HaonRekcef HaonRekcef requested a review from veloce January 23, 2026 19:48
@veloce veloce merged commit 15815bc into lichess-org:main Jan 26, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Joining user created(private) tournaments in the lichess mobile app

3 participants