Skip to content

Commit

Permalink
feat(user): add user view model (#31)
Browse files Browse the repository at this point in the history
* feat(user): add local and remote repository providers

* feat(user): add user view model

* feat(user): update the screens to use the view model

* chore: add devtools options

* refact: handle conflicts

* feat(user): add username functionality
  • Loading branch information
Mo2Hefny authored Nov 1, 2024
1 parent d350ae0 commit e61a6ee
Show file tree
Hide file tree
Showing 17 changed files with 1,191 additions and 297 deletions.
1 change: 0 additions & 1 deletion devtools_options.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:
- provider: true
2 changes: 1 addition & 1 deletion lib/core/mock/user_mock.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'package:telware_cross_platform/core/models/user_model.dart';

final UserModel userMock = UserModel(
username: 'mock.user',
screenName: 'Mocka Mocka',
screenName: 'Mocka Mocker',
email: '[email protected]',
status: 'online',
bio: 'I am a mocking user',
Expand Down
6 changes: 6 additions & 0 deletions lib/core/routes/routes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import 'package:telware_cross_platform/features/stories/view/screens/story_scree
import 'package:telware_cross_platform/features/user/view/screens/block_user.dart';
import 'package:telware_cross_platform/features/user/view/screens/blocked_users.dart';
import 'package:telware_cross_platform/features/user/view/screens/change_number_screen.dart';
import 'package:telware_cross_platform/features/user/view/screens/change_username_screen.dart';
import 'package:telware_cross_platform/features/user/view/screens/privacy_and_security_screen.dart';
import 'package:telware_cross_platform/features/user/view/screens/profile_info_screen.dart';
import 'package:telware_cross_platform/features/user/view/screens/settings_screen.dart';
Expand All @@ -41,6 +42,7 @@ class Routes {
static const String settings = SettingsScreen.route;
static const String changeNumber = ChangeNumberScreen.route;
static const String changeNumberForm = ChangeNumberFormScreen.route;
static const String changeUsername = ChangeUsernameScreen.route;
static const String profileInfo = ProfileInfoScreen.route;
static const String blockUser = BlockUserScreen.route;
static const String blockedUser = BlockedUsersScreen.route;
Expand Down Expand Up @@ -171,6 +173,10 @@ class Routes {
path: Routes.devicesScreen,
builder: (context, state) => const DevicesScreen(),
),
GoRoute(
path: Routes.changeUsername,
builder: (context, state) => const ChangeUsernameScreen(),
),
],
);

Expand Down
32 changes: 26 additions & 6 deletions lib/features/auth/view/screens/change_number_form_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import 'package:telware_cross_platform/features/auth/view/widget/auth_phone_numb
import 'package:telware_cross_platform/features/auth/view/widget/title_element.dart';
import 'package:telware_cross_platform/core/theme/sizes.dart';
import 'package:telware_cross_platform/features/auth/view/widget/auth_floating_action_button.dart';
import 'package:telware_cross_platform/features/user/view/screens/settings_screen.dart';
import 'package:telware_cross_platform/features/user/view_model/user_state.dart';
import 'package:telware_cross_platform/features/user/view_model/user_view_model.dart';
import 'package:vibration/vibration.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

Expand Down Expand Up @@ -53,12 +56,11 @@ class _ChangeNumberFormScreen extends ConsumerState<ChangeNumberFormScreen> {
return MediaQuery.of(context).viewInsets.bottom != 0;
}

void _updatePhoneNumber() {
// ref.read(authViewModelProvider.notifier).updatePhoneNumber(
// phone: phoneController.value.international,
// );
context.pop(); // to close the dialog
context.pushReplacement(Routes.verification);
Future<void> _updatePhoneNumber() async {
final newPhoneNumber = phoneController.value.international;

// Trigger the update process in UserViewModel
await ref.read(userViewModelProvider.notifier).updatePhoneNumber(newPhoneNumber);
}

void _onEdit() {
Expand All @@ -85,6 +87,7 @@ class _ChangeNumberFormScreen extends ConsumerState<ChangeNumberFormScreen> {
// showConfirmationDialog(context,
// "Is this the correct number?", phoneNumber,
// "Yes", "Edit", _updatePhoneNumber, _onEdit);
_updatePhoneNumber();
if (kDebugMode) {
print("Success");
}
Expand All @@ -93,6 +96,23 @@ class _ChangeNumberFormScreen extends ConsumerState<ChangeNumberFormScreen> {

@override
Widget build(BuildContext context) {
final userState = ref.watch(userViewModelProvider);

ref.listen<UserState>(userViewModelProvider, (previous, next) {
if (next.type == UserStateType.success) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(next.message ?? 'Phone number updated successfully')),
);
// context.pop();
// context.pushReplacement(Routes.verification);
context.go(SettingsScreen.route);
} else if (next.type == UserStateType.fail) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(next.message ?? 'Failed to update phone number')),
);
}
});

return Scaffold(
appBar: AppBar(
backgroundColor: Palette.background,
Expand Down
110 changes: 110 additions & 0 deletions lib/features/user/repository/user_local_repository.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import 'package:fpdart/fpdart.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:hive/hive.dart';
import 'package:telware_cross_platform/core/models/user_model.dart';
import 'package:telware_cross_platform/core/models/app_error.dart';

part 'user_local_repository.g.dart';

@Riverpod(keepAlive: true)
UserLocalRepository userLocalRepository(UserLocalRepositoryRef ref) {
return UserLocalRepository(
userBox: Hive.box<UserModel>('auth-user'), // Same box as in auth_local_repository
ref: ref,
);
}

class UserLocalRepository {
final Box<UserModel> _userBox;

UserLocalRepository({
required Box<UserModel> userBox,
required ProviderRef<UserLocalRepository> ref,
}) : _userBox = userBox;

Future<AppError?> changeNumber(String newPhoneNumber) async {
try {
final user = _userBox.get('user');
if (user != null) {
final updatedUser = user.copyWith(phone: newPhoneNumber);
await _userBox.put('user', updatedUser);
} else {
return AppError("User not found.");
}
} catch (error) {
return AppError("Couldn't update phone number. Try again later.");
}
return null;
}

Future<AppError?> updateBio(String newBio) async {
try {
final user = _userBox.get('user');
if (user != null) {
final updatedUser = user.copyWith(bio: newBio);
await _userBox.put('user', updatedUser);
} else {
return AppError("User not found.");
}
} catch (error) {
return AppError("Couldn't update bio. Try again later.");
}
return null;
}

Future<AppError?> updateScreenName(String newScreenName) async {
try {
final user = _userBox.get('user');
if (user != null) {
final updatedUser = user.copyWith(screenName: newScreenName);
await _userBox.put('user', updatedUser);
} else {
return AppError("User not found.");
}
} catch (error) {
return AppError("Couldn't update screen name. Try again later.");
}
return null;
}

Future<AppError?> changeUsername(String newUsername) async {
try {
final user = _userBox.get('user');
if (user != null) {
final updatedUser = user.copyWith(username: newUsername);
await _userBox.put('user', updatedUser);
} else {
return AppError("User not found.");
}
} catch (error) {
return AppError("Couldn't update username. Try again later.");
}
return null;
}

Either<AppError, bool> checkUsernameUniqueness(String username) {
final user = _userBox.get('user');
if (user != null) {
if (user.username == username) {
return Left(AppError("Username is already taken."));
}
return Right(true);
}
return Left(AppError("User not found."));
}

// Retrieves the current user
UserModel? getUser() {
return _userBox.get('user');
}

// Saves the updated user data
Future<void> setUser(UserModel user) async {
await _userBox.put('user', user);
}

// Deletes user data
Future<void> deleteUser() async {
await _userBox.delete('user');
}
}
26 changes: 26 additions & 0 deletions lib/features/user/repository/user_local_repository.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit e61a6ee

Please sign in to comment.