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
-81
View File
@@ -9,85 +9,4 @@ class MenuCubit extends Cubit<MenuState> {
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',
// ),
// ],
// ),
// ];
}
@@ -1,19 +1,21 @@
import 'dart:convert';
import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.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_state.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/services/api/profile_api.dart';
import 'package:syncrow_app/utils/helpers/snack_bar.dart';
import 'package:image_picker/image_picker.dart';
import 'package:permission_handler/permission_handler.dart';
class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
final String userId;
bool isSaving = false;
bool editName = false;
final FocusNode focusNode = FocusNode();
@@ -22,12 +24,13 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
String timeZoneSelected = '';
String regionSelected = '';
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<RegionModel>? regionList;
ProfileBloc({required this.userId}) : super(InitialState()) {
on<InitialEvent>(_fetchUserInfo);
ProfileBloc() : super(InitialState()) {
on<InitialProfileEvent>(_fetchUserInfo);
on<TimeZoneInitialEvent>(_fetchTimeZone);
on<RegionInitialEvent>(_fetchRegion);
on<SaveNameEvent>(saveName);
@@ -35,46 +38,18 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
on<ChangeNameEvent>(_changeName);
on<SelectTimeZoneEvent>(selectTimeZone);
on<SearchRegionEvent>(searchRegion);
on<SearchTimeZoneEvent>(searchTimeZone);
on<SelectRegionEvent>(selectRegion);
}
List<Map<String, String>> timeZone=
[
{"name": "GMT-12:00", "offset": "-12:00","id":'1'},
{"name": "GMT-11:00", "offset": "-11:00","id":'2'},
{"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'},
];
Uint8List? decodeBase64Image(String? base64String) {
if (base64String != null) {
final startIndex = base64String.indexOf('base64,') + 7;
final pureBase64String = base64String.substring(startIndex);
return base64.decode(pureBase64String);
}
return null;
}
void _changeName(ChangeNameEvent event, Emitter<ProfileState> emit) {
emit(LoadingInitialState());
editName = event.value!;
@@ -88,30 +63,21 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
emit(NameEditingState(editName: editName));
}
void _fetchUserInfo(InitialEvent event, Emitter<ProfileState> emit) async {
void _fetchUserInfo(InitialProfileEvent event, Emitter<ProfileState> emit) async {
try {
emit(LoadingInitialState());
// var response = await ProfileApi.fetchUserInfo(userId);
// List<StatusModel> statusModelList = [];
// for (var status in response['status']) {
// statusModelList.add(StatusModel.fromJson(status));
// }
AuthCubit.user = await ProfileApi.fetchUserInfo(AuthCubit.user!.uuid);
} catch (e) {
emit(FailedState(errorMessage: e.toString()));
return;
}
}
Future _fetchTimeZone(
TimeZoneInitialEvent event, Emitter<ProfileState> emit) async {
Future _fetchTimeZone(TimeZoneInitialEvent event, Emitter<ProfileState> emit) async {
emit(LoadingInitialState());
try {
timeZoneList = timeZone.map((zone) => TimeZone.fromJson(zone)).toList();
// List<StatusModel> statusModelList = [];
// for (var status in response['status']) {
// statusModelList.add(StatusModel.fromJson(status));
// }
emit(UpdateState(timeZoneList: timeZoneList!)); // Make sure UpdateState accepts the list
timeZoneList = await ProfileApi.fetchTimeZone();
emit(UpdateState(timeZoneList: timeZoneList!));
return timeZoneList;
} catch (e) {
emit(FailedState(errorMessage: e.toString()));
@@ -119,26 +85,25 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
}
}
Future selectTimeZone(SelectTimeZoneEvent event, Emitter<ProfileState> emit) async {
emit(LoadingInitialState());
try {
timeZoneSelected= event.val;
emit(LoadingInitialState());
timeZoneSelected = event.val;
await ProfileApi.saveTimeZone(regionUuid: event.val);
CustomSnackBar.displaySnackBar('Save Successfully');
Navigator.pop(event.context);
return timeZoneList;
emit(SaveState());
} catch (e) {
emit(FailedState(errorMessage: e.toString()));
return;
}
}
Future selectRegion(SelectRegionEvent event, Emitter<ProfileState> emit) async {
emit(LoadingInitialState());
try {
regionSelected= event.val;
emit(LoadingInitialState());
await ProfileApi.saveRegion(regionUuid:event.val );
CustomSnackBar.displaySnackBar('Save Successfully');
Navigator.pop(event.context);
return timeZoneList;
emit(SaveState());
} catch (e) {
emit(FailedState(errorMessage: e.toString()));
return;
@@ -146,26 +111,43 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
}
Future searchRegion(SearchRegionEvent event, Emitter<ProfileState> emit) async {
emit(LoadingInitialState());
final query = event.query.toLowerCase();
if(event.query.isEmpty){
regionList = region.map((zone) => RegionModel.fromJson(zone)).toList();
}else{
if(event.query.isNotEmpty){
final filteredRegions = regionList?.where((region) {
return region.name.toLowerCase().contains(query);
}).toList() ?? [];
regionList = filteredRegions;// Assume this fetches the regions
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 {
try {
emit(LoadingInitialState());
regionList = region.map((zone) => RegionModel.fromJson(zone)).toList();
emit(UpdateState(timeZoneList: timeZoneList!));
regionList = await ProfileApi.fetchRegion();
emit(RegionsLoadedState(regions: regionList!));
} catch (e) {
emit(FailedState(errorMessage: e.toString()));
return;
}
}
@@ -182,7 +164,7 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
image=null;
CustomSnackBar.displaySnackBar('Image size must be 1 MB or less');
}else{
await saveImage();
await _saveImage();
}
} else {
print('No image selected.');
@@ -193,54 +175,15 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
}
}
Future<void> saveImage() async {
Future<void> _saveImage() async {
emit(ChangeImageState());
List<int> imageBytes = image!.readAsBytesSync();
String base64Image = base64Encode(imageBytes);
print(base64Image);
var response = await ProfileApi.saveImage(base64Image);
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) {
showDialog(
@@ -272,129 +215,72 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
Future<void> saveName(SaveNameEvent event, Emitter<ProfileState> emit) async {
if (_validateInputs()) return;
try {
add(const ChangeNameEvent(value: false));
isSaving = true;
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);
CustomSnackBar.displaySnackBar('Save Successfully');
emit(SaveState());
} catch (_) {
// Handle the error
} finally {
isSaving = false;
}
}
bool _validateInputs() {
if (nameController.text.length < 7) {
CustomSnackBar.displaySnackBar('Password less than 7');
return true;
}
if (nameController.text.isEmpty) {
CustomSnackBar.displaySnackBar('Password required');
return true;
}
if (nameController.text.isEmpty) {
CustomSnackBar.displaySnackBar('Password name required');
if (nameController.text.length < 2) {
CustomSnackBar.displaySnackBar('Name Must More than 2 ');
return true;
}
return false;
}
void showRenameDialog(BuildContext context) {
showDialog(
context: context,
builder: (BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
child: SizedBox(
height: 200,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Padding(
padding: EdgeInsets.only(
top: 25,
),
child: Text(
'Change Name',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
const Spacer(),
const Spacer(),
Row(
children: [
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: () {
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,
),
),
),
),
),
],
),
],
),
),
);
},
);
Future<bool> _requestPermission() async {
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
if (Platform.isAndroid ) {
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
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;
}
}
}
}
@@ -8,7 +8,7 @@ abstract class ProfileEvent extends Equatable {
List<Object> get props => [];
}
class InitialEvent extends ProfileEvent {}
class InitialProfileEvent extends ProfileEvent {}
class TimeZoneInitialEvent extends ProfileEvent {}
@@ -20,13 +20,6 @@ class ChangeNameEvent 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 {
final BuildContext context;
const SaveNameEvent({required this.context});
@@ -71,5 +64,13 @@ class SearchRegionEvent extends ProfileEvent {
List<Object> get props => [query];
}
class SearchTimeZoneEvent extends ProfileEvent {
final String query;
const SearchTimeZoneEvent({required this.query});
@override
List<Object> get props => [query];
}
@@ -1,5 +1,4 @@
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/time_zone_model.dart';
@@ -26,13 +25,7 @@ class NameEditingState extends ProfileState {
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 {
final String errorMessage;
@@ -43,20 +36,9 @@ class FailedState extends ProfileState {
List<Object> get props => [errorMessage];
}
class GeneratePasswordState 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 SaveState extends ProfileState {}
@@ -66,4 +48,10 @@ class RegionsLoadedState extends ProfileState {
final List<RegionModel> regions;
const RegionsLoadedState({required this.regions});
}
class TimeZoneLoadedState extends ProfileState {
final List<TimeZone> regions;
const TimeZoneLoadedState({required this.regions});
}
@@ -11,15 +11,15 @@ class RegionModel {
factory RegionModel.fromJson(Map<String, dynamic> json) {
return RegionModel(
name: json['name'],
id: json['id'].toString(), // Ensure id is a String
name: json['regionName'],
id: json['uuid'].toString(), // Ensure id is a String
);
}
Map<String, dynamic> toJson() {
return {
'name': name,
'id': id,
'regionName': name,
'uuid': id,
};
}
}
@@ -11,9 +11,9 @@ class TimeZone {
factory TimeZone.fromJson(Map<String, dynamic> json) {
return TimeZone(
name: json['name'],
offset: json['offset'],
id: json['id'].toString(), // Ensure id is a String
name: json['cityName'],
offset: json['timeZoneOffset'],
id: json['uuid'].toString(), // Ensure id is a String
);
}