Skip to content

Commit 040dad1

Browse files
committed
feat: implement update password
Signed-off-by: Aman <aman2@me.iitr.ac.in>
1 parent f269bc3 commit 040dad1

14 files changed

Lines changed: 396 additions & 65 deletions

File tree

assets/app/icons/lock_filled.svg

Lines changed: 9 additions & 0 deletions
Loading

lib/data/config/config.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ class Environment {
22
/// URL that is common to most outgoing requests.
33
static const String baseUrl = String.fromEnvironment(
44
'BASE_URL',
5-
// defaultValue: 'https://codephile-test.herokuapp.com/v1/',
6-
defaultValue: 'https://codephile.mdg.iitr.ac.in/v1/',
5+
defaultValue: 'https://codephile-test.herokuapp.com/v1/',
76
);
87

98
/// Project identifier for Sentry error logging.

lib/data/constants/assets.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class AppAssets {
1212
static const String eyeOn = '$_iconsRoot/eye-on.svg';
1313
static const String eyeOff = '$_iconsRoot/eye-off.svg';
1414
static const String lock = '$_iconsRoot/lock.svg';
15+
static const String lockFilled = '$_iconsRoot/lock_filled.svg';
1516
static const String person = '$_iconsRoot/person.svg';
1617
static const String filter = '$_iconsRoot/filter.svg';
1718
static const String bell = '$_iconsRoot/bell.svg';

lib/domain/repositories/user_repository.dart

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ class UserRepository {
287287
throw Exception(AppStrings.genericError);
288288
}
289289

290-
static Future<int> updatePassword(
290+
static Future updatePassword(
291291
String oldPassword,
292292
String newPassword,
293293
) async {
@@ -304,7 +304,14 @@ class UserRepository {
304304
},
305305
);
306306

307-
return response['status_code'];
307+
switch (response['status_code']) {
308+
case 200:
309+
return;
310+
case 403:
311+
throw const IncorrectCredentials();
312+
default:
313+
throw const InternalFailure();
314+
}
308315
}
309316

310317
static Future<int> updateUserDetails(Map<String, dynamic>? data) async {

lib/presentation/components/inputs/text_input.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class TextInput extends StatelessWidget {
2424
this.border,
2525
this.onSubmitted,
2626
this.prefixIconConstraints,
27+
this.validator,
2728
Key? key,
2829
}) : assert(
2930
controller != null || onChanged != null,
@@ -47,11 +48,13 @@ class TextInput extends StatelessWidget {
4748
final Color? fillColor;
4849
final InputBorder? border;
4950
final BoxConstraints? prefixIconConstraints;
51+
final String? Function(String?)? validator;
5052

5153
@override
5254
Widget build(BuildContext context) {
5355
return TextFormField(
5456
controller: controller,
57+
validator: validator,
5558
decoration: InputDecoration(
5659
errorText: errorText,
5760
hintText: hint,
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_screenutil/flutter_screenutil.dart';
3+
4+
import '../../../data/constants/colors.dart';
5+
import '../../../data/constants/styles.dart';
6+
7+
class PrimaryButton extends StatelessWidget {
8+
const PrimaryButton({
9+
required this.label,
10+
this.onPressed,
11+
Key? key,
12+
}) : super(key: key);
13+
final String label;
14+
final VoidCallback? onPressed;
15+
16+
@override
17+
Widget build(BuildContext context) {
18+
return TextButton(
19+
onPressed: onPressed,
20+
style: ButtonStyle(
21+
backgroundColor: MaterialStateProperty.all<Color>(AppColors.primary),
22+
shape: MaterialStateProperty.all<OutlinedBorder>(
23+
const ContinuousRectangleBorder(),
24+
),
25+
side: MaterialStateProperty.all<BorderSide>(BorderSide.none),
26+
),
27+
child: Container(
28+
alignment: Alignment.center,
29+
height: 48.r,
30+
padding: EdgeInsets.symmetric(horizontal: 40.r),
31+
child: Text(
32+
label,
33+
style: AppStyles.h6.copyWith(
34+
color: AppColors.white,
35+
fontWeight: FontWeight.w700,
36+
),
37+
),
38+
),
39+
);
40+
}
41+
}

lib/presentation/core/router.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:flutter/material.dart';
2+
import 'package:flutter_bloc/flutter_bloc.dart';
23
import 'package:get/get.dart';
34

45
import '../../data/constants/routes.dart';
@@ -7,6 +8,7 @@ import '../login/login_screen.dart';
78
import '../onboarding/onboarding_screen.dart';
89
import '../signup/signup_screen.dart';
910
import '../signup/verify_screen.dart';
11+
import '../update_profile/bloc/update_profile_bloc.dart';
1012
import '../update_profile/update_profile.dart';
1113

1214
/// Wrapper for a single method to be passed to [GetMaterialApp.onGenerateRoute].
@@ -45,7 +47,10 @@ class AppRouter {
4547
);
4648
case AppRoutes.updateProfile:
4749
return GetPageRoute(
48-
page: () => const UpdateProfile(),
50+
page: () => BlocProvider(
51+
create: (context) => UpdateProfileBloc()..add(const Initialize()),
52+
child: const UpdateProfile(),
53+
),
4954
routeName: settings.name,
5055
settings: settings,
5156
);

lib/presentation/update_profile/bloc/update_profile_bloc.dart

Lines changed: 84 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import 'dart:async';
12
import 'dart:io';
23

34
import 'package:equatable/equatable.dart';
5+
import 'package:flutter/widgets.dart';
46
import 'package:flutter_bloc/flutter_bloc.dart';
57
import 'package:freezed_annotation/freezed_annotation.dart';
68

@@ -9,6 +11,8 @@ import '../../../data/services/local/storage_service.dart';
911
import '../../../domain/models/status.dart';
1012
import '../../../domain/models/user.dart';
1113
import '../../../domain/repositories/user_repository.dart';
14+
import '../../../utils/failures.dart';
15+
import '../../../utils/snackbar.dart';
1216

1317
part 'update_profile_event.dart';
1418
part 'update_profile_state.dart';
@@ -19,24 +23,41 @@ class UpdateProfileBloc extends Bloc<UpdateProfileEvent, UpdateProfileState> {
1923
on<Initialize>(_onInitialize);
2024
on<SelectImage>(_onSelectImage);
2125
on<UpdateInstitute>(_onUpdateInstitute);
26+
on<SwitchView>(_onSwitchView);
27+
on<UpdateFocusField>(_onUpdateFocusField);
28+
on<ToggleObscure>(_onToggleObscure);
29+
on<UpdatePassword>(_onUpdatePassword);
2230
}
2331

32+
static List<String> institutes = <String>[
33+
'Indian Institute of Technology Roorkee',
34+
'Indian Institute of Technology Delhi',
35+
'Indian Institute of Technology Mandi',
36+
'Indian Institute of Technology Indore',
37+
'Indian Institute of Technology Bombay',
38+
];
39+
2440
void _onInitialize(Initialize event, Emitter<UpdateProfileState> emit) async {
2541
final _instituteList = await UserRepository.getInstituteList();
26-
if (_instituteList.isEmpty) {
27-
_instituteList.addAll([
28-
'Indian Institute of Technology Roorkee',
29-
'Indian Institute of Technology Delhi',
30-
'Indian Institute of Technology Mandi',
31-
'Indian Institute of Technology Indore',
32-
'Indian Institute of Technology Bombay'
33-
]);
42+
if (_instituteList.isNotEmpty) {
43+
institutes = _instituteList;
3444
}
3545

46+
final _controllers = <String, TextEditingController?>{
47+
'codechef': TextEditingController(),
48+
'codeforces': TextEditingController(),
49+
'hackerrank': TextEditingController(),
50+
'spoj': TextEditingController(),
51+
'leetcode': TextEditingController(),
52+
'old_pass': TextEditingController(),
53+
'new_pass': TextEditingController(),
54+
're_enter': TextEditingController(),
55+
};
56+
3657
emit(state.copyWith(
3758
status: const Status(),
38-
instituteList: _instituteList,
3959
user: _currentUser,
60+
controllers: _controllers,
4061
));
4162
}
4263

@@ -52,6 +73,60 @@ class UpdateProfileBloc extends Bloc<UpdateProfileEvent, UpdateProfileState> {
5273
emit(state.copyWith(user: _updatedUser));
5374
}
5475

76+
void _onSwitchView(SwitchView event, Emitter<UpdateProfileState> emit) {
77+
final currentState = state.showChangePasswordView;
78+
final _controllers = state.controllers;
79+
_controllers['old_pass']?.text = '';
80+
_controllers['new_pass']?.text = '';
81+
_controllers['re_enter']?.text = '';
82+
emit(state.copyWith(
83+
showChangePasswordView: !currentState,
84+
activePasswordTextField: -1,
85+
controllers: _controllers,
86+
));
87+
}
88+
89+
void _onUpdateFocusField(
90+
UpdateFocusField event, Emitter<UpdateProfileState> emit) {
91+
emit(state.copyWith(activePasswordTextField: event.index));
92+
}
93+
94+
void _onToggleObscure(ToggleObscure event, Emitter<UpdateProfileState> emit) {
95+
final list = state.passwordFieldObscureState;
96+
emit(state.copyWith(passwordFieldObscureState: [
97+
...List.generate(
98+
list.length,
99+
(index) {
100+
if (index == event.index) return !list[index];
101+
return list[index];
102+
},
103+
)
104+
]));
105+
}
106+
107+
void _onUpdatePassword(
108+
UpdatePassword event, Emitter<UpdateProfileState> emit) async {
109+
emit(state.copyWith(isUpdating: true));
110+
try {
111+
await UserRepository.updatePassword(state.controllers['old_pass']!.text,
112+
state.controllers['new_pass']!.text);
113+
add(const SwitchView());
114+
showSnackBar(message: 'Password Changed');
115+
} on Failure catch (err) {
116+
showSnackBar(message: err.message);
117+
}
118+
emit(state.copyWith(isUpdating: false));
119+
}
120+
121+
Future<bool> onWillPop() async {
122+
if (state.showChangePasswordView) {
123+
add(const SwitchView());
124+
return false;
125+
}
126+
127+
return true;
128+
}
129+
55130
final User _currentUser = StorageService.user!;
56131
User _updatedUser = StorageService.user!;
57132
}

lib/presentation/update_profile/bloc/update_profile_event.dart

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,35 @@ class UpdateInstitute extends UpdateProfileEvent {
2626
@override
2727
List<Object?> get props => [institute];
2828
}
29+
30+
class SwitchView extends UpdateProfileEvent {
31+
const SwitchView();
32+
33+
@override
34+
List<Object?> get props => [];
35+
}
36+
37+
class UpdateFocusField extends UpdateProfileEvent {
38+
const UpdateFocusField({required this.index});
39+
40+
final int index;
41+
42+
@override
43+
List<Object?> get props => [index];
44+
}
45+
46+
class ToggleObscure extends UpdateProfileEvent {
47+
const ToggleObscure({required this.index});
48+
49+
final int index;
50+
51+
@override
52+
List<Object?> get props => [index];
53+
}
54+
55+
class UpdatePassword extends UpdateProfileEvent {
56+
const UpdatePassword();
57+
58+
@override
59+
List<Object?> get props => [];
60+
}

lib/presentation/update_profile/bloc/update_profile_state.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@ part of 'update_profile_bloc.dart';
44
class UpdateProfileState with _$UpdateProfileState {
55
const factory UpdateProfileState({
66
@Default(Status.loading()) Status status,
7+
@Default(false) bool showChangePasswordView,
78
File? image,
89
User? user,
9-
List<String>? instituteList,
10+
@Default(-1) int activePasswordTextField,
11+
@Default([true, true, true]) List<bool> passwordFieldObscureState,
12+
@Default({}) Map<String, TextEditingController?> controllers,
13+
@Default(false) bool isUpdating,
1014
}) = _UpdateProfileState;
1115

1216
const UpdateProfileState._();

0 commit comments

Comments
 (0)