Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/controllers/edit_profile_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class EditProfileController extends GetxController {
RxBool isLoading = false.obs;
Rx<bool> usernameAvailable = false.obs;


bool removeImage = false;
bool showSuccessSnackbar = false;

Expand Down
3 changes: 2 additions & 1 deletion lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -844,7 +844,7 @@
"@stable": {
"description": "A label indicating a stable, non-beta version of the app."
},
"usernameCharacterLimit": "Username should contain more than 5 characters.",
"usernameCharacterLimit": "Username should contain more than 7 characters.",
"@usernameCharacterLimit": {
"description": "Error message when a chosen username is too short."
},
Expand Down Expand Up @@ -1634,4 +1634,5 @@
"@noRecordingError": {
"description": "Error message when trying to exit a live chapter room without any recording."
}

}
76 changes: 58 additions & 18 deletions lib/views/screens/edit_profile_screen.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:loading_animation_widget/loading_animation_widget.dart';
import 'package:resonate/themes/theme_controller.dart';
import 'package:resonate/utils/debouncer.dart';
import 'package:resonate/utils/enums/log_type.dart';
import 'package:resonate/utils/ui_sizes.dart';
import 'package:resonate/views/widgets/loading_dialog.dart';
import 'package:resonate/views/widgets/snackbar.dart';
import 'package:resonate/l10n/app_localizations.dart';

import '../../controllers/auth_state_controller.dart';
Expand All @@ -22,6 +24,7 @@ class EditProfileScreen extends StatelessWidget {
final AuthStateController authStateController = Get.put(
AuthStateController(),
);
final debouncer = Debouncer(milliseconds: 800);

@override
Widget build(BuildContext context) {
Expand All @@ -30,9 +33,7 @@ class EditProfileScreen extends StatelessWidget {
!(editProfileController.isLoading.value ||
editProfileController.isThereUnsavedChanges()),
onPopInvokedWithResult: (didPop, result) async {
if (didPop) {
return;
}
if (didPop) return;

if (!editProfileController.isLoading.value &&
editProfileController.isThereUnsavedChanges()) {
Expand Down Expand Up @@ -112,16 +113,24 @@ class EditProfileScreen extends StatelessWidget {
keyboardType: TextInputType.text,
autocorrect: false,
decoration: InputDecoration(
// hintText: "Name",
labelText: AppLocalizations.of(context)!.name,
prefixIcon: Icon(Icons.abc_rounded),
),
),
SizedBox(height: UiSizes.height_20),
Obx(
() => TextFormField(
maxLength: 36,
validator: (value) {
if (value!.length > 5) {
if (value!.length >= 7) {
final validUsername = RegExp(
r'^[a-zA-Z0-9._-]+$',
).hasMatch(value.trim());
if (!validUsername) {
return AppLocalizations.of(
context,
)!.usernameInvalidOrTaken;
Copy link
Contributor

Choose a reason for hiding this comment

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

Please make a separate string for invalid username and taken username so user can have proper feedback

}
return null;
} else {
return AppLocalizations.of(
Expand All @@ -131,19 +140,55 @@ class EditProfileScreen extends StatelessWidget {
},
controller: controller.usernameController,
onChanged: (value) async {
if (value.length > 5) {
controller.usernameAvailable.value =
await controller.isUsernameAvailable(
value.trim(),
Get.closeCurrentSnackbar();
Copy link
Contributor

Choose a reason for hiding this comment

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

If you are no longer opening snackbars for validation then this is unnecessary

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We are still showing snackbars for validation errors (invalid format and username taken), so Get.closeCurrentSnackbar() is needed isnt

Copy link
Contributor

@M4dhav M4dhav Nov 23, 2025

Choose a reason for hiding this comment

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

but you said AutoValidator will handle those inline so the only snackbar is shown when the submit button is pressed it looks like


if (value.length >= 7) {
final validUsername = RegExp(
r'^[a-zA-Z0-9._-]+$',
).hasMatch(value.trim());

if (!validUsername || value.trim().length > 36) {
controller.usernameAvailable.value = false;
customSnackbar(
AppLocalizations.of(
context,
)!.usernameUnavailable,
"Username can only contain letters, numbers, dots, hyphens, and underscores (max 36 characters)",
Copy link
Contributor

Choose a reason for hiding this comment

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

Add localizations

LogType.error,
snackbarDuration: 1,
);
return;
}

// Temporarily show checking state
controller.usernameAvailable.value = true;
Copy link
Contributor

Choose a reason for hiding this comment

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

This is not a checking state, this will show the user a green tick even while checking


debouncer.run(() async {
final available = await controller
.isUsernameAvailable(value.trim());

controller.usernameAvailable.value = available;

if (!available) {
customSnackbar(
AppLocalizations.of(
context,
)!.usernameUnavailable,
AppLocalizations.of(
context,
)!.usernameInvalidOrTaken,
LogType.error,
snackbarDuration: 1,
);
}
});
} else {
controller.usernameAvailable.value = false;
}
},
keyboardType: TextInputType.text,
autocorrect: false,
decoration: InputDecoration(
// hintText: "Username",
labelText: AppLocalizations.of(context)!.username,
prefixIcon: const Icon(Icons.person),
suffixIcon: controller.usernameAvailable.value
Expand Down Expand Up @@ -298,12 +343,9 @@ class EditProfileScreen extends StatelessWidget {
Column(
children: [
IconButton(
tooltip: AppLocalizations.of(
context,
)!.clickPictureCamera,
tooltip: AppLocalizations.of(context)!.clickPictureCamera,
onPressed: () {
Navigator.pop(context);
// Display Loading Dialog
loadingDialog(context);
editProfileController.pickImageFromCamera();
},
Expand All @@ -322,8 +364,6 @@ class EditProfileScreen extends StatelessWidget {
tooltip: AppLocalizations.of(context)!.pickImageGallery,
onPressed: () {
Navigator.pop(context);

// Display Loading Dialog
loadingDialog(context);
editProfileController.pickImageFromGallery();
},
Expand Down Expand Up @@ -358,4 +398,4 @@ class EditProfileScreen extends StatelessWidget {
],
);
}
}
}