This commit is contained in:
mohammad
2024-07-23 16:16:31 +03:00
parent 8a4a158af5
commit e5e839248d
17 changed files with 457 additions and 516 deletions

View File

@ -1,3 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
@ -8,6 +9,7 @@ import 'package:syncrow_app/features/auth/model/user_model.dart';
import 'package:syncrow_app/navigation/navigation_service.dart'; import 'package:syncrow_app/navigation/navigation_service.dart';
import 'package:syncrow_app/navigation/routing_constants.dart'; import 'package:syncrow_app/navigation/routing_constants.dart';
import 'package:syncrow_app/services/api/authentication_api.dart'; import 'package:syncrow_app/services/api/authentication_api.dart';
import 'package:syncrow_app/services/api/profile_api.dart';
import 'package:syncrow_app/utils/helpers/shared_preferences_helper.dart'; import 'package:syncrow_app/utils/helpers/shared_preferences_helper.dart';
import 'package:syncrow_app/utils/helpers/snack_bar.dart'; import 'package:syncrow_app/utils/helpers/snack_bar.dart';
import 'package:syncrow_app/utils/resource_manager/strings_manager.dart'; import 'package:syncrow_app/utils/resource_manager/strings_manager.dart';
@ -178,12 +180,18 @@ class AuthCubit extends Cubit<AuthState> {
if (token.accessTokenIsNotEmpty) { if (token.accessTokenIsNotEmpty) {
debugPrint('token: ${token.accessToken}'); debugPrint('token: ${token.accessToken}');
FlutterSecureStorage storage = const FlutterSecureStorage(); FlutterSecureStorage storage = const FlutterSecureStorage();
await storage.write(key: Token.loginAccessTokenKey, value: token.accessToken); await storage.write(
key: Token.loginAccessTokenKey,
value: token.accessToken
);
await fetchUserInfo();
const FlutterSecureStorage().write( const FlutterSecureStorage().write(
key: UserModel.userUuidKey, key: UserModel.userUuidKey,
value: Token.decodeToken(token.accessToken)['uuid'].toString()); value: Token.decodeToken(token.accessToken)['uuid'].toString()
).then((value) async {
var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
user = await ProfileApi.fetchUserInfo(uuid);
},);
user = UserModel.fromToken(token); user = UserModel.fromToken(token);
emailController.clear(); emailController.clear();
passwordController.clear(); passwordController.clear();
@ -277,8 +285,7 @@ class AuthCubit extends Cubit<AuthState> {
try { try {
emit(AuthTokenLoading()); emit(AuthTokenLoading());
const storage = FlutterSecureStorage(); const storage = FlutterSecureStorage();
final firstLaunch = final firstLaunch = await SharedPreferencesHelper.readBoolFromSP(StringsManager.firstLaunch) ?? true;
await SharedPreferencesHelper.readBoolFromSP(StringsManager.firstLaunch) ?? true;
if (firstLaunch) { if (firstLaunch) {
storage.deleteAll(); storage.deleteAll();
@ -311,6 +318,7 @@ class AuthCubit extends Cubit<AuthState> {
} }
} }
sendToForgetPassword({required String password}) async { sendToForgetPassword({required String password}) async {
try { try {
emit(AuthForgetPassLoading()); emit(AuthForgetPassLoading());
@ -320,4 +328,18 @@ class AuthCubit extends Cubit<AuthState> {
emit(AuthForgetPassError(message: 'Something went wrong')); emit(AuthForgetPassError(message: 'Something went wrong'));
} }
} }
Future fetchUserInfo() async {
try {
emit(AuthLoading());
var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
user = await ProfileApi.fetchUserInfo(uuid);
emit(AuthLoginSuccess());
} catch (e) {
return;
}
}
} }

View File

@ -4,34 +4,42 @@ class UserModel {
static String userUuidKey = 'userUuid'; static String userUuidKey = 'userUuid';
final String? uuid; final String? uuid;
final String? email; final String? email;
final String? name; final String? firstName;
final String? photoUrl; final String? lastName;
final String? profilePicture;
final String? phoneNumber; final String? phoneNumber;
final bool? isEmailVerified; final bool? isEmailVerified;
final String? regionName;
final String? timeZone;
final bool? isAgreementAccepted; final bool? isAgreementAccepted;
UserModel({ UserModel({
required this.uuid, required this.uuid,
required this.email, required this.email,
required this.name, required this.firstName,
required this.photoUrl, required this.lastName,
required this.profilePicture,
required this.phoneNumber, required this.phoneNumber,
required this.isEmailVerified, required this.isEmailVerified,
required this.isAgreementAccepted, required this.isAgreementAccepted,
required this.regionName, // Add this line
required this.timeZone, // Add this line
}); });
factory UserModel.fromJson(Map<String, dynamic> json) { factory UserModel.fromJson(Map<String, dynamic> json) {
return UserModel( return UserModel(
uuid: json['id'], uuid: json['uuid'],
email: json['email'], email: json['email'],
name: json['name'], firstName: json['firstName'],
photoUrl: json['photoUrl'], lastName: json['lastName'],
profilePicture: json['profilePicture'],
phoneNumber: json['phoneNumber'], phoneNumber: json['phoneNumber'],
isEmailVerified: json['isEmailVerified'], isEmailVerified: json['isEmailVerified'],
isAgreementAccepted: json['isAgreementAccepted'], isAgreementAccepted: json['isAgreementAccepted'],
regionName: json['region']?['regionName'], // Extract regionName
timeZone: json['timeZone']?['timeZoneOffset'], // Extract regionName
); );
} }
@ -44,11 +52,16 @@ class UserModel {
return UserModel( return UserModel(
uuid: tempJson['uuid'].toString(), uuid: tempJson['uuid'].toString(),
email: tempJson['email'], email: tempJson['email'],
name: null, lastName: tempJson['lastName'],
photoUrl: null, firstName:tempJson['firstName'] ,
profilePicture: tempJson['profilePicture'],
phoneNumber: null, phoneNumber: null,
isEmailVerified: null, isEmailVerified: null,
isAgreementAccepted: null, isAgreementAccepted: null,
regionName: tempJson['region']?['regionName'],
timeZone: tempJson['timezone']?['timeZoneOffset'],
); );
} }
@ -56,8 +69,9 @@ class UserModel {
return { return {
'id': uuid, 'id': uuid,
'email': email, 'email': email,
'name': name, 'lastName': lastName,
'photoUrl': photoUrl, 'firstName': firstName,
'photoUrl': profilePicture,
'phoneNumber': phoneNumber, 'phoneNumber': phoneNumber,
'isEmailVerified': isEmailVerified, 'isEmailVerified': isEmailVerified,
'isAgreementAccepted': isAgreementAccepted, 'isAgreementAccepted': isAgreementAccepted,

View File

@ -9,85 +9,4 @@ class MenuCubit extends Cubit<MenuState> {
String name = ''; String name = '';
// List<MenuListModel> menuLists = [
// MenuListModel(
// label: 'Home Management',
// listItems: [
// ListItemModel(
// label: 'Create a Home',
// ),
// ListItemModel(
// label: 'Join a Home',
// ),
// ListItemModel(
// label: 'Manage Your Home',
// ),
// ],
// ),
// MenuListModel(
// label: 'General Settings',
// listItems: [
// ListItemModel(
// label: 'Voice Assistant',
// ),
// ListItemModel(
// label: 'Temperature unit',
// ),
// ListItemModel(
// label: 'Touch tone on panel',
// ),
// ListItemModel(
// label: 'Language',
// ),
// ListItemModel(
// label: 'Network diagnosis',
// ),
// ListItemModel(
// label: 'Clear cache',
// ),
// ],
// ),
// MenuListModel(
// label: 'Messages Center',
// listItems: [
// ListItemModel(
// label: 'Alerts',
// ),
// ListItemModel(
// label: 'Messages',
// ),
// ListItemModel(
// label: 'FAQs',
// ),
// ListItemModel(
// label: 'Help & Feedback',
// ),
// ],
// ),
// MenuListModel(
// label: 'Security and Privacy',
// listItems: [
// ListItemModel(
// label: 'Security',
// ),
// ListItemModel(
// label: 'privacy',
// ),
// ],
// ),
// MenuListModel(
// label: 'Legal Information',
// listItems: [
// ListItemModel(
// label: 'About',
// ),
// ListItemModel(
// label: 'Privacy Policy',
// ),
// ListItemModel(
// label: 'User Agreement',
// ),
// ],
// ),
// ];
} }

View File

@ -1,19 +1,21 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart'; import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart';
import 'package:syncrow_app/features/menu/bloc/profile_bloc/profile_event.dart'; import 'package:syncrow_app/features/menu/bloc/profile_bloc/profile_event.dart';
import 'package:syncrow_app/features/menu/bloc/profile_bloc/profile_state.dart'; import 'package:syncrow_app/features/menu/bloc/profile_bloc/profile_state.dart';
import 'package:syncrow_app/features/menu/bloc/profile_bloc/region_model.dart'; import 'package:syncrow_app/features/menu/bloc/profile_bloc/region_model.dart';
import 'package:syncrow_app/features/menu/bloc/profile_bloc/time_zone_model.dart'; import 'package:syncrow_app/features/menu/bloc/profile_bloc/time_zone_model.dart';
import 'package:syncrow_app/services/api/profile_api.dart';
import 'package:syncrow_app/utils/helpers/snack_bar.dart'; import 'package:syncrow_app/utils/helpers/snack_bar.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
class ProfileBloc extends Bloc<ProfileEvent, ProfileState> { class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
final String userId;
bool isSaving = false; bool isSaving = false;
bool editName = false; bool editName = false;
final FocusNode focusNode = FocusNode(); final FocusNode focusNode = FocusNode();
@ -22,12 +24,13 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
String timeZoneSelected = ''; String timeZoneSelected = '';
String regionSelected = ''; String regionSelected = '';
final TextEditingController searchController = TextEditingController(); final TextEditingController searchController = TextEditingController();
final TextEditingController nameController = TextEditingController(text: 'asd alsdkasdd '); final TextEditingController nameController = TextEditingController(text: '${AuthCubit.user!.firstName} ${AuthCubit.user!.lastName}');
List<TimeZone>? timeZoneList; List<TimeZone>? timeZoneList;
List<RegionModel>? regionList; List<RegionModel>? regionList;
ProfileBloc({required this.userId}) : super(InitialState()) {
on<InitialEvent>(_fetchUserInfo); ProfileBloc() : super(InitialState()) {
on<InitialProfileEvent>(_fetchUserInfo);
on<TimeZoneInitialEvent>(_fetchTimeZone); on<TimeZoneInitialEvent>(_fetchTimeZone);
on<RegionInitialEvent>(_fetchRegion); on<RegionInitialEvent>(_fetchRegion);
on<SaveNameEvent>(saveName); on<SaveNameEvent>(saveName);
@ -35,46 +38,18 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
on<ChangeNameEvent>(_changeName); on<ChangeNameEvent>(_changeName);
on<SelectTimeZoneEvent>(selectTimeZone); on<SelectTimeZoneEvent>(selectTimeZone);
on<SearchRegionEvent>(searchRegion); on<SearchRegionEvent>(searchRegion);
on<SearchTimeZoneEvent>(searchTimeZone);
on<SelectRegionEvent>(selectRegion);
} }
Uint8List? decodeBase64Image(String? base64String) {
List<Map<String, String>> timeZone= if (base64String != null) {
[ final startIndex = base64String.indexOf('base64,') + 7;
{"name": "GMT-12:00", "offset": "-12:00","id":'1'}, final pureBase64String = base64String.substring(startIndex);
return base64.decode(pureBase64String);
{"name": "GMT-11:00", "offset": "-11:00","id":'2'}, }
return null;
{"name": "GMT-10:00", "offset": "-10:00","id":'3'}, }
{"name": "GMT-09:00", "offset": "-09:00","id":'4'},
{"name": "GMT-08:00", "offset": "-08:00","id":'5'},
{"name": "GMT-07:00", "offset": "-07:00","id":'6'},
{"name": "GMT-06:00", "offset": "-06:00","id":'7'},
{"name": "GMT-05:00", "offset": "-05:00","id":'8'},
{"name": "GMT-04:00", "offset": "-04:00","id":'9'},
];
List<Map<String, String>> region=
[
{"name": "region 1", "id":'1'},
{"name": "region 2", "id":'2'},
{"name": "region 3","id":'3'},
{"name": "region 4", "id":'4'},
{"name": "region 5","id":'5'},
{"name": "region 6","id":'6'},
{"name": "region 7","id":'7'},
{"name": "region 8", "id":'8'},
];
void _changeName(ChangeNameEvent event, Emitter<ProfileState> emit) { void _changeName(ChangeNameEvent event, Emitter<ProfileState> emit) {
emit(LoadingInitialState()); emit(LoadingInitialState());
editName = event.value!; editName = event.value!;
@ -88,30 +63,21 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
emit(NameEditingState(editName: editName)); emit(NameEditingState(editName: editName));
} }
void _fetchUserInfo(InitialEvent event, Emitter<ProfileState> emit) async { void _fetchUserInfo(InitialProfileEvent event, Emitter<ProfileState> emit) async {
try { try {
emit(LoadingInitialState()); emit(LoadingInitialState());
// var response = await ProfileApi.fetchUserInfo(userId); AuthCubit.user = await ProfileApi.fetchUserInfo(AuthCubit.user!.uuid);
// List<StatusModel> statusModelList = [];
// for (var status in response['status']) {
// statusModelList.add(StatusModel.fromJson(status));
// }
} catch (e) { } catch (e) {
emit(FailedState(errorMessage: e.toString())); emit(FailedState(errorMessage: e.toString()));
return; return;
} }
} }
Future _fetchTimeZone( Future _fetchTimeZone(TimeZoneInitialEvent event, Emitter<ProfileState> emit) async {
TimeZoneInitialEvent event, Emitter<ProfileState> emit) async {
emit(LoadingInitialState()); emit(LoadingInitialState());
try { try {
timeZoneList = timeZone.map((zone) => TimeZone.fromJson(zone)).toList(); timeZoneList = await ProfileApi.fetchTimeZone();
// List<StatusModel> statusModelList = []; emit(UpdateState(timeZoneList: timeZoneList!));
// for (var status in response['status']) {
// statusModelList.add(StatusModel.fromJson(status));
// }
emit(UpdateState(timeZoneList: timeZoneList!)); // Make sure UpdateState accepts the list
return timeZoneList; return timeZoneList;
} catch (e) { } catch (e) {
emit(FailedState(errorMessage: e.toString())); emit(FailedState(errorMessage: e.toString()));
@ -119,26 +85,25 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
} }
} }
Future selectTimeZone(SelectTimeZoneEvent event, Emitter<ProfileState> emit) async { Future selectTimeZone(SelectTimeZoneEvent event, Emitter<ProfileState> emit) async {
emit(LoadingInitialState());
try { try {
timeZoneSelected= event.val; emit(LoadingInitialState());
timeZoneSelected = event.val;
await ProfileApi.saveTimeZone(regionUuid: event.val);
CustomSnackBar.displaySnackBar('Save Successfully'); CustomSnackBar.displaySnackBar('Save Successfully');
Navigator.pop(event.context); emit(SaveState());
return timeZoneList;
} catch (e) { } catch (e) {
emit(FailedState(errorMessage: e.toString())); emit(FailedState(errorMessage: e.toString()));
return;
} }
} }
Future selectRegion(SelectRegionEvent event, Emitter<ProfileState> emit) async { Future selectRegion(SelectRegionEvent event, Emitter<ProfileState> emit) async {
emit(LoadingInitialState());
try { try {
regionSelected= event.val; emit(LoadingInitialState());
await ProfileApi.saveRegion(regionUuid:event.val );
CustomSnackBar.displaySnackBar('Save Successfully'); CustomSnackBar.displaySnackBar('Save Successfully');
Navigator.pop(event.context); emit(SaveState());
return timeZoneList;
} catch (e) { } catch (e) {
emit(FailedState(errorMessage: e.toString())); emit(FailedState(errorMessage: e.toString()));
return; return;
@ -146,26 +111,43 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
} }
Future searchRegion(SearchRegionEvent event, Emitter<ProfileState> emit) async { Future searchRegion(SearchRegionEvent event, Emitter<ProfileState> emit) async {
emit(LoadingInitialState());
final query = event.query.toLowerCase(); final query = event.query.toLowerCase();
if(event.query.isEmpty){ if(event.query.isNotEmpty){
regionList = region.map((zone) => RegionModel.fromJson(zone)).toList();
}else{
final filteredRegions = regionList?.where((region) { final filteredRegions = regionList?.where((region) {
return region.name.toLowerCase().contains(query); return region.name.toLowerCase().contains(query);
}).toList() ?? []; }).toList() ?? [];
regionList = filteredRegions;// Assume this fetches the regions regionList = filteredRegions;// Assume this fetches the regions
emit(RegionsLoadedState(regions: filteredRegions)); emit(RegionsLoadedState(regions: filteredRegions));
}else{
regionList = await ProfileApi.fetchRegion();
emit(RegionsLoadedState(regions: regionList!));
} }
} }
Future searchTimeZone(SearchTimeZoneEvent event, Emitter<ProfileState> emit) async {
emit(LoadingInitialState());
final query = event.query.toLowerCase();
if(event.query.isNotEmpty){
final filtered = timeZoneList?.where((region) {
return region.name.toLowerCase().contains(query);
}).toList() ?? [];
timeZoneList = filtered;
emit(TimeZoneLoadedState(regions: filtered));
}else{
timeZoneList = await ProfileApi.fetchTimeZone();
emit(UpdateState(timeZoneList: timeZoneList!));
}
}
void _fetchRegion(RegionInitialEvent event, Emitter<ProfileState> emit) async { void _fetchRegion(RegionInitialEvent event, Emitter<ProfileState> emit) async {
try { try {
emit(LoadingInitialState()); emit(LoadingInitialState());
regionList = region.map((zone) => RegionModel.fromJson(zone)).toList(); regionList = await ProfileApi.fetchRegion();
emit(UpdateState(timeZoneList: timeZoneList!)); emit(RegionsLoadedState(regions: regionList!));
} catch (e) { } catch (e) {
emit(FailedState(errorMessage: e.toString())); emit(FailedState(errorMessage: e.toString()));
return;
} }
} }
@ -182,7 +164,7 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
image=null; image=null;
CustomSnackBar.displaySnackBar('Image size must be 1 MB or less'); CustomSnackBar.displaySnackBar('Image size must be 1 MB or less');
}else{ }else{
await saveImage(); await _saveImage();
} }
} else { } else {
print('No image selected.'); print('No image selected.');
@ -193,54 +175,15 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
} }
} }
Future<void> saveImage() async { Future<void> _saveImage() async {
emit(ChangeImageState()); emit(ChangeImageState());
List<int> imageBytes = image!.readAsBytesSync(); List<int> imageBytes = image!.readAsBytesSync();
String base64Image = base64Encode(imageBytes); String base64Image = base64Encode(imageBytes);
print(base64Image); print(base64Image);
var response = await ProfileApi.saveImage(base64Image);
emit(ImageSelectedState()); emit(ImageSelectedState());
} }
Future<bool> _requestPermission() async {
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
if (Platform.isAndroid ) {
PermissionStatus status = await Permission.photos.status;
if(androidInfo.version.sdkInt<= 33){
if (status.isDenied) {
PermissionStatus status = await Permission.storage.request();
if (status.isGranted) {
return true;
} else {
return false;
}
}
}else{
if (status.isGranted) {
return true;
} else if (status.isDenied) {
PermissionStatus status = await Permission.photos.request();
if (status.isGranted) {
return true;
} else {
return false;
}
}
}
return false;
} else {
SharedPreferences sharedPreferences =
await SharedPreferences.getInstance();
bool firstClick = sharedPreferences.getBool('firstPermission') ?? true;
await sharedPreferences.setBool('firstPermission', false);
if (firstClick == false) {
var status = await Permission.photos.status;
return status.isGranted;
} else {
return true;
}
}
}
void _showPermissionDeniedDialog(BuildContext context) { void _showPermissionDeniedDialog(BuildContext context) {
showDialog( showDialog(
@ -272,129 +215,72 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
Future<void> saveName(SaveNameEvent event, Emitter<ProfileState> emit) async { Future<void> saveName(SaveNameEvent event, Emitter<ProfileState> emit) async {
if (_validateInputs()) return; if (_validateInputs()) return;
try { try {
add(const ChangeNameEvent(value: false));
isSaving = true; isSaving = true;
emit(LoadingSaveState()); emit(LoadingSaveState());
final fullName = nameController.text;
final nameParts = fullName.split(' ');
final firstName = nameParts[0];
final lastName = nameParts.length > 1 ? nameParts[1] : '';
var response = await ProfileApi.saveName(firstName: firstName, lastName: lastName);
add(InitialProfileEvent());
AuthCubit.get(event.context).fetchUserInfo();
Navigator.of(event.context).pop(true); Navigator.of(event.context).pop(true);
CustomSnackBar.displaySnackBar('Save Successfully'); CustomSnackBar.displaySnackBar('Save Successfully');
emit(SaveState()); emit(SaveState());
} catch (_) { } catch (_) {
// Handle the error
} finally { } finally {
isSaving = false; isSaving = false;
} }
} }
bool _validateInputs() { bool _validateInputs() {
if (nameController.text.length < 7) { if (nameController.text.length < 2) {
CustomSnackBar.displaySnackBar('Password less than 7'); CustomSnackBar.displaySnackBar('Name Must More than 2 ');
return true;
}
if (nameController.text.isEmpty) {
CustomSnackBar.displaySnackBar('Password required');
return true;
}
if (nameController.text.isEmpty) {
CustomSnackBar.displaySnackBar('Password name required');
return true; return true;
} }
return false; return false;
} }
void showRenameDialog(BuildContext context) { Future<bool> _requestPermission() async {
showDialog( DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
context: context, if (Platform.isAndroid ) {
builder: (BuildContext context) { AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
return Dialog( PermissionStatus status = await Permission.photos.status;
shape: RoundedRectangleBorder( if(androidInfo.version.sdkInt<= 33){
borderRadius: BorderRadius.circular(15), if (status.isDenied) {
), PermissionStatus status = await Permission.storage.request();
child: SizedBox( if (status.isGranted) {
height: 200, return true;
child: Column( } else {
mainAxisAlignment: MainAxisAlignment.center, return false;
children: [ }
const Padding( }
padding: EdgeInsets.only( }else{
top: 25, if (status.isGranted) {
), return true;
child: Text( } else if (status.isDenied) {
'Change Name', PermissionStatus status = await Permission.photos.request();
style: TextStyle( if (status.isGranted) {
fontSize: 20, return true;
fontWeight: FontWeight.bold, } else {
), return false;
), }
), }
const Spacer(), }
const Spacer(), return false;
Row( } else {
children: [ SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
Expanded( bool firstClick = sharedPreferences.getBool('firstPermission') ?? true;
child: Container( await sharedPreferences.setBool('firstPermission', false);
decoration: const BoxDecoration( if (firstClick == false) {
border: Border( var status = await Permission.photos.status;
right: BorderSide( return status.isGranted;
color: Colors.grey, } else {
width: 0.5, return true;
), }
top: BorderSide( }
color: Colors.grey,
width: 0.5,
),
),
),
child: TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text(
'Cancel',
style: TextStyle(
fontSize: 18,
color: Colors.black,
),
),
),
),
),
const VerticalDivider(
thickness: 1,
width: 1,
),
Expanded(
child: Container(
decoration: const BoxDecoration(
border: Border(
right: BorderSide(
color: Colors.grey,
width: 0.5,
),
top: BorderSide(
color: Colors.grey,
width: 0.5,
),
),
),
child: TextButton(
onPressed: () {
// Handle save action
},
child: const Text(
'Save',
style: TextStyle(
fontSize: 18,
color: Colors.grey,
),
),
),
),
),
],
),
],
),
),
);
},
);
} }
} }

View File

@ -8,7 +8,7 @@ abstract class ProfileEvent extends Equatable {
List<Object> get props => []; List<Object> get props => [];
} }
class InitialEvent extends ProfileEvent {} class InitialProfileEvent extends ProfileEvent {}
class TimeZoneInitialEvent extends ProfileEvent {} class TimeZoneInitialEvent extends ProfileEvent {}
@ -20,13 +20,6 @@ class ChangeNameEvent extends ProfileEvent {
class RegionInitialEvent extends ProfileEvent {} class RegionInitialEvent extends ProfileEvent {}
class UpdateLockEvent extends ProfileEvent {
final bool value;
const UpdateLockEvent({required this.value});
@override
List<Object> get props => [value];
}
class SaveNameEvent extends ProfileEvent { class SaveNameEvent extends ProfileEvent {
final BuildContext context; final BuildContext context;
const SaveNameEvent({required this.context}); const SaveNameEvent({required this.context});
@ -71,5 +64,13 @@ class SearchRegionEvent extends ProfileEvent {
List<Object> get props => [query]; List<Object> get props => [query];
} }
class SearchTimeZoneEvent extends ProfileEvent {
final String query;
const SearchTimeZoneEvent({required this.query});
@override
List<Object> get props => [query];
}

View File

@ -1,5 +1,4 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:syncrow_app/features/devices/model/smart_door_model.dart';
import 'package:syncrow_app/features/menu/bloc/profile_bloc/region_model.dart'; import 'package:syncrow_app/features/menu/bloc/profile_bloc/region_model.dart';
import 'package:syncrow_app/features/menu/bloc/profile_bloc/time_zone_model.dart'; import 'package:syncrow_app/features/menu/bloc/profile_bloc/time_zone_model.dart';
@ -26,13 +25,7 @@ class NameEditingState extends ProfileState {
NameEditingState({required this.editName}); NameEditingState({required this.editName});
} }
class LoadingNewSate extends ProfileState {
final SmartDoorModel smartDoorModel;
const LoadingNewSate({required this.smartDoorModel});
@override
List<Object> get props => [smartDoorModel];
}
class FailedState extends ProfileState { class FailedState extends ProfileState {
final String errorMessage; final String errorMessage;
@ -43,20 +36,9 @@ class FailedState extends ProfileState {
List<Object> get props => [errorMessage]; List<Object> get props => [errorMessage];
} }
class GeneratePasswordState extends ProfileState {}
class ImageSelectedState extends ProfileState {} class ImageSelectedState extends ProfileState {}
class ImageTooLargeState extends ProfileState {}
class IsRepeatState extends ProfileState {}
class IsStartEndState extends ProfileState {}
class ChangeStartTimeState extends ProfileState {}
class ChangeEndTimeState extends ProfileState {}
class ChangeImageState extends ProfileState {} class ChangeImageState extends ProfileState {}
class SaveState extends ProfileState {} class SaveState extends ProfileState {}
@ -66,4 +48,10 @@ class RegionsLoadedState extends ProfileState {
final List<RegionModel> regions; final List<RegionModel> regions;
const RegionsLoadedState({required this.regions}); const RegionsLoadedState({required this.regions});
}
class TimeZoneLoadedState extends ProfileState {
final List<TimeZone> regions;
const TimeZoneLoadedState({required this.regions});
} }

View File

@ -11,15 +11,15 @@ class RegionModel {
factory RegionModel.fromJson(Map<String, dynamic> json) { factory RegionModel.fromJson(Map<String, dynamic> json) {
return RegionModel( return RegionModel(
name: json['name'], name: json['regionName'],
id: json['id'].toString(), // Ensure id is a String id: json['uuid'].toString(), // Ensure id is a String
); );
} }
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return { return {
'name': name, 'regionName': name,
'id': id, 'uuid': id,
}; };
} }
} }

View File

@ -11,9 +11,9 @@ class TimeZone {
factory TimeZone.fromJson(Map<String, dynamic> json) { factory TimeZone.fromJson(Map<String, dynamic> json) {
return TimeZone( return TimeZone(
name: json['name'], name: json['cityName'],
offset: json['offset'], offset: json['timeZoneOffset'],
id: json['id'].toString(), // Ensure id is a String id: json['uuid'].toString(), // Ensure id is a String
); );
} }

View File

@ -25,7 +25,7 @@ class MenuView extends StatelessWidget {
physics: const BouncingScrollPhysics(), physics: const BouncingScrollPhysics(),
child: Column( child: Column(
children: [ children: [
ProfileTab(name:profileBloc.name), ProfileTab(),
for (var section in menuSections) for (var section in menuSections)
MenuList( MenuList(
section: section, section: section,

View File

@ -1,62 +1,98 @@
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart';
import 'package:syncrow_app/features/menu/view/widgets/profile/profile_view.dart'; import 'package:syncrow_app/features/menu/view/widgets/profile/profile_view.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart'; import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/syncrow_logo.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart';
class ProfileTab extends StatelessWidget { class ProfileTab extends StatelessWidget {
final String? name; final String? name;
const ProfileTab({super.key,this.name}); const ProfileTab({super.key, this.name});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Padding( return BlocBuilder<AuthCubit, AuthState>(
padding: const EdgeInsets.symmetric(vertical: 10), builder: (context, state) {
child: InkWell( final user = AuthCubit.user;
onTap: () { Uint8List? imageBytes;
Navigator.of(context).push( if (user!.profilePicture != null) {
MaterialPageRoute( final base64String = user.profilePicture!;
builder: (context) => const ProfileView(), final startIndex = base64String.indexOf('base64,') + 7;
), final pureBase64String = base64String.substring(startIndex);
); imageBytes = base64.decode(pureBase64String);
}, }
child: Stack( return Padding(
children: [ padding: const EdgeInsets.symmetric(vertical: 10),
Column( child: InkWell(
crossAxisAlignment: CrossAxisAlignment.stretch, onTap: () {
Navigator.of(context)
.push(
MaterialPageRoute(
builder: (context) => const ProfileView(),
),
)
.then((result) {
if (result == true) {
context.read<AuthCubit>().fetchUserInfo();
}
});
},
child: Stack(
children: [ children: [
const SizedBox(height: 20), Column(
DefaultContainer( crossAxisAlignment: CrossAxisAlignment.stretch,
child: Column( children: [
crossAxisAlignment: CrossAxisAlignment.start, const SizedBox(height: 20),
children: [ DefaultContainer(
BodyMedium( child: Column(
text: name!, crossAxisAlignment: CrossAxisAlignment.start,
fontWeight: FontWeight.bold, children: [
Row(
children: [
BodyMedium(
text: '${user.firstName!} ',
fontWeight: FontWeight.bold,
),
BodyMedium(
text: user.lastName!,
fontWeight: FontWeight.bold,
),
],
),
const BodySmall(text: "Syncrow Account"),
],
), ),
BodySmall(text: "Syncrow Account") ),
], ],
),
Positioned(
right: 20,
top: 0,
child: CircleAvatar(
radius: 38,
backgroundColor: Colors.white,
child: CircleAvatar(
radius: 37,
backgroundColor: Colors.grey,
child: ClipOval(
child: Image.memory(
imageBytes!,
fit: BoxFit.cover,
width: 110, // You can adjust the width and height as needed
height: 110,
)),
),
), ),
), ),
], ],
), ),
Positioned( ),
right: 20, );
top: 0, },
child: CircleAvatar(
radius: 38,
backgroundColor: Colors.white,
child: CircleAvatar(
radius: 37,
backgroundColor: Colors.grey,
child: SyncrowLogo(),
),
),
),
],
),
),
); );
} }
} }

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart';
import 'package:syncrow_app/features/menu/bloc/profile_bloc/profile_bloc.dart'; import 'package:syncrow_app/features/menu/bloc/profile_bloc/profile_bloc.dart';
import 'package:syncrow_app/features/menu/bloc/profile_bloc/profile_event.dart'; import 'package:syncrow_app/features/menu/bloc/profile_bloc/profile_event.dart';
import 'package:syncrow_app/features/menu/bloc/profile_bloc/profile_state.dart'; import 'package:syncrow_app/features/menu/bloc/profile_bloc/profile_state.dart';
@ -13,29 +14,29 @@ import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class ProfileView extends StatelessWidget { class ProfileView extends StatelessWidget {
const ProfileView({super.key}); const ProfileView({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (BuildContext context) => ProfileBloc(userId: ''), create: (BuildContext context) => ProfileBloc()..add(InitialProfileEvent()),
child: BlocConsumer<ProfileBloc, ProfileState>(listener: (context, state) { child: BlocConsumer<ProfileBloc, ProfileState>(
if (state is FailedState) { listener: (context, state) {},
ScaffoldMessenger.of(context).showSnackBar( builder: (context, state) {
SnackBar(
content: Text(state.errorMessage),
backgroundColor: Colors.red,
),
);
}
}, builder: (context, state) {
final profileBloc = BlocProvider.of<ProfileBloc>(context); final profileBloc = BlocProvider.of<ProfileBloc>(context);
Uint8List? imageBytes = profileBloc.decodeBase64Image(AuthCubit.user!.profilePicture);
return DefaultScaffold( return DefaultScaffold(
title: 'Syncrow Account', title: 'Syncrow Account',
child: Column( child: Column(
children: [ children: [
SizedBox(height: MediaQuery.of(context).size.height*0.05,), SizedBox(
height: MediaQuery.of(context).size.height * 0.05,
),
InkWell( InkWell(
onTap: () { onTap: () {
profileBloc.add(SelectImageEvent(context: context, isSelected: false)); profileBloc.add(
SelectImageEvent(context: context, isSelected: false));
}, },
child: SizedBox.square( child: SizedBox.square(
dimension: 125, dimension: 125,
@ -48,6 +49,18 @@ class ProfileView extends StatelessWidget {
backgroundImage: profileBloc.image == null backgroundImage: profileBloc.image == null
? null ? null
: FileImage(profileBloc.image!), : FileImage(profileBloc.image!),
child: profileBloc.image != null
? null
:imageBytes != null
? ClipOval(
child: Image.memory(
imageBytes,
fit: BoxFit.cover,
width: 110,
height: 110,
),
)
: null,
), ),
), ),
), ),
@ -70,7 +83,7 @@ class ProfileView extends StatelessWidget {
controller: profileBloc.nameController, controller: profileBloc.nameController,
enabled: profileBloc.editName, enabled: profileBloc.editName,
onEditingComplete: () { onEditingComplete: () {
profileBloc.add(const ChangeNameEvent(value: false)); profileBloc.add(SaveNameEvent(context: context));
}, },
decoration: const InputDecoration( decoration: const InputDecoration(
hintText: "Your Name", hintText: "Your Name",
@ -98,89 +111,80 @@ class ProfileView extends StatelessWidget {
), ),
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
//Info // Info
DefaultContainer( DefaultContainer(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 25, horizontal: 25,
vertical: 5, vertical: 5,
), ),
child: Column( child:
mainAxisSize: MainAxisSize.min, BlocBuilder<AuthCubit, AuthState>(
mainAxisAlignment: MainAxisAlignment.start, builder: (context, state) {
children: [ final user = AuthCubit.user;
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween, return Column(
// children: [ mainAxisSize: MainAxisSize.min,
// const BodyMedium(text: 'Email '), mainAxisAlignment: MainAxisAlignment.start,
// Flexible( children: [
// child: TextField( InkWell(
// textAlign: TextAlign.end, onTap: () {
// decoration: InputDecoration( Navigator.push(
// hintText: ' Test@test.com', context,
// hintStyle: MaterialPageRoute(
// context.bodyMedium.copyWith(color: Colors.grey), builder: (context) => const RegionPage(),
// border: InputBorder.none,
// ),
// ),
// ),
// ],
// ),
// Container(
// height: 1,
// color: ColorsManager.greyColor,
// ),
InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const RegionPage(),
));
},
child: const Padding(
padding: EdgeInsets.only(top: 20, bottom: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
BodyMedium(text: 'Region '),
Flexible(
child: BodyMedium(text: 'United Arab Emirates')
),
],
),
)),
Container(
height: 1,
color: ColorsManager.greyColor,
),
InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const TimeZoneScreenPage(),
));
},
child: const Padding(
padding: EdgeInsets.only(top: 15, bottom: 15),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
BodyMedium(text: 'Time Zone '),
Flexible(
child: BodyMedium(text: 'GMT +4')
),
],
), ),
).then((result) {
context.read<AuthCubit>().fetchUserInfo();
});
},
child: Padding(
padding: const EdgeInsets.only(top: 20, bottom: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const BodyMedium(text: 'Region '),
Flexible(child: BodyMedium(text: user!.regionName??'No Region')),
],
), ),
), ),
], ),
)), Container(
height: 1,
color: ColorsManager.greyColor,
),
InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const TimeZoneScreenPage(),
),
).then((result) {
context.read<AuthCubit>().fetchUserInfo();
});
},
child: Padding(
padding: const EdgeInsets.only(top: 15, bottom: 15),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const BodyMedium(text: 'Time Zone '),
Flexible(
child: BodyMedium(text: user.timeZone??"No Time Zone"),
),
],
),
),
),
],
);}),
),
], ],
), ),
); );
})); },
),
);
} }
} }

View File

@ -13,7 +13,7 @@ class RegionPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (BuildContext context) => ProfileBloc(userId: '')..add(RegionInitialEvent()), create: (BuildContext context) => ProfileBloc()..add(RegionInitialEvent()),
child: BlocConsumer<ProfileBloc, ProfileState>(listener: (context, state) { child: BlocConsumer<ProfileBloc, ProfileState>(listener: (context, state) {
if (state is FailedState) { if (state is FailedState) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(

View File

@ -15,7 +15,7 @@ class TimeZoneScreenPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (BuildContext context) => ProfileBloc(userId: '')..add(TimeZoneInitialEvent()), create: (BuildContext context) => ProfileBloc()..add(TimeZoneInitialEvent()),
child: child:
BlocConsumer<ProfileBloc, ProfileState>(listener: (context, state) { BlocConsumer<ProfileBloc, ProfileState>(listener: (context, state) {
if (state is FailedState) { if (state is FailedState) {
@ -38,6 +38,10 @@ class TimeZoneScreenPage extends StatelessWidget {
: Column( : Column(
children: [ children: [
TextFormField( TextFormField(
controller:profileBloc.searchController ,
onChanged: (value) {
profileBloc.add(SearchTimeZoneEvent(query: value));
},
decoration: const InputDecoration( decoration: const InputDecoration(
prefixIcon: Icon(Icons.search), prefixIcon: Icon(Icons.search),
hintText: 'Search', hintText: 'Search',
@ -59,7 +63,6 @@ class TimeZoneScreenPage extends StatelessWidget {
itemBuilder: (context, index) { itemBuilder: (context, index) {
return InkWell( return InkWell(
onTap: () { onTap: () {
profileBloc.add(SelectTimeZoneEvent( profileBloc.add(SelectTimeZoneEvent(
val: timeZoneList[index].id, val: timeZoneList[index].id,
context: context)); context: context));

View File

@ -18,6 +18,7 @@ class _SplashViewState extends State<SplashView> {
@override @override
void initState() { void initState() {
AuthCubit.get(context).getTokenAndValidate(); AuthCubit.get(context).getTokenAndValidate();
AuthCubit.get(context).fetchUserInfo();
super.initState(); super.initState();
} }

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart'; import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart';
import 'package:syncrow_app/features/menu/bloc/profile_bloc/profile_bloc.dart';
import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart'; import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart';
import 'package:syncrow_app/navigation/navigation_service.dart'; import 'package:syncrow_app/navigation/navigation_service.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
@ -26,6 +27,7 @@ class MyApp extends StatelessWidget {
BlocProvider(create: (context) => AuthCubit()), BlocProvider(create: (context) => AuthCubit()),
BlocProvider(create: (context) => CreateSceneBloc()), BlocProvider(create: (context) => CreateSceneBloc()),
BlocProvider(create: (context) => SceneBloc()), BlocProvider(create: (context) => SceneBloc()),
BlocProvider(create: (context) => ProfileBloc()),
], ],
child: MaterialApp( child: MaterialApp(

View File

@ -1,5 +1,5 @@
abstract class ApiEndpoints { abstract class ApiEndpoints {
static const String baseUrl = 'https://syncrow.azurewebsites.net'; static const String baseUrl = 'https://syncrow-dev.azurewebsites.net';
// static const String baseUrl = 'http://100.107.182.63:4001'; //Localhost // static const String baseUrl = 'http://100.107.182.63:4001'; //Localhost
////////////////////////////////////// Authentication /////////////////////////////// ////////////////////////////////////// Authentication ///////////////////////////////
@ -151,4 +151,15 @@ abstract class ApiEndpoints {
//multiple-time offline //multiple-time offline
static const String deleteTemporaryPassword = static const String deleteTemporaryPassword =
'$baseUrl/door-lock/temporary-password/{doorLockUuid}/{passwordId}'; '$baseUrl/door-lock/temporary-password/{doorLockUuid}/{passwordId}';
//user
static const String getUser = '$baseUrl/user/{userUuid}';
static const String saveRegion = '$baseUrl/user/region/{userUuid}';
static const String saveTimeZone = '$baseUrl/user/timezone/{userUuid}';
static const String saveName = '$baseUrl/user/name/{userUuid}';
static const String sendPicture = '$baseUrl/user/profile-picture/{userUuid}';
static const String getRegion = '$baseUrl/region';
static const String getTimezone = '$baseUrl/timezone';
} }

View File

@ -1,23 +1,22 @@
import 'dart:async'; import 'dart:async';
import 'dart:io'; import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart';
import 'package:syncrow_app/features/devices/model/device_category_model.dart'; import 'package:syncrow_app/features/auth/model/user_model.dart';
import 'package:syncrow_app/features/devices/model/device_control_model.dart'; import 'package:syncrow_app/features/menu/bloc/profile_bloc/region_model.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/menu/bloc/profile_bloc/time_zone_model.dart';
import 'package:syncrow_app/features/devices/model/function_model.dart';
import 'package:syncrow_app/services/api/api_links_endpoints.dart'; import 'package:syncrow_app/services/api/api_links_endpoints.dart';
import 'package:syncrow_app/services/api/http_service.dart'; import 'package:syncrow_app/services/api/http_service.dart';
import '../../features/devices/model/create_temporary_password_model.dart';
class ProfileApi { class ProfileApi {
static final HTTPService _httpService = HTTPService(); static final HTTPService _httpService = HTTPService();
static Future<Map<String, dynamic>> saveName(
String newName, String userId) async { static Future<Map<String, dynamic>> saveName({String? firstName, String? lastName,}) async {
try { try {
final response = await _httpService.post( final response = await _httpService.put(
path: ApiEndpoints.controlDevice.replaceAll('{deviceUuid}', userId), path: ApiEndpoints.saveName.replaceAll('{userUuid}', AuthCubit.user!.uuid!),
body: newName, body: {
showServerMessage: false, "firstName": firstName,
"lastName": lastName
},
expectedResponseModel: (json) { expectedResponseModel: (json) {
return json; return json;
}, },
@ -28,13 +27,29 @@ class ProfileApi {
} }
} }
static Future<Map<String, dynamic>> saveImage( static Future saveRegion({String? regionUuid,}) async {
File newName, String userId) async {
try { try {
final response = await _httpService.post( final response = await _httpService.put(
path: ApiEndpoints.controlDevice.replaceAll('{deviceUuid}', userId), path: ApiEndpoints.saveRegion.replaceAll('{userUuid}', AuthCubit.user!.uuid!),
body: newName, body: {
showServerMessage: false, "regionUuid": regionUuid,
},
expectedResponseModel: (json) {
return json;
},
);
return response;
} catch (e) {
rethrow;
}
}
static Future saveTimeZone({String? regionUuid,}) async {
try {
final response = await _httpService.put(
path: ApiEndpoints.saveTimeZone.replaceAll('{userUuid}', AuthCubit.user!.uuid!),
body: {
"timezoneUuid": regionUuid,
},
expectedResponseModel: (json) { expectedResponseModel: (json) {
return json; return json;
}, },
@ -45,15 +60,54 @@ class ProfileApi {
} }
} }
static Future fetchUserInfo(String userId) async { static Future<Map<String, dynamic>> saveImage(String image) async {
try {
final response = await _httpService.put(
path: ApiEndpoints.sendPicture.replaceAll('{userUuid}', AuthCubit.user!.uuid!),
body: {
"profilePicture": 'data:image/png;base64,$image'
},
expectedResponseModel: (json) {
return json;
},
);
return response;
} catch (e) {
rethrow;
}
}
static Future fetchUserInfo(userId) async {
final response = await _httpService.get( final response = await _httpService.get(
path: ApiEndpoints.groupBySpace.replaceAll('{unitUuid}', userId), path: ApiEndpoints.getUser.replaceAll('{userUuid}', userId!),
// queryParameters: params, showServerMessage: true,
showServerMessage: false, expectedResponseModel: (json) =>UserModel.fromJson(json)
expectedResponseModel: (json) => DevicesCategoryModel.fromJsonList(json),
); );
return response; return response;
} }
static Future<List<RegionModel>> fetchRegion() async {
final response = await _httpService.get(
path: ApiEndpoints.getRegion,
showServerMessage: true,
expectedResponseModel: (json) {
return (json as List).map((zone) => RegionModel.fromJson(zone)).toList();
}
);
return response as List<RegionModel>;
}
static Future<List<TimeZone>> fetchTimeZone() async {
final response = await _httpService.get(
path: ApiEndpoints.getTimezone,
showServerMessage: true,
expectedResponseModel: (json) {
return (json as List).map((zone) => TimeZone.fromJson(zone)).toList();
}
);
return response as List<TimeZone>;
}
} }