mirror of
https://github.com/SyncrowIOT/syncrow-app.git
synced 2025-08-25 22:39:39 +00:00
Compare commits
40 Commits
fix-flush-
...
4ac6077011
Author | SHA1 | Date | |
---|---|---|---|
4ac6077011 | |||
d5321a9ca0 | |||
09dc8cc330 | |||
f7245e5de9 | |||
fe472f0ca0 | |||
d9b68a11e5 | |||
6c91af6f90 | |||
65b8ecf672 | |||
ea0bd32eb7 | |||
069a8e16db | |||
5989cdcfa0 | |||
4c81d22605 | |||
dc123e6231 | |||
a705384717 | |||
d644c9c949 | |||
9d7113cee8 | |||
3893740080 | |||
c7a9cd7ea7 | |||
24a12af0f6 | |||
5d327e29c7 | |||
2b58af4560 | |||
19f1bb8ec3 | |||
a4f56effbb | |||
b35447384c | |||
dbf031287b | |||
293d6a1d98 | |||
eaecd4996e | |||
bfb0cb1dc1 | |||
c2bf32af1c | |||
b7a42af223 | |||
e8e5ddd102 | |||
fca69cef73 | |||
7a19291088 | |||
c76995fbb8 | |||
5d1b8e39b0 | |||
a04beb32f2 | |||
23c307338a | |||
4db16fd567 | |||
2c5ca67b10 | |||
79c1205932 |
12
assets/icons/Booking.svg
Normal file
12
assets/icons/Booking.svg
Normal file
@ -0,0 +1,12 @@
|
||||
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_9795_16810)">
|
||||
<path d="M3.68007 14.1344H4.98041C5.73422 14.1344 5.73533 12.96 4.98041 12.96H3.68007C2.92629 12.96 2.92518 14.1344 3.68007 14.1344ZM3.68007 13.3994H4.98041C5.17143 13.3994 5.17143 13.695 4.98041 13.695H3.68007C3.48908 13.695 3.48908 13.3994 3.68007 13.3994Z" fill="white"/>
|
||||
<path d="M5.29431 7.55226L3.86289 8.9837L3.36716 8.48794C3.16621 8.28714 2.85552 8.59777 3.05641 8.79869L3.70753 9.44981C3.81112 9.53559 3.91472 9.53559 4.01828 9.44981L5.60509 7.86301C5.80595 7.66209 5.49525 7.35145 5.29431 7.55226Z" fill="white"/>
|
||||
<path d="M14.8368 9.72059C14.7481 9.55125 14.6838 9.36574 14.6401 9.17956C14.4259 8.28325 14.1003 7.41489 13.6727 6.59856L12.9576 5.23374C12.5529 4.46106 11.3583 4.58953 11.1328 5.437C10.5978 5.24455 9.99396 5.56087 9.84768 6.11024C9.40544 5.9511 8.89477 6.13855 8.6603 6.54527V1.7204V0.858486C8.66028 0.385137 8.27517 0 7.80176 0H2.68301C2.39892 0 2.39892 0.439453 2.68301 0.439453H7.80176C8.03282 0.439453 8.22082 0.627422 8.22082 0.858486V1.50067H0.439659V0.858486C0.439659 0.627422 0.627628 0.439453 0.858692 0.439453H1.45257C1.73666 0.439453 1.73666 0 1.45257 0H0.858692C0.385343 0 0.000205994 0.385137 0.000205994 0.858486V1.7204V12.3108V14.1415C0.000205994 14.6149 0.385343 15 0.858692 15H7.80176C8.27514 15 8.66027 14.6149 8.66027 14.1415V12.3108V11.4307L9.44546 11.819C9.63949 11.915 9.79896 12.0748 9.89452 12.269C10.2433 12.9779 11.1374 13.2717 11.8396 12.9038L13.1487 12.218C13.4004 12.0861 13.1965 11.6969 12.9448 11.8287L11.6357 12.5145C11.1494 12.7694 10.5312 12.5676 10.2888 12.075C10.1508 11.7945 9.92051 11.5637 9.64028 11.4251L6.30826 9.77698C5.68245 9.47303 6.13503 8.49126 6.7706 8.78769L7.98044 9.42457C8.16709 9.52283 8.3753 9.31506 8.27742 9.1282L6.17569 5.11652C5.83058 4.46771 6.75481 3.86883 7.10733 4.54184L8.59292 7.37742L8.98096 8.1181C9.10904 8.36271 9.50171 8.16518 9.37023 7.91417L8.99244 7.19309C8.83945 6.68329 9.46884 6.27226 9.87393 6.62766L10.2943 7.43004C10.4224 7.67464 10.8151 7.47712 10.6836 7.2261L10.2561 6.41024C10.2161 5.94152 10.7901 5.63988 11.153 5.94905L11.5761 6.75671C11.7042 7.00131 12.0968 6.80379 11.9653 6.55277L11.5421 5.745C11.4846 5.16976 12.2942 4.91437 12.5683 5.43765L13.2834 6.80247C13.6942 7.5866 14.0068 8.42074 14.2127 9.28163C14.2627 9.50496 14.3413 9.7217 14.4475 9.92446C14.7007 10.4077 14.5135 11.0068 14.0303 11.26C13.7786 11.3919 13.9826 11.7811 14.2342 11.6492C14.9321 11.2836 15.2024 10.4184 14.8368 9.72059ZM8.22082 14.1415C8.22082 14.3726 8.03282 14.5605 7.80176 14.5605H0.858692C0.627628 14.5605 0.439659 14.3726 0.439659 14.1415V12.5305H8.22082V14.1415ZM5.64987 8.85926C5.50072 9.17279 5.5297 9.54832 5.72405 9.83587C5.36068 10.2161 4.86217 10.4316 4.33022 10.4316C3.26581 10.4316 2.39985 9.56563 2.39985 8.50122C2.39985 7.43681 3.26581 6.57085 4.33022 6.57085C5.34018 6.57085 6.17092 7.35062 6.2533 8.33965C5.99306 8.41904 5.76686 8.61328 5.64987 8.85926ZM7.24526 4.0302C7.14946 3.95241 7.03893 3.89259 6.91758 3.85468C6.21375 3.63475 5.50491 4.28016 5.65983 5.00092H1.48843C1.44803 5.00092 1.41519 4.96808 1.41519 4.92768V3.35742C1.41519 3.31702 1.44803 3.28418 1.48843 3.28418H7.17202C7.21242 3.28418 7.24526 3.31702 7.24526 3.35742V4.0302ZM8.22082 5.72027L7.68472 4.69702V3.35742C7.68472 3.07474 7.45471 2.84473 7.17202 2.84473H1.48843C1.20574 2.84473 0.975733 3.07474 0.975733 3.35742V4.92771C0.975733 5.21039 1.20574 5.4404 1.48843 5.4404H5.84921L7.56065 8.70703L6.97102 8.39663C6.96841 8.39525 6.96574 8.39391 6.96308 8.39265C6.87577 8.3511 6.78445 8.3231 6.69155 8.30851C6.59306 7.09166 5.572 6.1314 4.33025 6.1314C3.02353 6.1314 1.96043 7.19449 1.96043 8.50122C1.96043 9.80795 3.02353 10.871 4.33025 10.871C4.98563 10.871 5.60016 10.6053 6.04658 10.1354C6.74367 10.5371 7.49956 10.8565 8.22082 11.2133V12.091H0.439659V1.94013H8.22082V5.72027Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_9795_16810">
|
||||
<rect width="15" height="15" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 3.9 KiB |
@ -26,6 +26,7 @@
|
||||
buildConfiguration = "Debug-prod"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
@ -54,6 +55,7 @@
|
||||
buildConfiguration = "Debug-prod"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
|
@ -479,7 +479,7 @@ class HomeCubit extends Cubit<HomeState> {
|
||||
// onPressed: () {},
|
||||
// ),
|
||||
],
|
||||
'Routine': [
|
||||
'Automation': [
|
||||
// IconButton(
|
||||
// icon: Image.asset(
|
||||
// Assets.assetsIconsFilter,
|
||||
@ -553,7 +553,7 @@ class HomeCubit extends Cubit<HomeState> {
|
||||
static Map<String, Widget?> appBarLeading = {
|
||||
// 'Dashboard': const AppBarHomeDropdown(),
|
||||
'Devices': const AppBarHomeDropdown(),
|
||||
'Routine': const AppBarHomeDropdown(),
|
||||
'Automation': const AppBarHomeDropdown(),
|
||||
'Menu': Padding(
|
||||
padding: const EdgeInsets.only(left: 15),
|
||||
child: Image.asset(
|
||||
@ -569,7 +569,8 @@ class HomeCubit extends Cubit<HomeState> {
|
||||
// defaultBottomNavBarItem(icon: Assets.assetsIconsDashboard, label: 'Dashboard'),
|
||||
// defaultBottomNavBarItem(icon: Assets.assetsIconslayout, label: 'Layout'),
|
||||
defaultBottomNavBarItem(icon: Assets.assetsIconsDevices, label: 'Devices'),
|
||||
defaultBottomNavBarItem(icon: Assets.assetsIconsRoutines, label: 'Routine'),
|
||||
defaultBottomNavBarItem(
|
||||
icon: Assets.assetsIconsRoutines, label: 'Automation'),
|
||||
defaultBottomNavBarItem(icon: Assets.assetsIconsMenu, label: 'Menu'),
|
||||
];
|
||||
|
||||
@ -616,7 +617,6 @@ BottomNavigationBarItem defaultBottomNavBarItem(
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// class PermissionUtils {
|
||||
// // Check if the "VIEW" permission exists in "MANAGE_SUBSPACE"
|
||||
// static bool hasViewPermission(List<dynamic> permissions) {
|
||||
|
@ -270,7 +270,7 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
|
||||
Future<bool> reSendOtp({bool? forget}) async {
|
||||
try {
|
||||
emit(AuthLoading());
|
||||
emit(ResendOtpLoading());
|
||||
await AuthenticationAPI.sendOtp(body: {
|
||||
'email': email,
|
||||
'type': forget == true ? 'PASSWORD' : 'VERIFICATION'
|
||||
@ -286,7 +286,10 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
}
|
||||
|
||||
verifyOtp(bool isForgotPass) async {
|
||||
emit(AuthLoginLoading());
|
||||
if (otpCode.length != 6) {
|
||||
return;
|
||||
}
|
||||
emit(AuthOtpLoading());
|
||||
try {
|
||||
final response = await AuthenticationAPI.verifyPassCode(body: {
|
||||
'email': email,
|
||||
|
@ -20,9 +20,13 @@ class AuthLoginLoading extends AuthLoading {}
|
||||
|
||||
class AuthLoginSuccess extends AuthSuccess {}
|
||||
|
||||
class AuthSignUpSuccess extends AuthSuccess {}
|
||||
|
||||
class AuthOtpSuccess extends AuthSuccess {}
|
||||
|
||||
class AuthSignUpSuccess extends AuthSuccess {}
|
||||
class AuthOtpLoading extends AuthLoading {}
|
||||
|
||||
class ResendOtpLoading extends AuthLoading {}
|
||||
|
||||
class ResendOtpSuccess extends AuthSuccess {}
|
||||
|
||||
|
@ -22,7 +22,7 @@ class UserModel {
|
||||
final DateTime? appAgreementAcceptedAt;
|
||||
final Role? role;
|
||||
final Project? project;
|
||||
|
||||
final int? points;
|
||||
UserModel({
|
||||
required this.uuid,
|
||||
required this.email,
|
||||
@ -41,6 +41,7 @@ class UserModel {
|
||||
required this.appAgreementAcceptedAt,
|
||||
required this.role,
|
||||
required this.project,
|
||||
this.points,
|
||||
});
|
||||
|
||||
factory UserModel.fromJson(Map<String, dynamic> json) {
|
||||
@ -67,33 +68,36 @@ class UserModel {
|
||||
role: json['role'] != null ? Role.fromJson(json['role']) : null,
|
||||
project:
|
||||
json['project'] != null ? Project.fromJson(json['project']) : null,
|
||||
points: json['points'],
|
||||
);
|
||||
}
|
||||
|
||||
factory UserModel.fromToken(Token token) {
|
||||
Map<String, dynamic> tempJson = Token.decodeToken(token.accessToken);
|
||||
return UserModel(
|
||||
uuid: tempJson['uuid'].toString(),
|
||||
email: tempJson['email'],
|
||||
lastName: tempJson['lastName'],
|
||||
firstName: tempJson['firstName'],
|
||||
profilePicture: UserModel.decodeBase64Image(tempJson['profilePicture']),
|
||||
phoneNumber: null,
|
||||
isEmailVerified: null,
|
||||
isAgreementAccepted: null,
|
||||
regionUuid: null,
|
||||
regionName: tempJson['region']?['regionName'],
|
||||
timeZone: tempJson['timezone']?['timeZoneOffset'],
|
||||
hasAcceptedWebAgreement: tempJson['hasAcceptedWebAgreement'],
|
||||
webAgreementAcceptedAt: tempJson['webAgreementAcceptedAt'] != null
|
||||
? DateTime.parse(tempJson['webAgreementAcceptedAt'])
|
||||
: null,
|
||||
hasAcceptedAppAgreement: tempJson['hasAcceptedAppAgreement'],
|
||||
appAgreementAcceptedAt: tempJson['appAgreementAcceptedAt'] != null
|
||||
? DateTime.parse(tempJson['appAgreementAcceptedAt'])
|
||||
: null,
|
||||
role: tempJson['role'] != null ? Role.fromJson(tempJson['role']) : null,
|
||||
project: null);
|
||||
uuid: tempJson['uuid'].toString(),
|
||||
email: tempJson['email'],
|
||||
lastName: tempJson['lastName'],
|
||||
firstName: tempJson['firstName'],
|
||||
profilePicture: UserModel.decodeBase64Image(tempJson['profilePicture']),
|
||||
phoneNumber: null,
|
||||
isEmailVerified: null,
|
||||
isAgreementAccepted: null,
|
||||
regionUuid: null,
|
||||
regionName: tempJson['region']?['regionName'],
|
||||
timeZone: tempJson['timezone']?['timeZoneOffset'],
|
||||
hasAcceptedWebAgreement: tempJson['hasAcceptedWebAgreement'],
|
||||
webAgreementAcceptedAt: tempJson['webAgreementAcceptedAt'] != null
|
||||
? DateTime.parse(tempJson['webAgreementAcceptedAt'])
|
||||
: null,
|
||||
hasAcceptedAppAgreement: tempJson['hasAcceptedAppAgreement'],
|
||||
appAgreementAcceptedAt: tempJson['appAgreementAcceptedAt'] != null
|
||||
? DateTime.parse(tempJson['appAgreementAcceptedAt'])
|
||||
: null,
|
||||
role: tempJson['role'] != null ? Role.fromJson(tempJson['role']) : null,
|
||||
project: null,
|
||||
points: tempJson['points'],
|
||||
);
|
||||
}
|
||||
|
||||
static Uint8List? decodeBase64Image(String? base64String) {
|
||||
|
@ -321,7 +321,7 @@ class _OtpViewState extends State<OtpView> {
|
||||
Expanded(
|
||||
child: DefaultButton(
|
||||
isDone: state is AuthLoginSuccess,
|
||||
isLoading: state is AuthLoading,
|
||||
isLoading: state is AuthOtpLoading,
|
||||
customButtonStyle: ButtonStyle(
|
||||
backgroundColor:
|
||||
MaterialStateProperty.all(
|
||||
@ -349,8 +349,8 @@ class _OtpViewState extends State<OtpView> {
|
||||
),
|
||||
Expanded(
|
||||
child: DefaultButton(
|
||||
isDone: state is AuthLoginSuccess,
|
||||
isLoading: state is AuthLoading,
|
||||
isDone: state is ResendOtpSuccess,
|
||||
isLoading: state is ResendOtpLoading,
|
||||
customButtonStyle: ButtonStyle(
|
||||
backgroundColor:
|
||||
MaterialStateProperty.all(
|
||||
@ -383,7 +383,8 @@ class _OtpViewState extends State<OtpView> {
|
||||
if (success) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) =>const SuccessDialog(
|
||||
builder: (_) =>
|
||||
const SuccessDialog(
|
||||
key: ValueKey(
|
||||
'SuccessDialog'),
|
||||
message: 'New OTP sent!',
|
||||
|
32
lib/features/booking_system/data/booking_dummy_source.dart
Normal file
32
lib/features/booking_system/data/booking_dummy_source.dart
Normal file
@ -0,0 +1,32 @@
|
||||
import 'package:syncrow_app/features/booking_system/domain/booking_model.dart';
|
||||
import 'package:syncrow_app/features/booking_system/domain/booking_service.dart';
|
||||
|
||||
class BookingDummySource implements BookingService {
|
||||
@override
|
||||
Future<List<BookingModel>> get() async {
|
||||
await Future.delayed(Duration(seconds: 2));
|
||||
return [
|
||||
BookingModel(
|
||||
uuid: 'uuid1',
|
||||
roomName: 'roomName1',
|
||||
date: 'wed 28th May 2025',
|
||||
timeSlot: '10:30 AM - 11:30 AM',
|
||||
cost: 4,
|
||||
),
|
||||
BookingModel(
|
||||
uuid: 'uuid2',
|
||||
roomName: 'roomName2',
|
||||
date: 'wed 28th May 2025',
|
||||
timeSlot: '10:30 AM - 11:30 AM',
|
||||
cost: 6,
|
||||
),
|
||||
BookingModel(
|
||||
uuid: 'uuid3',
|
||||
roomName: 'roomName3',
|
||||
date: 'thur 2th june 2025',
|
||||
timeSlot: '10:30 AM - 1:30 PM',
|
||||
cost: 10,
|
||||
)
|
||||
];
|
||||
}
|
||||
}
|
35
lib/features/booking_system/data/booking_remote_source.dart
Normal file
35
lib/features/booking_system/data/booking_remote_source.dart
Normal file
@ -0,0 +1,35 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:syncrow_app/features/booking_system/domain/booking_model.dart';
|
||||
import 'package:syncrow_app/features/booking_system/domain/booking_service.dart';
|
||||
import 'package:syncrow_app/services/api/api_links_endpoints.dart';
|
||||
import 'package:syncrow_app/services/api/http_service.dart';
|
||||
|
||||
class BookingRemoteSource implements BookingService {
|
||||
final HTTPService _httpService;
|
||||
BookingRemoteSource(this._httpService);
|
||||
|
||||
@override
|
||||
Future<List<BookingModel>> get() async {
|
||||
try {
|
||||
return _httpService.get(
|
||||
path: ApiEndpoints.upcomingBookings,
|
||||
expectedResponseModel: (json) {
|
||||
return BookingModel.fromJsonList(json['data']);
|
||||
},
|
||||
);
|
||||
} on DioException catch (e) {
|
||||
return [];
|
||||
// final message = e.response?.data as Map<String, dynamic>?;
|
||||
// final error = message?['error'] as Map<String, dynamic>?;
|
||||
// final errorMessage = error?['error'] as String? ?? '';
|
||||
// final formattedErrorMessage =
|
||||
// [_defaultErrorMessage, errorMessage].join(': ');
|
||||
// throw APIException(formattedErrorMessage);
|
||||
// } catch (e) {
|
||||
// final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': ');
|
||||
// throw APIException(formattedErrorMessage);
|
||||
// }
|
||||
//
|
||||
}
|
||||
}
|
||||
}
|
39
lib/features/booking_system/domain/booking_model.dart
Normal file
39
lib/features/booking_system/domain/booking_model.dart
Normal file
@ -0,0 +1,39 @@
|
||||
class BookingModel {
|
||||
final String uuid, roomName, date, timeSlot;
|
||||
final int cost;
|
||||
BookingModel({
|
||||
required this.uuid,
|
||||
required this.roomName,
|
||||
required this.date,
|
||||
required this.timeSlot,
|
||||
required this.cost,
|
||||
});
|
||||
factory BookingModel.zero() => BookingModel(
|
||||
uuid: '',
|
||||
roomName: '',
|
||||
date: '',
|
||||
timeSlot: '',
|
||||
cost: -1,
|
||||
);
|
||||
factory BookingModel.fromJson(Map<String, dynamic> json) => BookingModel(
|
||||
uuid: json['uuid'] as String,
|
||||
roomName: json['roomName'] as String,
|
||||
date: json['date'] as String,
|
||||
timeSlot: json['timeSlot'] as String,
|
||||
cost: json['cost'] as int,
|
||||
);
|
||||
|
||||
static List<BookingModel> fromJsonList(List<dynamic> jsonList) => jsonList
|
||||
.map(
|
||||
(bookModel) => BookingModel.fromJson(bookModel),
|
||||
)
|
||||
.toList();
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'uuid': uuid,
|
||||
'roomName': roomName,
|
||||
'date': date,
|
||||
'timeSlot': timeSlot,
|
||||
'cost': cost,
|
||||
};
|
||||
}
|
5
lib/features/booking_system/domain/booking_service.dart
Normal file
5
lib/features/booking_system/domain/booking_service.dart
Normal file
@ -0,0 +1,5 @@
|
||||
import 'package:syncrow_app/features/booking_system/domain/booking_model.dart';
|
||||
|
||||
abstract interface class BookingService {
|
||||
Future<List<BookingModel>> get();
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
import '../../../domain/booking_model.dart';
|
||||
import '../../../domain/booking_service.dart';
|
||||
|
||||
part 'past_bookings_event.dart';
|
||||
part 'past_bookings_state.dart';
|
||||
|
||||
class PastBookingsBloc extends Bloc<PastBookingsEvent, PastBookingsState> {
|
||||
final BookingService _bookingService;
|
||||
|
||||
PastBookingsBloc(this._bookingService) : super(PastBookingsInitial()) {
|
||||
on<GetPastBookingsEvent>(_onGetPastBookingsEvent);
|
||||
}
|
||||
Future<void> _onGetPastBookingsEvent(
|
||||
GetPastBookingsEvent event,
|
||||
Emitter<PastBookingsState> emit,
|
||||
) async {
|
||||
emit(PastBookingLoadingState());
|
||||
try {
|
||||
final pastBookings = await _bookingService.get();
|
||||
emit(
|
||||
PastBookingLoadedState(pastBookings: pastBookings),
|
||||
);
|
||||
} catch (e) {
|
||||
emit(
|
||||
PastBookingErrorState(e.toString()),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
part of 'past_bookings_bloc.dart';
|
||||
|
||||
sealed class PastBookingsEvent extends Equatable {
|
||||
const PastBookingsEvent();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class GetPastBookingsEvent extends PastBookingsEvent {}
|
@ -0,0 +1,24 @@
|
||||
part of 'past_bookings_bloc.dart';
|
||||
|
||||
sealed class PastBookingsState extends Equatable {
|
||||
const PastBookingsState();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
final class PastBookingsInitial extends PastBookingsState {}
|
||||
|
||||
final class PastBookingLoadingState extends PastBookingsState {}
|
||||
|
||||
final class PastBookingLoadedState extends PastBookingsState {
|
||||
final List<BookingModel> pastBookings;
|
||||
const PastBookingLoadedState({
|
||||
required this.pastBookings,
|
||||
});
|
||||
}
|
||||
|
||||
final class PastBookingErrorState extends PastBookingsState {
|
||||
final String errorMsg;
|
||||
const PastBookingErrorState(this.errorMsg);
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_app/features/booking_system/domain/booking_model.dart';
|
||||
import 'package:syncrow_app/features/booking_system/domain/booking_service.dart';
|
||||
|
||||
part 'upcoming_bookings_event.dart';
|
||||
part 'upcoming_bookings_state.dart';
|
||||
|
||||
class UpcomingBookingsBloc
|
||||
extends Bloc<UpcomingBookingsEvent, UpcomingBookingsState> {
|
||||
final BookingService _bookingService;
|
||||
UpcomingBookingsBloc(this._bookingService)
|
||||
: super(UpcomingBookingsInitial()) {
|
||||
on<GetUpcomingBookingsEvent>(_onGetUpcomingBookingsEvent);
|
||||
}
|
||||
Future<void> _onGetUpcomingBookingsEvent(
|
||||
GetUpcomingBookingsEvent event,
|
||||
Emitter<UpcomingBookingsState> emit,
|
||||
) async {
|
||||
emit(UpcomingBookingLoadingState());
|
||||
try {
|
||||
final upcomingBookings = await _bookingService.get();
|
||||
emit(
|
||||
UpcomingBookingLoadedState(upcomingBookings: upcomingBookings),
|
||||
);
|
||||
} catch (e) {
|
||||
emit(
|
||||
UpcomingBookingErrorState(e.toString()),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
part of 'upcoming_bookings_bloc.dart';
|
||||
|
||||
sealed class UpcomingBookingsEvent extends Equatable {
|
||||
const UpcomingBookingsEvent();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class GetUpcomingBookingsEvent extends UpcomingBookingsEvent {}
|
@ -0,0 +1,24 @@
|
||||
part of 'upcoming_bookings_bloc.dart';
|
||||
|
||||
sealed class UpcomingBookingsState extends Equatable {
|
||||
const UpcomingBookingsState();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
final class UpcomingBookingsInitial extends UpcomingBookingsState {}
|
||||
|
||||
final class UpcomingBookingLoadingState extends UpcomingBookingsState {}
|
||||
|
||||
final class UpcomingBookingLoadedState extends UpcomingBookingsState {
|
||||
final List<BookingModel> upcomingBookings;
|
||||
const UpcomingBookingLoadedState({
|
||||
required this.upcomingBookings,
|
||||
});
|
||||
}
|
||||
|
||||
final class UpcomingBookingErrorState extends UpcomingBookingsState {
|
||||
final String errorMsg;
|
||||
const UpcomingBookingErrorState(this.errorMsg);
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_app/features/booking_system/presentation/widgets/booking_appbar_widget.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_button.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
|
||||
import 'package:syncrow_app/utils/helpers/app_size.dart';
|
||||
|
||||
import '../../../../utils/resource_manager/color_manager.dart';
|
||||
import '../widgets/row_of_title_arrow_widget.dart';
|
||||
|
||||
class BookPage extends StatelessWidget {
|
||||
const BookPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DefaultScaffold(
|
||||
padding: EdgeInsets.zero,
|
||||
appBar: BookingAppBar(),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Text(
|
||||
'Booking Details',
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
color: ColorsManager.grayColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.all(10),
|
||||
height: deviceHeight(context) * 0.25,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(20)),
|
||||
color: ColorsManager.onPrimaryColor,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
RowOfTitleAndArrowWidget(
|
||||
title: 'Space',
|
||||
onTap: () {},
|
||||
),
|
||||
Divider(
|
||||
color: ColorsManager.grayButtonColors,
|
||||
),
|
||||
RowOfTitleAndArrowWidget(
|
||||
title: 'Booking Date',
|
||||
onTap: () {},
|
||||
),
|
||||
Divider(
|
||||
color: ColorsManager.grayButtonColors,
|
||||
),
|
||||
RowOfTitleAndArrowWidget(
|
||||
title: 'Time Slot',
|
||||
onTap: () {},
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 30,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||
child: DefaultButton(
|
||||
backgroundColor: ColorsManager.blueColor1,
|
||||
child: Text(
|
||||
'Book Now',
|
||||
style: TextStyle(
|
||||
fontSize: 17,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
|
||||
import 'package:syncrow_app/features/booking_system/data/booking_dummy_source.dart';
|
||||
import 'package:syncrow_app/features/booking_system/presentation/blocs/upcoming_bookings_bloc/upcoming_bookings_bloc.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
|
||||
import '../blocs/past_bookings_bloc/past_bookings_bloc.dart';
|
||||
import '../widgets/booking_appbar_widget.dart';
|
||||
import '../widgets/current_balance_widget.dart';
|
||||
import '../widgets/past_booking_widget.dart';
|
||||
import '../widgets/upcoming_bookings_widget.dart';
|
||||
|
||||
class BookingSystemPage extends StatelessWidget {
|
||||
const BookingSystemPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider<UpcomingBookingsBloc>(
|
||||
create: (_) => UpcomingBookingsBloc(BookingDummySource())
|
||||
..add(GetUpcomingBookingsEvent()),
|
||||
),
|
||||
BlocProvider<PastBookingsBloc>(
|
||||
create: (_) => PastBookingsBloc(BookingDummySource())
|
||||
..add(GetPastBookingsEvent()),
|
||||
)
|
||||
],
|
||||
child: DefaultScaffold(
|
||||
appBar: BookingAppBar(),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: CurrentBalanceWidget(
|
||||
userBalance: HomeCubit.user!.points == null
|
||||
? '0'
|
||||
: HomeCubit.user!.points.toString(),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Expanded(
|
||||
flex: 8,
|
||||
child: Column(
|
||||
children: [
|
||||
UpcomingBookingsWidget(),
|
||||
SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
PastBookingsWidget(),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../../utils/resource_manager/color_manager.dart';
|
||||
|
||||
class BookingAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||
const BookingAppBar({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
centerTitle: true,
|
||||
leading: IconButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
icon: Icon(Icons.arrow_back_ios_new)),
|
||||
title: Text(
|
||||
'Booking',
|
||||
style: TextStyle(
|
||||
color: ColorsManager.blueColor1,
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../../utils/resource_manager/color_manager.dart';
|
||||
import '../../domain/booking_model.dart';
|
||||
|
||||
class BookingCardWidget extends StatelessWidget {
|
||||
const BookingCardWidget({
|
||||
super.key,
|
||||
required this.bookingModel,
|
||||
});
|
||||
|
||||
final BookingModel bookingModel;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: EdgeInsets.all(15),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(20)),
|
||||
color: ColorsManager.onPrimaryColor,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
bookingModel.roomName,
|
||||
style: TextStyle(
|
||||
color: ColorsManager.blackColor,
|
||||
fontSize: 17,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
Text(
|
||||
'Booking Date',
|
||||
style: TextStyle(
|
||||
color: ColorsManager.grayColor,
|
||||
fontSize: 15,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
bookingModel.date,
|
||||
style: TextStyle(
|
||||
color: ColorsManager.blackColor,
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Time slot',
|
||||
style: TextStyle(
|
||||
color: ColorsManager.grayColor,
|
||||
fontSize: 15,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
bookingModel.timeSlot,
|
||||
style: TextStyle(
|
||||
color: ColorsManager.blackColor,
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
width: 80,
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Text(
|
||||
'cost',
|
||||
style: TextStyle(
|
||||
color: ColorsManager.grayColor,
|
||||
fontSize: 15,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
bookingModel.cost.toString(),
|
||||
style: TextStyle(
|
||||
color: ColorsManager.blackColor,
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../../utils/resource_manager/color_manager.dart';
|
||||
import '../screens/book_page.dart';
|
||||
|
||||
class CurrentBalanceWidget extends StatelessWidget {
|
||||
final String userBalance;
|
||||
const CurrentBalanceWidget({
|
||||
super.key,
|
||||
required this.userBalance,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.onPrimaryColor,
|
||||
borderRadius: BorderRadius.circular(20)),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 75,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Current Balance',
|
||||
style: TextStyle(
|
||||
color: ColorsManager.blackColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 17,
|
||||
),
|
||||
)),
|
||||
SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
Expanded(
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
userBalance,
|
||||
style: TextStyle(
|
||||
color: ColorsManager.blueColor1,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 50),
|
||||
),
|
||||
SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
Text(
|
||||
'Points',
|
||||
style: TextStyle(
|
||||
color: ColorsManager.blueColor1,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 17),
|
||||
),
|
||||
],
|
||||
)),
|
||||
],
|
||||
),
|
||||
)),
|
||||
Expanded(
|
||||
flex: 25,
|
||||
child: InkWell(
|
||||
//TODO:should use custom Navigator
|
||||
onTap: () => Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (context) => BookPage(),
|
||||
)),
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
padding: EdgeInsets.only(left: 15),
|
||||
height: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.blueColor,
|
||||
borderRadius:
|
||||
BorderRadius.horizontal(right: Radius.circular(20)),
|
||||
),
|
||||
child: Text(
|
||||
'Book Now',
|
||||
style: TextStyle(
|
||||
color: ColorsManager.onPrimaryColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 20),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../../../utils/helpers/app_size.dart';
|
||||
import '../../../../utils/resource_manager/color_manager.dart';
|
||||
import '../../../shared_widgets/default_button.dart';
|
||||
import '../blocs/past_bookings_bloc/past_bookings_bloc.dart';
|
||||
import 'booking_card_widget.dart';
|
||||
|
||||
class PastBookingsWidget extends StatelessWidget {
|
||||
const PastBookingsWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Past Bookings',
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
color: ColorsManager.grayColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
BlocBuilder<PastBookingsBloc, PastBookingsState>(
|
||||
builder: (context, state) {
|
||||
if (state is PastBookingLoadingState) {
|
||||
return CircularProgressIndicator();
|
||||
} else if (state is PastBookingErrorState) {
|
||||
return DefaultButton(
|
||||
onPressed: () => context
|
||||
.read<PastBookingsBloc>()
|
||||
.add(GetPastBookingsEvent()),
|
||||
child: Text('Try again'),
|
||||
);
|
||||
} else if (state is PastBookingLoadedState) {
|
||||
return SizedBox(
|
||||
height: deviceHeight(context) * 0.3,
|
||||
child: state.pastBookings.isEmpty
|
||||
? Text('You Dont Have past Bookings')
|
||||
: ListView.separated(
|
||||
separatorBuilder: (context, index) => SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
itemCount: state.pastBookings.length,
|
||||
itemBuilder: (context, index) {
|
||||
final pastBooking = state.pastBookings[index];
|
||||
return BookingCardWidget(bookingModel: pastBooking);
|
||||
},
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return SizedBox();
|
||||
}
|
||||
},
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../../utils/resource_manager/color_manager.dart';
|
||||
|
||||
class RowOfTitleAndArrowWidget extends StatelessWidget {
|
||||
final String title;
|
||||
final void Function() onTap;
|
||||
const RowOfTitleAndArrowWidget({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.all(10),
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 17,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: onTap,
|
||||
icon: Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
color: ColorsManager.grayColor,
|
||||
size: 15,
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../../../utils/helpers/app_size.dart';
|
||||
import '../../../../utils/resource_manager/color_manager.dart';
|
||||
import '../../../shared_widgets/default_button.dart';
|
||||
import '../blocs/upcoming_bookings_bloc/upcoming_bookings_bloc.dart';
|
||||
import 'booking_card_widget.dart';
|
||||
|
||||
class UpcomingBookingsWidget extends StatelessWidget {
|
||||
const UpcomingBookingsWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Upcoming Bookings',
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
color: ColorsManager.grayColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
BlocBuilder<UpcomingBookingsBloc, UpcomingBookingsState>(
|
||||
builder: (context, state) {
|
||||
if (state is UpcomingBookingLoadingState) {
|
||||
//TODO:should use CustomLoadingWidget
|
||||
return CircularProgressIndicator();
|
||||
} else if (state is UpcomingBookingErrorState) {
|
||||
//TODO:shold use CustomErrorWidget
|
||||
return DefaultButton(
|
||||
onPressed: () => context
|
||||
.read<UpcomingBookingsBloc>()
|
||||
.add(GetUpcomingBookingsEvent()),
|
||||
child: Text('Try again'),
|
||||
);
|
||||
} else if (state is UpcomingBookingLoadedState) {
|
||||
return SizedBox(
|
||||
height: deviceHeight(context) * 0.3,
|
||||
child: state.upcomingBookings.isEmpty
|
||||
? Text('You Dont Have Upcoming Bookings')
|
||||
: ListView.separated(
|
||||
separatorBuilder: (context, index) => SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
itemCount: state.upcomingBookings.length,
|
||||
itemBuilder: (context, index) {
|
||||
final upcomingBooking = state.upcomingBookings[index];
|
||||
return BookingCardWidget(
|
||||
bookingModel: upcomingBooking);
|
||||
},
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return SizedBox();
|
||||
}
|
||||
},
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -171,6 +171,7 @@ class FlushSensorBloc extends Bloc<FlushSensorEvent, FlushSensorState> {
|
||||
deviceInfo = DeviceInfoModel.fromJson(response);
|
||||
deviceName = deviceInfo.name;
|
||||
emit(FlushSensorLoadingDeviceInfo(deviceInfo: deviceInfo));
|
||||
emit(FlushSensorUpdateState(flushSensorModel: deviceStatus));
|
||||
} catch (e) {
|
||||
emit(FlushSensorFailedState(error: e.toString()));
|
||||
}
|
||||
|
@ -150,7 +150,6 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
return super.close();
|
||||
}
|
||||
|
||||
|
||||
_doorLockUpdated(DoorLockUpdated event, Emitter<SmartDoorState> emit) {
|
||||
unlockRequest = deviceStatus.unlockRequest;
|
||||
emit(UpdateState(smartDoorModel: deviceStatus));
|
||||
@ -254,18 +253,42 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
}
|
||||
|
||||
void _updateLock(UpdateLockEvent event, Emitter<SmartDoorState> emit) async {
|
||||
emit(LoadingNewSate(smartDoorModel: deviceStatus));
|
||||
final oldValue = deviceStatus.normalOpenSwitch;
|
||||
deviceStatus = deviceStatus.copyWith(normalOpenSwitch: !oldValue);
|
||||
emit(UpdateState(smartDoorModel: deviceStatus));
|
||||
try {
|
||||
// final response = await DevicesAPI.controlDevice(
|
||||
// DeviceControlModel(deviceId: deviceId, code: 'normal_open_switch', value: !event.value),
|
||||
// deviceId);
|
||||
|
||||
final response = await DevicesAPI.openDoorLock(deviceId);
|
||||
|
||||
if (response) {
|
||||
deviceStatus.normalOpenSwitch = !event.value;
|
||||
if (!response) {
|
||||
_revertValueAndEmit(deviceId, 'normal_open_switch', oldValue, emit);
|
||||
}
|
||||
} catch (_) {}
|
||||
} catch (_) {
|
||||
_revertValueAndEmit(deviceId, 'normal_open_switch', oldValue, emit);
|
||||
}
|
||||
}
|
||||
|
||||
void _revertValueAndEmit(String deviceId, String code, dynamic oldValue,
|
||||
Emitter<SmartDoorState> emit) {
|
||||
_updateLocalValue(code, oldValue);
|
||||
emit(UpdateState(smartDoorModel: deviceStatus));
|
||||
emit(const FailedState(errorMessage: 'Failed to control the device.'));
|
||||
}
|
||||
|
||||
void _updateLocalValue(String code, dynamic value) {
|
||||
switch (code) {
|
||||
case 'normal_open_switch':
|
||||
if (value is bool) {
|
||||
deviceStatus = deviceStatus.copyWith(normalOpenSwitch: value);
|
||||
}
|
||||
break;
|
||||
case 'reverse_lock':
|
||||
if (value is bool) {
|
||||
deviceStatus = deviceStatus.copyWith(reverseLock: value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
emit(UpdateState(smartDoorModel: deviceStatus));
|
||||
}
|
||||
|
||||
@ -331,6 +354,7 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
|
||||
Future<void> selectTimeOnlinePassword(
|
||||
SelectTimeOnlinePasswordEvent event, Emitter<SmartDoorState> emit) async {
|
||||
effectiveTimeTimeStamp ??= DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||||
emit(ChangeTimeState());
|
||||
final DateTime? picked = await showDatePicker(
|
||||
context: event.context,
|
||||
@ -375,7 +399,13 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
selectedDateTime.minute,
|
||||
).millisecondsSinceEpoch ~/
|
||||
1000; // Divide by 1000 to remove milliseconds
|
||||
final currentTimestamp = DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||||
if (event.isEffective) {
|
||||
if (selectedTimestamp < currentTimestamp) {
|
||||
CustomSnackBar.displaySnackBar(
|
||||
'Effective Time cannot be later than Expiration Time.');
|
||||
return;
|
||||
}
|
||||
if (expirationTimeTimeStamp != null &&
|
||||
selectedTimestamp > expirationTimeTimeStamp!) {
|
||||
CustomSnackBar.displaySnackBar(
|
||||
|
@ -55,31 +55,33 @@ class DeviceInfoModel {
|
||||
|
||||
factory DeviceInfoModel.fromJson(Map<String, dynamic> json) {
|
||||
return DeviceInfoModel(
|
||||
activeTime: json['activeTime'],
|
||||
category: json['category'],
|
||||
categoryName: json['categoryName'],
|
||||
createTime: json['createTime'],
|
||||
gatewayId: json['gatewayId'],
|
||||
icon: json['icon'],
|
||||
activeTime: json['activeTime'] ?? '',
|
||||
category: json['category'] ?? '',
|
||||
categoryName: json['categoryName'] ?? '',
|
||||
createTime: json['createTime'] ?? '',
|
||||
gatewayId: json['gatewayId'] ?? '',
|
||||
icon: json['icon'] ?? '',
|
||||
ip: json['ip'] ?? "",
|
||||
lat: json['lat'],
|
||||
localKey: json['localKey'],
|
||||
lon: json['lon'],
|
||||
model: json['model'],
|
||||
name: json['name'],
|
||||
nodeId: json['nodeId'],
|
||||
online: json['online'],
|
||||
ownerId: json['ownerId'],
|
||||
productName: json['productName'],
|
||||
sub: json['sub'],
|
||||
timeZone: json['timeZone'],
|
||||
updateTime: json['updateTime'],
|
||||
uuid: json['uuid'],
|
||||
productUuid: json['productUuid'],
|
||||
productType: json['productType'],
|
||||
lat: json['lat'] ?? '',
|
||||
localKey: json['localKey'] ?? '',
|
||||
lon: json['lon'] ?? '',
|
||||
model: json['model'] ?? '',
|
||||
name: json['name'] ?? '',
|
||||
nodeId: json['nodeId'] ?? '',
|
||||
online: json['online'] ?? '',
|
||||
ownerId: json['ownerId'] ?? '',
|
||||
productName: json['productName'] ?? '',
|
||||
sub: json['sub'] ?? '',
|
||||
timeZone: json['timeZone'] ?? '',
|
||||
updateTime: json['updateTime'] ?? '',
|
||||
uuid: json['uuid'] ?? '',
|
||||
productUuid: json['productUuid'] ?? '',
|
||||
productType: json['productType'] ?? '',
|
||||
permissionType: json['permissionType'] ?? '',
|
||||
macAddress: json['macAddress'],
|
||||
subspace: Subspace.fromJson(json['subspace']),
|
||||
macAddress: json['macAddress'] ?? '',
|
||||
subspace: json['subspace'] != null
|
||||
? Subspace.fromJson(json['subspace'])
|
||||
: throw ArgumentError('subspace cannot be null'),
|
||||
);
|
||||
}
|
||||
|
||||
@ -129,10 +131,10 @@ class Subspace {
|
||||
|
||||
factory Subspace.fromJson(Map<String, dynamic> json) {
|
||||
return Subspace(
|
||||
uuid: json['uuid'],
|
||||
createdAt: json['createdAt'],
|
||||
updatedAt: json['updatedAt'],
|
||||
subspaceName: json['subspaceName'],
|
||||
uuid: json['uuid'] ?? '',
|
||||
createdAt: json['createdAt'] ?? '',
|
||||
updatedAt: json['updatedAt'] ?? '',
|
||||
subspaceName: json['subspaceName'] ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -113,4 +113,44 @@ class SmartDoorModel {
|
||||
remoteNoDpKey: _remoteNoDpKey,
|
||||
normalOpenSwitch: _normalOpenSwitch);
|
||||
}
|
||||
SmartDoorModel copyWith({
|
||||
String? uuid,
|
||||
int? unlockFingerprint,
|
||||
int? unlockPassword,
|
||||
int? unlockTemporary,
|
||||
int? unlockCard,
|
||||
String? alarmLock,
|
||||
int? unlockRequest,
|
||||
int? residualElectricity,
|
||||
bool? reverseLock,
|
||||
int? unlockApp,
|
||||
bool? hijack,
|
||||
bool? doorbell,
|
||||
String? unlockOfflinePd,
|
||||
String? unlockOfflineClear,
|
||||
String? unlockDoubleKit,
|
||||
String? remoteNoPdSetkey,
|
||||
String? remoteNoDpKey,
|
||||
bool? normalOpenSwitch,
|
||||
}) {
|
||||
return SmartDoorModel(
|
||||
unlockAlarm: alarmLock ?? unlockAlarm,
|
||||
unlockFingerprint: unlockFingerprint ?? this.unlockFingerprint,
|
||||
unlockPassword: unlockPassword ?? this.unlockPassword,
|
||||
unlockTemporary: unlockTemporary ?? this.unlockTemporary,
|
||||
unlockCard: unlockCard ?? this.unlockCard,
|
||||
unlockRequest: unlockRequest ?? this.unlockRequest,
|
||||
residualElectricity: residualElectricity ?? this.residualElectricity,
|
||||
reverseLock: reverseLock ?? this.reverseLock,
|
||||
unlockApp: unlockApp ?? this.unlockApp,
|
||||
hijack: hijack ?? this.hijack,
|
||||
doorbell: doorbell ?? this.doorbell,
|
||||
unlockOfflinePd: unlockOfflinePd ?? this.unlockOfflinePd,
|
||||
unlockOfflineClear: unlockOfflineClear ?? this.unlockOfflineClear,
|
||||
unlockDoubleKit: unlockDoubleKit ?? this.unlockDoubleKit,
|
||||
remoteNoPdSetkey: remoteNoPdSetkey ?? this.remoteNoPdSetkey,
|
||||
remoteNoDpKey: remoteNoDpKey ?? this.remoteNoDpKey,
|
||||
normalOpenSwitch: normalOpenSwitch ?? this.normalOpenSwitch,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -22,141 +22,155 @@ class SettingProfilePage extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
var spaces = HomeCubit.getInstance().spaces;
|
||||
|
||||
return DefaultScaffold(
|
||||
title: 'Device Settings',
|
||||
leading: IconButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
icon: const Icon(Icons.arrow_back_ios)),
|
||||
child: BlocProvider(
|
||||
create: (context) => DeviceSettingBloc(deviceId: device?.uuid ?? '')
|
||||
..add(const DeviceSettingInitial())
|
||||
..add(const DeviceSettingInitialInfo()),
|
||||
child: BlocBuilder<DeviceSettingBloc, DeviceSettingState>(
|
||||
builder: (context, state) {
|
||||
final _bloc = BlocProvider.of<DeviceSettingBloc>(context);
|
||||
return state is DeviceSettingLoadingState
|
||||
? const Center(
|
||||
child: DefaultContainer(
|
||||
width: 50,
|
||||
height: 50,
|
||||
child: CircularProgressIndicator()),
|
||||
)
|
||||
: RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
_bloc.add(const DeviceSettingInitial());
|
||||
},
|
||||
child: ListView(
|
||||
children: [
|
||||
buildDeviceAvatar(context, device!),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
SizedBox(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
IntrinsicWidth(
|
||||
child: ConstrainedBox(
|
||||
constraints:
|
||||
const BoxConstraints(maxWidth: 200),
|
||||
child: TextFormField(
|
||||
maxLength: 30,
|
||||
style: const TextStyle(
|
||||
color: Colors.black,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
focusNode: _bloc.focusNode,
|
||||
controller: _bloc.nameController,
|
||||
enabled: _bloc.editName,
|
||||
onEditingComplete: () {
|
||||
_bloc.add(const SaveNameEvent());
|
||||
},
|
||||
decoration: const InputDecoration(
|
||||
hintText: "Your Name",
|
||||
border: InputBorder.none,
|
||||
fillColor: Colors.white10,
|
||||
counterText: '',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 5),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
_bloc.add(const ChangeNameEvent(value: true));
|
||||
},
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||
child: SvgPicture.asset(
|
||||
Assets.sosEditProfile,
|
||||
color: Colors.grey,
|
||||
fit: BoxFit.contain,
|
||||
height: MediaQuery.of(context).size.height *
|
||||
0.02,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
return PopScope(
|
||||
canPop: false,
|
||||
onPopInvoked: (didPop) {
|
||||
if (didPop) {
|
||||
return;
|
||||
}
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
child: DefaultScaffold(
|
||||
title: 'Device Settings',
|
||||
leading: IconButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
icon: const Icon(Icons.arrow_back_ios)),
|
||||
child: BlocProvider(
|
||||
create: (context) => DeviceSettingBloc(deviceId: device?.uuid ?? '')
|
||||
..add(const DeviceSettingInitial())
|
||||
..add(const DeviceSettingInitialInfo()),
|
||||
child: BlocBuilder<DeviceSettingBloc, DeviceSettingState>(
|
||||
builder: (context, state) {
|
||||
final _bloc = BlocProvider.of<DeviceSettingBloc>(context);
|
||||
return state is DeviceSettingLoadingState
|
||||
? const Center(
|
||||
child: DefaultContainer(
|
||||
width: 50,
|
||||
height: 50,
|
||||
child: CircularProgressIndicator()),
|
||||
)
|
||||
: RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
_bloc.add(const DeviceSettingInitial());
|
||||
},
|
||||
child: ListView(
|
||||
children: [
|
||||
buildDeviceAvatar(context, device!),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
const BodyMedium(
|
||||
text: 'Smart Device Information',
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 12,
|
||||
fontColor: ColorsManager.grayColor,
|
||||
),
|
||||
const SizedBox(height: 7),
|
||||
DefaultContainer(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
if (HomeCubit.visitorPasswordManagement) {
|
||||
bool? val = await Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => LocationSettingPage(
|
||||
space: spaces!.first,
|
||||
deviceId: device?.uuid ?? '',
|
||||
)),
|
||||
);
|
||||
if (val != null && val == true) {
|
||||
_bloc.add(const DeviceSettingInitialInfo());
|
||||
}
|
||||
}
|
||||
},
|
||||
SizedBox(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const SizedBox(
|
||||
child: Text('Location'),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
child: BodyMedium(
|
||||
text: _bloc
|
||||
.deviceInfo.subspace.subspaceName,
|
||||
fontColor: ColorsManager.textGray,
|
||||
IntrinsicWidth(
|
||||
child: ConstrainedBox(
|
||||
constraints:
|
||||
const BoxConstraints(maxWidth: 200),
|
||||
child: TextFormField(
|
||||
maxLength: 30,
|
||||
style: const TextStyle(
|
||||
color: Colors.black,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
focusNode: _bloc.focusNode,
|
||||
controller: _bloc.nameController,
|
||||
enabled: _bloc.editName,
|
||||
onEditingComplete: () {
|
||||
_bloc.add(const SaveNameEvent());
|
||||
},
|
||||
decoration: const InputDecoration(
|
||||
hintText: "Your Name",
|
||||
border: InputBorder.none,
|
||||
fillColor: Colors.white10,
|
||||
counterText: '',
|
||||
),
|
||||
),
|
||||
const Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
size: 15,
|
||||
color: ColorsManager.textGray,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 5),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
_bloc.add(
|
||||
const ChangeNameEvent(value: true));
|
||||
},
|
||||
child: Padding(
|
||||
padding:
|
||||
EdgeInsets.symmetric(horizontal: 10),
|
||||
child: SvgPicture.asset(
|
||||
Assets.sosEditProfile,
|
||||
color: Colors.grey,
|
||||
fit: BoxFit.contain,
|
||||
height:
|
||||
MediaQuery.of(context).size.height *
|
||||
0.02,
|
||||
),
|
||||
],
|
||||
)
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
const SizedBox(height: 20),
|
||||
const BodyMedium(
|
||||
text: 'Smart Device Information',
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 12,
|
||||
fontColor: ColorsManager.grayColor,
|
||||
),
|
||||
const SizedBox(height: 7),
|
||||
DefaultContainer(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
if (HomeCubit.visitorPasswordManagement) {
|
||||
bool? val = await Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
LocationSettingPage(
|
||||
space: spaces!.first,
|
||||
deviceId: device?.uuid ?? '',
|
||||
)),
|
||||
);
|
||||
if (val != null && val == true) {
|
||||
_bloc.add(const DeviceSettingInitialInfo());
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const SizedBox(
|
||||
child: Text('Location'),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
child: BodyMedium(
|
||||
text: _bloc
|
||||
.deviceInfo.subspace.subspaceName,
|
||||
fontColor: ColorsManager.textGray,
|
||||
),
|
||||
),
|
||||
const Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
size: 15,
|
||||
color: ColorsManager.textGray,
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -28,406 +28,449 @@ class SettingsPage extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DefaultScaffold(
|
||||
title: 'Device Settings',
|
||||
child: BlocProvider(
|
||||
create: (context) => DeviceSettingBloc(deviceId: device?.uuid ?? '')
|
||||
..add(const DeviceSettingInitial())
|
||||
..add(const DeviceSettingInitialInfo()),
|
||||
child: BlocBuilder<DeviceSettingBloc, DeviceSettingState>(
|
||||
builder: (context, state) {
|
||||
final _bloc = BlocProvider.of<DeviceSettingBloc>(context);
|
||||
return PopScope(
|
||||
canPop: false,
|
||||
onPopInvoked: (didPop) {
|
||||
if (didPop) {
|
||||
return;
|
||||
}
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
child: DefaultScaffold(
|
||||
title: 'Device Settings',
|
||||
child: BlocProvider(
|
||||
create: (context) => DeviceSettingBloc(deviceId: device?.uuid ?? '')
|
||||
..add(const DeviceSettingInitial())
|
||||
..add(const DeviceSettingInitialInfo()),
|
||||
child: BlocBuilder<DeviceSettingBloc, DeviceSettingState>(
|
||||
builder: (context, state) {
|
||||
final _bloc = BlocProvider.of<DeviceSettingBloc>(context);
|
||||
|
||||
return state is DeviceSettingLoadingState
|
||||
? const Center(
|
||||
child:
|
||||
DefaultContainer(width: 50, height: 50, child: CircularProgressIndicator()),
|
||||
)
|
||||
: ListView(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 10,
|
||||
),
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
bool val = await Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => SettingProfilePage(
|
||||
device: device,
|
||||
),
|
||||
),
|
||||
);
|
||||
if (val == true) {
|
||||
_bloc.add(const DeviceSettingInitialInfo());
|
||||
}
|
||||
},
|
||||
child: Stack(
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const SizedBox(height: 20),
|
||||
DefaultContainer(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(30)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 90),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
child: Text(
|
||||
_bloc.deviceInfo.name,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: ColorsManager.grayColor,
|
||||
),
|
||||
overflow: TextOverflow
|
||||
.ellipsis, // Adds ellipsis (...) when the text overflows.
|
||||
maxLines:
|
||||
1, // Restricts the text to a single line.
|
||||
)),
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
BodySmall(
|
||||
text: _bloc.deviceInfo.subspace.subspaceName),
|
||||
],
|
||||
),
|
||||
),
|
||||
SvgPicture.asset(
|
||||
Assets.editNameSetting,
|
||||
fit: BoxFit.contain,
|
||||
height: 30,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Positioned(
|
||||
top: 0,
|
||||
left: 20,
|
||||
child: CircleAvatar(
|
||||
radius: 43,
|
||||
backgroundColor: Colors.white,
|
||||
child: CircleAvatar(
|
||||
radius: 40,
|
||||
backgroundColor: Colors.white,
|
||||
child: CircleAvatar(
|
||||
radius: 40,
|
||||
backgroundColor: ColorsManager.backgroundColor,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
device!.type == 'NCPS'
|
||||
? SizedBox(
|
||||
height: 80,
|
||||
width: 50,
|
||||
child: SvgPicture.asset(
|
||||
Assets.flushIcon,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
):
|
||||
device!.type == "SOS"
|
||||
? ClipOval(
|
||||
child: SvgPicture.asset(
|
||||
Assets.sosHomeIcon,
|
||||
fit: BoxFit.contain,
|
||||
height: 70,
|
||||
),
|
||||
)
|
||||
: Padding(
|
||||
padding: device!.type == "4S"
|
||||
? const EdgeInsets.only(top: 5)
|
||||
: const EdgeInsets.only(top: 0),
|
||||
child: SizedBox(
|
||||
height: 70,
|
||||
child: SvgPicture.asset(
|
||||
device!.type == "4S"
|
||||
? Assets.fourSceneIcon
|
||||
: Assets.sixSceneIcon,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)),
|
||||
),
|
||||
],
|
||||
return state is DeviceSettingLoadingState
|
||||
? const Center(
|
||||
child: DefaultContainer(
|
||||
width: 50,
|
||||
height: 50,
|
||||
child: CircularProgressIndicator()),
|
||||
)
|
||||
: ListView(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 10,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
const BodyMedium(
|
||||
text: 'Device Management',
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 12,
|
||||
fontColor: ColorsManager.grayColor,
|
||||
),
|
||||
DefaultContainer(
|
||||
child: Column(
|
||||
children: [
|
||||
SettingWidget(
|
||||
onTap: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => SettingInfoPage(
|
||||
device: device!,
|
||||
)),
|
||||
);
|
||||
},
|
||||
text: 'Device Information',
|
||||
icon: Assets.infoIcon,
|
||||
),
|
||||
// const Divider(
|
||||
// color: ColorsManager.dividerColor,
|
||||
// ),
|
||||
// SettingWidget(
|
||||
// onTap: () {
|
||||
// Navigator.of(context).push(
|
||||
// MaterialPageRoute(
|
||||
// builder: (context) =>
|
||||
// ShareFourScenePage(
|
||||
// device: device!)),
|
||||
// );
|
||||
// },
|
||||
// text: 'Tap-to Run and Automation',
|
||||
// icon: Assets.tapRunIcon,
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
// const BodyMedium(
|
||||
// text: 'Device Offline Notification',
|
||||
// fontWeight: FontWeight.w700,
|
||||
// fontSize: 12,
|
||||
// fontColor: ColorsManager.grayColor,
|
||||
// ),
|
||||
// DefaultContainer(
|
||||
// child: Column(
|
||||
// children: [
|
||||
// SettingWidget(
|
||||
// value: _bloc.enableAlarm,
|
||||
// onChanged: (p0) {
|
||||
// context
|
||||
// .read<DeviceSettingBloc>()
|
||||
// .add(ToggleEnableAlarmEvent(p0));
|
||||
// },
|
||||
// isNotification: true,
|
||||
// onTap: () {},
|
||||
// text: 'Offline Notification',
|
||||
// icon: Assets.notificationIcon,
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(height: 20),
|
||||
// const BodyMedium(
|
||||
// text: 'Others',
|
||||
// fontWeight: FontWeight.w700,
|
||||
// fontSize: 12,
|
||||
// fontColor: ColorsManager.grayColor,
|
||||
// ),
|
||||
// const SizedBox(height: 5),
|
||||
// DefaultContainer(
|
||||
// child: Column(
|
||||
// children: [
|
||||
// SettingWidget(
|
||||
// onTap: () {
|
||||
// Navigator.of(context).push(
|
||||
// MaterialPageRoute(
|
||||
// builder: (context) =>
|
||||
// ShareDevicePage(device: device!)),
|
||||
// );
|
||||
// },
|
||||
// text: 'Share Device',
|
||||
// icon: Assets.shareIcon,
|
||||
// ),
|
||||
// // const Divider(
|
||||
// // color: ColorsManager.dividerColor,
|
||||
// // ),
|
||||
// // SettingWidget(
|
||||
// // onTap: () {
|
||||
// // Navigator.of(context).push(
|
||||
// // MaterialPageRoute(
|
||||
// // builder: (context) =>
|
||||
// // FourSceneCreateGroup(
|
||||
// // device: device!)),
|
||||
// // );
|
||||
// // },
|
||||
// // text: 'Create Group',
|
||||
// // icon: Assets.createGroupIcon,
|
||||
// // ),
|
||||
// const Divider(
|
||||
// color: ColorsManager.dividerColor,
|
||||
// ),
|
||||
// SettingWidget(
|
||||
// onTap: () {
|
||||
// Navigator.of(context).push(
|
||||
// MaterialPageRoute(
|
||||
// builder: (context) =>
|
||||
// FaqSettingPage(device: device!)),
|
||||
// );
|
||||
// },
|
||||
// text: 'Device FAQ',
|
||||
// icon: Assets.faqIcon,
|
||||
// ),
|
||||
// const Divider(
|
||||
// color: ColorsManager.dividerColor,
|
||||
// ),
|
||||
// SettingWidget(
|
||||
// onTapUpdate: () {
|
||||
// showDialog(
|
||||
// context: context,
|
||||
// builder: (context) {
|
||||
// return UpdateInfoDialog(
|
||||
// cancelTab: () {
|
||||
// Navigator.of(context).pop();
|
||||
// },
|
||||
// confirmTab: () {
|
||||
// Navigator.of(context).pop();
|
||||
// },
|
||||
// );
|
||||
// },
|
||||
// );
|
||||
// },
|
||||
// isUpdate: true,
|
||||
// onTap: () {
|
||||
// Navigator.of(context).push(
|
||||
// MaterialPageRoute(
|
||||
// builder: (context) =>
|
||||
// const UpdatePageSetting()),
|
||||
// );
|
||||
// },
|
||||
// text: 'Device Update',
|
||||
// icon: Assets.updateIcon,
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
const SizedBox(height: 20),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return Container(
|
||||
height: 200,
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
bool val = await Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => SettingProfilePage(
|
||||
device: device,
|
||||
),
|
||||
),
|
||||
);
|
||||
if (val == true) {
|
||||
_bloc.add(const DeviceSettingInitialInfo());
|
||||
}
|
||||
},
|
||||
child: Stack(
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const BodyMedium(
|
||||
text: 'Remove Device',
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 16,
|
||||
fontColor: ColorsManager.red,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
const SizedBox(
|
||||
width: 250,
|
||||
child: Divider(
|
||||
color: ColorsManager.dividerColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return DisconnectDeviceDialog(
|
||||
cancelTab: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
confirmTab: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: const BodyMedium(
|
||||
text: 'Disconnect Device',
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 15,
|
||||
fontColor: ColorsManager.textPrimaryColor,
|
||||
),
|
||||
),
|
||||
const Icon(
|
||||
Icons.keyboard_arrow_right,
|
||||
color: ColorsManager.textGray,
|
||||
)
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return DisconnectWipeData(
|
||||
cancelTab: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
confirmTab: () {
|
||||
_bloc.add(DeleteDeviceEvent());
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: const BodyMedium(
|
||||
text: 'Disconnect Device and Wipe Data',
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 15,
|
||||
fontColor: ColorsManager.textPrimaryColor,
|
||||
DefaultContainer(
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(30)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(left: 90),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
child: Text(
|
||||
_bloc.deviceInfo.name,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight:
|
||||
FontWeight.w700,
|
||||
color: ColorsManager
|
||||
.grayColor,
|
||||
),
|
||||
overflow: TextOverflow
|
||||
.ellipsis, // Adds ellipsis (...) when the text overflows.
|
||||
maxLines:
|
||||
1, // Restricts the text to a single line.
|
||||
)),
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
BodySmall(
|
||||
text: _bloc
|
||||
.deviceInfo
|
||||
.subspace
|
||||
.subspaceName),
|
||||
],
|
||||
),
|
||||
),
|
||||
SvgPicture.asset(
|
||||
Assets.editNameSetting,
|
||||
fit: BoxFit.contain,
|
||||
height: 30,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Icon(
|
||||
Icons.keyboard_arrow_right,
|
||||
color: ColorsManager.textGray,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: const Center(
|
||||
child: BodyMedium(
|
||||
text: 'Remove Device',
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 15,
|
||||
fontColor: ColorsManager.red,
|
||||
Positioned(
|
||||
top: 0,
|
||||
left: 20,
|
||||
child: CircleAvatar(
|
||||
radius: 43,
|
||||
backgroundColor: Colors.white,
|
||||
child: CircleAvatar(
|
||||
radius: 40,
|
||||
backgroundColor: Colors.white,
|
||||
child: CircleAvatar(
|
||||
radius: 40,
|
||||
backgroundColor:
|
||||
ColorsManager.backgroundColor,
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.center,
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
children: [
|
||||
device!.type == 'NCPS'
|
||||
? SizedBox(
|
||||
height: 80,
|
||||
width: 50,
|
||||
child: SvgPicture.asset(
|
||||
Assets.flushIcon,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
)
|
||||
: device!.type == "SOS"
|
||||
? ClipOval(
|
||||
child:
|
||||
SvgPicture.asset(
|
||||
Assets.sosHomeIcon,
|
||||
fit: BoxFit.contain,
|
||||
height: 70,
|
||||
),
|
||||
)
|
||||
: Padding(
|
||||
padding: device!
|
||||
.type ==
|
||||
"4S"
|
||||
? const EdgeInsets
|
||||
.only(top: 5)
|
||||
: const EdgeInsets
|
||||
.only(top: 0),
|
||||
child: SizedBox(
|
||||
height: 70,
|
||||
child: SvgPicture
|
||||
.asset(
|
||||
device!.type ==
|
||||
"4S"
|
||||
? Assets
|
||||
.fourSceneIcon
|
||||
: Assets
|
||||
.sixSceneIcon,
|
||||
fit: BoxFit
|
||||
.contain,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
const SizedBox(height: 20),
|
||||
const BodyMedium(
|
||||
text: 'Device Management',
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 12,
|
||||
fontColor: ColorsManager.grayColor,
|
||||
),
|
||||
DefaultContainer(
|
||||
child: Column(
|
||||
children: [
|
||||
SettingWidget(
|
||||
onTap: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => SettingInfoPage(
|
||||
device: device!,
|
||||
)),
|
||||
);
|
||||
},
|
||||
text: 'Device Information',
|
||||
icon: Assets.infoIcon,
|
||||
),
|
||||
// const Divider(
|
||||
// color: ColorsManager.dividerColor,
|
||||
// ),
|
||||
// SettingWidget(
|
||||
// onTap: () {
|
||||
// Navigator.of(context).push(
|
||||
// MaterialPageRoute(
|
||||
// builder: (context) =>
|
||||
// ShareFourScenePage(
|
||||
// device: device!)),
|
||||
// );
|
||||
// },
|
||||
// text: 'Tap-to Run and Automation',
|
||||
// icon: Assets.tapRunIcon,
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
// const BodyMedium(
|
||||
// text: 'Device Offline Notification',
|
||||
// fontWeight: FontWeight.w700,
|
||||
// fontSize: 12,
|
||||
// fontColor: ColorsManager.grayColor,
|
||||
// ),
|
||||
// DefaultContainer(
|
||||
// child: Column(
|
||||
// children: [
|
||||
// SettingWidget(
|
||||
// value: _bloc.enableAlarm,
|
||||
// onChanged: (p0) {
|
||||
// context
|
||||
// .read<DeviceSettingBloc>()
|
||||
// .add(ToggleEnableAlarmEvent(p0));
|
||||
// },
|
||||
// isNotification: true,
|
||||
// onTap: () {},
|
||||
// text: 'Offline Notification',
|
||||
// icon: Assets.notificationIcon,
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(height: 20),
|
||||
// const BodyMedium(
|
||||
// text: 'Others',
|
||||
// fontWeight: FontWeight.w700,
|
||||
// fontSize: 12,
|
||||
// fontColor: ColorsManager.grayColor,
|
||||
// ),
|
||||
// const SizedBox(height: 5),
|
||||
// DefaultContainer(
|
||||
// child: Column(
|
||||
// children: [
|
||||
// SettingWidget(
|
||||
// onTap: () {
|
||||
// Navigator.of(context).push(
|
||||
// MaterialPageRoute(
|
||||
// builder: (context) =>
|
||||
// ShareDevicePage(device: device!)),
|
||||
// );
|
||||
// },
|
||||
// text: 'Share Device',
|
||||
// icon: Assets.shareIcon,
|
||||
// ),
|
||||
// // const Divider(
|
||||
// // color: ColorsManager.dividerColor,
|
||||
// // ),
|
||||
// // SettingWidget(
|
||||
// // onTap: () {
|
||||
// // Navigator.of(context).push(
|
||||
// // MaterialPageRoute(
|
||||
// // builder: (context) =>
|
||||
// // FourSceneCreateGroup(
|
||||
// // device: device!)),
|
||||
// // );
|
||||
// // },
|
||||
// // text: 'Create Group',
|
||||
// // icon: Assets.createGroupIcon,
|
||||
// // ),
|
||||
// const Divider(
|
||||
// color: ColorsManager.dividerColor,
|
||||
// ),
|
||||
// SettingWidget(
|
||||
// onTap: () {
|
||||
// Navigator.of(context).push(
|
||||
// MaterialPageRoute(
|
||||
// builder: (context) =>
|
||||
// FaqSettingPage(device: device!)),
|
||||
// );
|
||||
// },
|
||||
// text: 'Device FAQ',
|
||||
// icon: Assets.faqIcon,
|
||||
// ),
|
||||
// const Divider(
|
||||
// color: ColorsManager.dividerColor,
|
||||
// ),
|
||||
// SettingWidget(
|
||||
// onTapUpdate: () {
|
||||
// showDialog(
|
||||
// context: context,
|
||||
// builder: (context) {
|
||||
// return UpdateInfoDialog(
|
||||
// cancelTab: () {
|
||||
// Navigator.of(context).pop();
|
||||
// },
|
||||
// confirmTab: () {
|
||||
// Navigator.of(context).pop();
|
||||
// },
|
||||
// );
|
||||
// },
|
||||
// );
|
||||
// },
|
||||
// isUpdate: true,
|
||||
// onTap: () {
|
||||
// Navigator.of(context).push(
|
||||
// MaterialPageRoute(
|
||||
// builder: (context) =>
|
||||
// const UpdatePageSetting()),
|
||||
// );
|
||||
// },
|
||||
// text: 'Device Update',
|
||||
// icon: Assets.updateIcon,
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
const SizedBox(height: 20),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return Container(
|
||||
height: 200,
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const BodyMedium(
|
||||
text: 'Remove Device',
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 16,
|
||||
fontColor: ColorsManager.red,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
const SizedBox(
|
||||
width: 250,
|
||||
child: Divider(
|
||||
color: ColorsManager.dividerColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return DisconnectDeviceDialog(
|
||||
cancelTab: () {
|
||||
Navigator.of(context)
|
||||
.pop();
|
||||
},
|
||||
confirmTab: () {
|
||||
Navigator.of(context)
|
||||
.pop();
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: const BodyMedium(
|
||||
text: 'Disconnect Device',
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 15,
|
||||
fontColor: ColorsManager
|
||||
.textPrimaryColor,
|
||||
),
|
||||
),
|
||||
const Icon(
|
||||
Icons.keyboard_arrow_right,
|
||||
color: ColorsManager.textGray,
|
||||
)
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return DisconnectWipeData(
|
||||
cancelTab: () {
|
||||
Navigator.of(context)
|
||||
.pop();
|
||||
},
|
||||
confirmTab: () {
|
||||
_bloc.add(
|
||||
DeleteDeviceEvent());
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: const BodyMedium(
|
||||
text:
|
||||
'Disconnect Device and Wipe Data',
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 15,
|
||||
fontColor: ColorsManager
|
||||
.textPrimaryColor,
|
||||
),
|
||||
),
|
||||
const Icon(
|
||||
Icons.keyboard_arrow_right,
|
||||
color: ColorsManager.textGray,
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: const Center(
|
||||
child: BodyMedium(
|
||||
text: 'Remove Device',
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 15,
|
||||
fontColor: ColorsManager.red,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -46,7 +46,7 @@ class SixSceneScreen extends StatelessWidget {
|
||||
model = state.device;
|
||||
}
|
||||
return DefaultScaffold(
|
||||
title: device?.name ?? '6 Scene Switch',
|
||||
title: bloc.deviceInfo.name ,
|
||||
actions: [
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
@ -55,7 +55,7 @@ class SixSceneScreen extends StatelessWidget {
|
||||
builder: (context) => SettingsPage(device: device!),
|
||||
),
|
||||
);
|
||||
if (val == null) {
|
||||
if (val == null || val == true) {
|
||||
bloc.add(const SixSceneInitialInfo());
|
||||
bloc.add(const SixSceneInitial());
|
||||
bloc.add(const SexSceneSwitchInitial());
|
||||
@ -195,8 +195,8 @@ class SixSceneScreen extends StatelessWidget {
|
||||
);
|
||||
if (value == true) {
|
||||
Future.delayed(
|
||||
const Duration(milliseconds: 200),
|
||||
() {
|
||||
const Duration(
|
||||
milliseconds: 200), () {
|
||||
bloc.add(
|
||||
const SexSceneSwitchInitial());
|
||||
});
|
||||
|
@ -95,17 +95,15 @@ class FlushPresenceRecords extends StatelessWidget {
|
||||
SizedBox(
|
||||
child: ListTile(
|
||||
leading: Icon(
|
||||
record.value == 'true'
|
||||
record.value == "presence"
|
||||
? Icons.radio_button_checked
|
||||
: Icons.radio_button_unchecked,
|
||||
color: record.value == 'true'
|
||||
color: record.value == "presence"
|
||||
? Colors.blue
|
||||
: Colors.grey,
|
||||
),
|
||||
title: Text(
|
||||
record.value == 'true'
|
||||
? "Opened"
|
||||
: "Closed",
|
||||
record.value.toString(),
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 18,
|
||||
|
@ -70,7 +70,7 @@ class FlushMountedInterface extends StatelessWidget {
|
||||
SettingsPage(device: deviceModel),
|
||||
),
|
||||
);
|
||||
if (val == null) {
|
||||
if (val == null || val == true) {
|
||||
bloc.add(FlushSensorInitialDeviceInfo());
|
||||
bloc.add(FlushSensorInitialEvent());
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ class FourSceneScreen extends StatelessWidget {
|
||||
}
|
||||
|
||||
return DefaultScaffold(
|
||||
title: device?.name ?? '4 Scene',
|
||||
title: _bloc.deviceInfo.name,
|
||||
actions: [
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
@ -56,7 +56,7 @@ class FourSceneScreen extends StatelessWidget {
|
||||
builder: (context) => SettingsPage(device: device!),
|
||||
),
|
||||
);
|
||||
if (val == null) {
|
||||
if (val == null || val == true) {
|
||||
_bloc.add(const FourSceneInitial());
|
||||
_bloc.add(const FourSceneInitialInfo());
|
||||
_bloc.add(const FourSceneSwitchInitial());
|
||||
@ -164,8 +164,7 @@ class FourSceneScreen extends StatelessWidget {
|
||||
cancelTab: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
confirmTab:
|
||||
(switchSelected) {
|
||||
confirmTab: (switchSelected) {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
|
@ -11,6 +11,9 @@ class NameTimeWidget extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
DateTime now = DateTime.now();
|
||||
DateTime cleaned =
|
||||
DateTime(now.year, now.month, now.day, now.hour, now.minute);
|
||||
return DefaultContainer(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
@ -59,19 +62,22 @@ class NameTimeWidget extends StatelessWidget {
|
||||
width: MediaQuery.of(context).size.width / 3.5,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
|
||||
BlocProvider.of<SmartDoorBloc>(context).add(SelectTimeOnlinePasswordEvent(context: context, isEffective: true));
|
||||
BlocProvider.of<SmartDoorBloc>(context).add(
|
||||
SelectTimeOnlinePasswordEvent(
|
||||
context: context, isEffective: true));
|
||||
},
|
||||
child: Text(
|
||||
BlocProvider.of<SmartDoorBloc>(context).effectiveTime,
|
||||
style: TextStyle(fontSize: 14,
|
||||
color: BlocProvider.of<SmartDoorBloc>(context).effectiveTime ==
|
||||
BlocProvider.of<SmartDoorBloc>(context)
|
||||
.effectiveTime ==
|
||||
'Select Time'
|
||||
? ColorsManager.textGray
|
||||
: null),
|
||||
? cleaned.toString()
|
||||
: BlocProvider.of<SmartDoorBloc>(context)
|
||||
.effectiveTime,
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
)),
|
||||
],),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Divider(
|
||||
color: ColorsManager.graysColor,
|
||||
@ -96,10 +102,13 @@ class NameTimeWidget extends StatelessWidget {
|
||||
context: context, isEffective: false));
|
||||
},
|
||||
child: Text(
|
||||
BlocProvider.of<SmartDoorBloc>(context).expirationTime,
|
||||
BlocProvider.of<SmartDoorBloc>(context)
|
||||
.expirationTime,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: BlocProvider.of<SmartDoorBloc>(context).expirationTime == 'Select Time'
|
||||
color: BlocProvider.of<SmartDoorBloc>(context)
|
||||
.expirationTime ==
|
||||
'Select Time'
|
||||
? ColorsManager.textGray
|
||||
: null),
|
||||
),
|
||||
|
@ -6,10 +6,9 @@ import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_eve
|
||||
import 'package:syncrow_app/features/devices/model/device_model.dart';
|
||||
import 'package:syncrow_app/features/devices/model/smart_door_model.dart';
|
||||
import 'package:syncrow_app/generated/assets.dart';
|
||||
import 'package:syncrow_app/utils/context_extension.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
|
||||
class DoorLockButton extends StatefulWidget {
|
||||
class DoorLockButton extends StatelessWidget {
|
||||
const DoorLockButton({
|
||||
super.key,
|
||||
required this.doorLock,
|
||||
@ -18,154 +17,76 @@ class DoorLockButton extends StatefulWidget {
|
||||
|
||||
final DeviceModel doorLock;
|
||||
final SmartDoorModel smartDoorModel;
|
||||
@override
|
||||
State<DoorLockButton> createState() => _DoorLockButtonState(smartDoorModel: smartDoorModel);
|
||||
}
|
||||
|
||||
class _DoorLockButtonState extends State<DoorLockButton> with SingleTickerProviderStateMixin {
|
||||
late AnimationController _animationController;
|
||||
late Animation<double> _animation;
|
||||
SmartDoorModel smartDoorModel;
|
||||
_DoorLockButtonState({required this.smartDoorModel});
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_animationController = AnimationController(
|
||||
vsync: this,
|
||||
value: context.read<SmartDoorBloc>().unlockRequest > 0 ? 1 : 0,
|
||||
duration: Duration(seconds: context.read<SmartDoorBloc>().unlockRequest),
|
||||
);
|
||||
if (context.read<SmartDoorBloc>().unlockRequest > 0) {
|
||||
_animationController.reverse();
|
||||
}
|
||||
|
||||
_animation = Tween<double>(begin: 0, end: 1).animate(_animationController)
|
||||
..addListener(() {
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(DoorLockButton oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
|
||||
if (_animationController.status == AnimationStatus.dismissed) {
|
||||
if (context.read<SmartDoorBloc>().unlockRequest > 0) {
|
||||
_animationController.value = 1;
|
||||
_animationController.duration =
|
||||
Duration(seconds: context.read<SmartDoorBloc>().unlockRequest);
|
||||
_animationController.reverse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_animationController.dispose();
|
||||
super.dispose();
|
||||
double _calculateProgress() {
|
||||
final value = smartDoorModel.unlockRequest;
|
||||
if (value <= 0 || value > 30) return 0;
|
||||
return value / 30.0;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(
|
||||
right: context.width * 0.25 / 2,
|
||||
left: context.width * 0.25 / 2,
|
||||
bottom: context.width * 0.2 / 2,
|
||||
),
|
||||
final progress = _calculateProgress();
|
||||
final isEnabled = smartDoorModel.unlockRequest > 0;
|
||||
|
||||
return SizedBox(
|
||||
width: 255,
|
||||
height: 255,
|
||||
child: InkWell(
|
||||
overlayColor:
|
||||
WidgetStateProperty.all(ColorsManager.primaryColorWithOpacity.withOpacity(0.1)),
|
||||
borderRadius: BorderRadius.circular(999),
|
||||
onTapDown: (details) {
|
||||
// if (_animationController.status == AnimationStatus.dismissed) {
|
||||
// _animationController.forward();
|
||||
// } else if (_animationController.status == AnimationStatus.completed) {
|
||||
// _animationController.reverse();
|
||||
// } else if (_animationController.status == AnimationStatus.forward) {
|
||||
// _animationController.reverse();
|
||||
// } else if (_animationController.status == AnimationStatus.reverse) {
|
||||
// _animationController.forward();
|
||||
// }
|
||||
if (context.read<SmartDoorBloc>().unlockRequest > 0) {
|
||||
BlocProvider.of<SmartDoorBloc>(context)
|
||||
.add(UpdateLockEvent(value: smartDoorModel.normalOpenSwitch));
|
||||
}
|
||||
},
|
||||
onTapUp: (details) {
|
||||
// if (_animationController.status == AnimationStatus.forward) {
|
||||
// _animationController.reverse();
|
||||
// } else if (_animationController.status == AnimationStatus.reverse) {
|
||||
// _animationController.forward();
|
||||
// }
|
||||
},
|
||||
onTap: isEnabled
|
||||
? () {
|
||||
BlocProvider.of<SmartDoorBloc>(context).add(
|
||||
UpdateLockEvent(value: !smartDoorModel.normalOpenSwitch),
|
||||
);
|
||||
}
|
||||
: null,
|
||||
child: Container(
|
||||
width: context.width * 06,
|
||||
height: context.width * 0.6,
|
||||
margin: const EdgeInsets.all(10),
|
||||
decoration: const BoxDecoration(
|
||||
width: 255,
|
||||
height: 255,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFEBECED),
|
||||
shape: BoxShape.circle,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey,
|
||||
color: Colors.grey.withOpacity(0.5),
|
||||
blurRadius: 18,
|
||||
// offset: Offset(6, 7),
|
||||
blurStyle: BlurStyle.outer,
|
||||
),
|
||||
],
|
||||
color: Color(0xFFEBECED),
|
||||
borderRadius: BorderRadius.all(Radius.circular(999)),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(25),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Container(
|
||||
margin: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(999),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.white.withOpacity(0.5),
|
||||
blurRadius: 30,
|
||||
offset: const Offset(-5, -5),
|
||||
blurStyle: BlurStyle.outer,
|
||||
),
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.14),
|
||||
blurRadius: 25,
|
||||
offset: const Offset(5, 5),
|
||||
blurStyle: BlurStyle.outer,
|
||||
),
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.14),
|
||||
blurRadius: 30,
|
||||
offset: const Offset(5, 5),
|
||||
blurStyle: BlurStyle.inner,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Center(
|
||||
child: SvgPicture.asset(
|
||||
smartDoorModel.normalOpenSwitch
|
||||
? Assets.doorUnlockIcon
|
||||
: Assets.assetsIconsDoorlockAssetsLockIcon,
|
||||
),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Container(
|
||||
margin: const EdgeInsets.all(30),
|
||||
decoration: const BoxDecoration(
|
||||
color: Color(0xFFF9F9F9),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Center(
|
||||
child: SvgPicture.asset(
|
||||
smartDoorModel.normalOpenSwitch
|
||||
? Assets.doorUnlockIcon
|
||||
: Assets.assetsIconsDoorlockAssetsLockIcon,
|
||||
width: 60,
|
||||
height: 60,
|
||||
),
|
||||
),
|
||||
SizedBox.expand(
|
||||
),
|
||||
if (progress > 0)
|
||||
Container(
|
||||
decoration: BoxDecoration(shape: BoxShape.circle),
|
||||
height: 250,
|
||||
width: 250,
|
||||
child: CircularProgressIndicator(
|
||||
value: _animation.value,
|
||||
strokeWidth: 15,
|
||||
value: progress,
|
||||
strokeWidth: 8,
|
||||
backgroundColor: Colors.transparent,
|
||||
valueColor: const AlwaysStoppedAnimation<Color>(ColorsManager.primaryColor),
|
||||
valueColor: const AlwaysStoppedAnimation<Color>(
|
||||
ColorsManager.primaryColor),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -18,7 +18,8 @@ import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
|
||||
class CreateOfflineTimeLimitPasswordPage extends StatelessWidget {
|
||||
final String? deviceId;
|
||||
final String? type;
|
||||
const CreateOfflineTimeLimitPasswordPage({super.key, this.deviceId, this.type});
|
||||
const CreateOfflineTimeLimitPasswordPage(
|
||||
{super.key, this.deviceId, this.type});
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool isRepeat = false;
|
||||
@ -28,9 +29,7 @@ class CreateOfflineTimeLimitPasswordPage extends StatelessWidget {
|
||||
child: BlocConsumer<SmartDoorBloc, SmartDoorState>(
|
||||
listener: (context, state) {
|
||||
if (state is FailedState) {
|
||||
CustomSnackBar.displaySnackBar(
|
||||
state.errorMessage
|
||||
);
|
||||
CustomSnackBar.displaySnackBar(state.errorMessage);
|
||||
}
|
||||
if (state is IsRepeatState) {
|
||||
isRepeat = state.repeat;
|
||||
@ -39,6 +38,10 @@ class CreateOfflineTimeLimitPasswordPage extends StatelessWidget {
|
||||
generated = state.generated;
|
||||
}
|
||||
}, builder: (context, state) {
|
||||
DateTime now = DateTime.now();
|
||||
DateTime cleaned =
|
||||
DateTime(now.year, now.month, now.day, now.hour, now.minute);
|
||||
|
||||
final smartDoorBloc = BlocProvider.of<SmartDoorBloc>(context);
|
||||
return DefaultScaffold(
|
||||
appBar: AppBar(
|
||||
@ -85,46 +88,56 @@ class CreateOfflineTimeLimitPasswordPage extends StatelessWidget {
|
||||
Flexible(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: smartDoorBloc.passwordController.text.isEmpty ?
|
||||
List.generate(10, (index) {
|
||||
return const Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 4.0,
|
||||
vertical: 15),
|
||||
child: Icon(
|
||||
Icons.circle,
|
||||
size: 20.0,
|
||||
color: Colors.black,
|
||||
),
|
||||
);
|
||||
}) : [
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
children: smartDoorBloc
|
||||
.passwordController.text.isEmpty
|
||||
? List.generate(10, (index) {
|
||||
return const Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 4.0,
|
||||
vertical: 15),
|
||||
child: Icon(
|
||||
Icons.circle,
|
||||
size: 20.0,
|
||||
color: Colors.black,
|
||||
),
|
||||
);
|
||||
})
|
||||
: [
|
||||
Expanded(
|
||||
child: BodyLarge(
|
||||
style: const TextStyle(
|
||||
color: ColorsManager.primaryColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
letterSpacing: 8.0,
|
||||
fontSize: 25,
|
||||
wordSpacing: 2),
|
||||
textAlign: TextAlign.center,
|
||||
text: smartDoorBloc.passwordController.text,
|
||||
fontSize: 25,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: BodyLarge(
|
||||
style: const TextStyle(
|
||||
color: ColorsManager
|
||||
.primaryColor,
|
||||
fontWeight:
|
||||
FontWeight.bold,
|
||||
letterSpacing: 8.0,
|
||||
fontSize: 25,
|
||||
wordSpacing: 2),
|
||||
textAlign:
|
||||
TextAlign.center,
|
||||
text: smartDoorBloc
|
||||
.passwordController
|
||||
.text,
|
||||
fontSize: 25,
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
await Clipboard.setData(
|
||||
ClipboardData(
|
||||
text: smartDoorBloc
|
||||
.passwordController
|
||||
.text));
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.copy)),
|
||||
],
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
await Clipboard.setData(
|
||||
ClipboardData(text: smartDoorBloc.passwordController.text)
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.copy)),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
)),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
@ -135,91 +148,142 @@ class CreateOfflineTimeLimitPasswordPage extends StatelessWidget {
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: const BodyMedium(
|
||||
text: 'Password Name',
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width / 2.6,
|
||||
child: TextFormField(
|
||||
controller: BlocProvider.of<SmartDoorBloc>(context).passwordNameController,
|
||||
decoration:
|
||||
const InputDecoration(
|
||||
hintText: 'Enter The Name',
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 14,
|
||||
color: ColorsManager.textGray)
|
||||
),
|
||||
)),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
const Divider(color: ColorsManager.graysColor,),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Expanded(
|
||||
child: BodyMedium(
|
||||
text: 'Effective Time',
|
||||
fontWeight: FontWeight.normal,
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
padding:
|
||||
const EdgeInsets.all(10.0),
|
||||
child: const BodyMedium(
|
||||
text: 'Password Name',
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width / 3.5,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
BlocProvider.of<SmartDoorBloc>(context).add(SelectTimeEvent(context: context, isEffective: true));
|
||||
},
|
||||
child: Text(
|
||||
BlocProvider.of<SmartDoorBloc>(context).effectiveTime,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: BlocProvider.of<SmartDoorBloc>(context).effectiveTime ==
|
||||
'Select Time' ? ColorsManager.textGray : null),
|
||||
),
|
||||
)),],
|
||||
),
|
||||
),
|
||||
const Divider(
|
||||
color: ColorsManager.graysColor,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: Row(mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Expanded(
|
||||
child: BodyMedium(
|
||||
text: 'Expiration Time',
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width / 3.5,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
BlocProvider.of<SmartDoorBloc>(context).add(SelectTimeEvent(
|
||||
context: context,
|
||||
isEffective: false));
|
||||
},
|
||||
child: Text(
|
||||
BlocProvider.of<SmartDoorBloc>(context).expirationTime,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: BlocProvider.of<SmartDoorBloc>(context)
|
||||
.expirationTime == 'Select Time' ? ColorsManager
|
||||
.textGray : null),
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context)
|
||||
.size
|
||||
.width /
|
||||
2.6,
|
||||
child: TextFormField(
|
||||
controller: BlocProvider.of<
|
||||
SmartDoorBloc>(context)
|
||||
.passwordNameController,
|
||||
decoration:
|
||||
const InputDecoration(
|
||||
hintText:
|
||||
'Enter The Name',
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 14,
|
||||
color: ColorsManager
|
||||
.textGray)),
|
||||
)),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
const Divider(
|
||||
color: ColorsManager.graysColor,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.spaceBetween,
|
||||
children: [
|
||||
const Expanded(
|
||||
child: BodyMedium(
|
||||
text: 'Effective Time',
|
||||
fontWeight:
|
||||
FontWeight.normal,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width:
|
||||
MediaQuery.of(context)
|
||||
.size
|
||||
.width /
|
||||
3.5,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
BlocProvider.of<
|
||||
SmartDoorBloc>(
|
||||
context)
|
||||
.add(
|
||||
SelectTimeEvent(
|
||||
context:
|
||||
context,
|
||||
isEffective:
|
||||
true));
|
||||
},
|
||||
child: Text(
|
||||
BlocProvider.of<SmartDoorBloc>(
|
||||
context)
|
||||
.effectiveTime ==
|
||||
'Select Time'
|
||||
? cleaned.toString()
|
||||
: BlocProvider.of<
|
||||
SmartDoorBloc>(
|
||||
context)
|
||||
.effectiveTime,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Divider(
|
||||
color: ColorsManager.graysColor,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.spaceBetween,
|
||||
children: [
|
||||
const Expanded(
|
||||
child: BodyMedium(
|
||||
text: 'Expiration Time',
|
||||
fontWeight:
|
||||
FontWeight.normal,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context)
|
||||
.size
|
||||
.width /
|
||||
3.5,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
BlocProvider.of<
|
||||
SmartDoorBloc>(
|
||||
context)
|
||||
.add(SelectTimeEvent(
|
||||
context: context,
|
||||
isEffective:
|
||||
false));
|
||||
},
|
||||
child: Text(
|
||||
BlocProvider.of<
|
||||
SmartDoorBloc>(
|
||||
context)
|
||||
.expirationTime,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: BlocProvider.of<
|
||||
SmartDoorBloc>(
|
||||
context)
|
||||
.expirationTime ==
|
||||
'Select Time'
|
||||
? ColorsManager
|
||||
.textGray
|
||||
: null),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -238,7 +302,8 @@ class CreateOfflineTimeLimitPasswordPage extends StatelessWidget {
|
||||
),
|
||||
const BodyMedium(
|
||||
textAlign: TextAlign.center,
|
||||
text: 'Use the time-limited password at least once within 24 hours after the password takes effect. Otherwise, the password becomes invalid.',
|
||||
text:
|
||||
'Use the time-limited password at least once within 24 hours after the password takes effect. Otherwise, the password becomes invalid.',
|
||||
fontWeight: FontWeight.normal,
|
||||
fontColor: ColorsManager.grayColor,
|
||||
),
|
||||
@ -256,10 +321,13 @@ class CreateOfflineTimeLimitPasswordPage extends StatelessWidget {
|
||||
backgroundColor: ColorsManager.primaryColor,
|
||||
onPressed: () async {
|
||||
if (generated == false) {
|
||||
smartDoorBloc.add(GenerateAndSavePasswordTimeLimitEvent(context: context));
|
||||
smartDoorBloc.add(
|
||||
GenerateAndSavePasswordTimeLimitEvent(
|
||||
context: context));
|
||||
} else {
|
||||
if(smartDoorBloc.passwordNameController.text.isNotEmpty){
|
||||
smartDoorBloc.add(RenamePasswordEvent());
|
||||
if (smartDoorBloc
|
||||
.passwordNameController.text.isNotEmpty) {
|
||||
smartDoorBloc.add(RenamePasswordEvent());
|
||||
}
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ class SosScreen extends StatelessWidget {
|
||||
builder: (context, state) {
|
||||
final sensor = BlocProvider.of<SosBloc>(context);
|
||||
return DefaultScaffold(
|
||||
title: device?.name,
|
||||
title: sensor.deviceInfo.name,
|
||||
actions: [
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
@ -38,7 +38,7 @@ class SosScreen extends StatelessWidget {
|
||||
builder: (context) => SettingsPage(device: device!),
|
||||
),
|
||||
);
|
||||
if (val == null) {
|
||||
if (val == null || val == true) {
|
||||
sensor.add(SosInitialDeviseInfo());
|
||||
sensor.add(const SosInitial());
|
||||
}
|
||||
@ -72,33 +72,38 @@ class SosScreen extends StatelessWidget {
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: InkWell(
|
||||
overlayColor:
|
||||
WidgetStateProperty.all(Colors.transparent),
|
||||
overlayColor: WidgetStateProperty.all(
|
||||
Colors.transparent),
|
||||
onTap: () {
|
||||
// Add functionality for the main SOS button here
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(890),
|
||||
borderRadius:
|
||||
BorderRadius.circular(890),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.white.withOpacity(0.1),
|
||||
color:
|
||||
Colors.white.withOpacity(0.1),
|
||||
blurRadius: 24,
|
||||
offset: const Offset(-5, -5),
|
||||
blurStyle: BlurStyle.outer,
|
||||
),
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.11),
|
||||
color: Colors.black
|
||||
.withOpacity(0.11),
|
||||
blurRadius: 25,
|
||||
offset: const Offset(5, 5),
|
||||
blurStyle: BlurStyle.outer,
|
||||
),
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.13),
|
||||
color: Colors.black
|
||||
.withOpacity(0.13),
|
||||
blurRadius: 30,
|
||||
offset: const Offset(5, 5),
|
||||
blurStyle: BlurStyle.inner,
|
||||
@ -125,8 +130,9 @@ class SosScreen extends StatelessWidget {
|
||||
onTap: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => SosRecordsScreen(
|
||||
sosId: device!.uuid!),
|
||||
builder: (context) =>
|
||||
SosRecordsScreen(
|
||||
sosId: device!.uuid!),
|
||||
),
|
||||
);
|
||||
},
|
||||
@ -181,8 +187,8 @@ class SosScreen extends StatelessWidget {
|
||||
maxHeight: 46,
|
||||
maxWidth: 50,
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
Assets.doorNotificationSetting),
|
||||
child: SvgPicture.asset(Assets
|
||||
.doorNotificationSetting),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
const Flexible(
|
||||
|
@ -2,6 +2,7 @@ import 'dart:ui';
|
||||
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
|
||||
import 'package:syncrow_app/features/booking_system/presentation/screens/booking_system_page.dart';
|
||||
import 'package:syncrow_app/features/menu/bloc/privacy_policy.dart';
|
||||
import 'package:syncrow_app/features/menu/bloc/user_agreement.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/join_home/join_home_view.dart';
|
||||
@ -44,6 +45,18 @@ class MenuCubit extends Cubit<MenuState> {
|
||||
}
|
||||
|
||||
List<Map<String, Object>> menuSections = [
|
||||
//Booking System
|
||||
{
|
||||
'title': 'Booking System',
|
||||
'color': const Color(0xFF8AB9FF),
|
||||
'buttons': [
|
||||
{
|
||||
'title': 'Booking',
|
||||
'Icon': Assets.assetsIconsMenuBookingSystem,
|
||||
'page': BookingSystemPage()
|
||||
},
|
||||
],
|
||||
},
|
||||
//Home Management
|
||||
{
|
||||
'title': 'Home Management',
|
||||
|
@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
|
||||
import 'package:syncrow_app/features/app_layout/model/community_model.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/manage_home/home_settings.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
|
||||
@ -15,29 +16,37 @@ class ManageHomeView extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
var spaces = HomeCubit.getInstance().spaces;
|
||||
return DefaultScaffold(
|
||||
title: 'Manage Your Home',
|
||||
height: MediaQuery.sizeOf(context).height,
|
||||
title: 'Manage Your Home',
|
||||
child: Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: spaces.isEmpty
|
||||
? const Center(
|
||||
child: BodyMedium(text: 'No spaces found'),
|
||||
)
|
||||
? const Center(child: BodyMedium(text: 'No spaces found'))
|
||||
: DefaultContainer(
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 25),
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 20, vertical: 25),
|
||||
child: ListView.builder(
|
||||
itemCount: spaces.length,
|
||||
itemBuilder: (context, index) {
|
||||
if (index == spaces.length - 1) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
Navigator.of(context).push(CustomPageRoute(
|
||||
builder: (context) => HomeSettingsView(
|
||||
space: spaces[index],
|
||||
)));
|
||||
},
|
||||
child: Row(
|
||||
shrinkWrap: true,
|
||||
itemCount: spaces.length,
|
||||
itemBuilder: (context, index) {
|
||||
final space = spaces[index];
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
Navigator.of(context).push(
|
||||
CustomPageRoute(
|
||||
builder: (context) =>
|
||||
HomeSettingsView(space: space),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
BodyMedium(text: StringHelpers.toTitleCase(spaces[index].name)),
|
||||
BodyMedium(
|
||||
text: StringHelpers.toTitleCase(space.name)),
|
||||
const Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
color: ColorsManager.greyColor,
|
||||
@ -45,45 +54,19 @@ class ManageHomeView extends StatelessWidget {
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
//TODO refactor the routing to use named routes
|
||||
// Navigator.of(context).pushNamed(
|
||||
// '/home_settings',
|
||||
// arguments: spaces[index],
|
||||
// );
|
||||
|
||||
Navigator.of(context).push(CustomPageRoute(
|
||||
builder: (context) => HomeSettingsView(
|
||||
space: spaces[index],
|
||||
)));
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
BodyMedium(text: HomeCubit.getInstance().spaces[index].name),
|
||||
const Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
color: ColorsManager.greyColor,
|
||||
size: 15,
|
||||
)
|
||||
],
|
||||
),
|
||||
if (index != spaces.length - 1)
|
||||
Container(
|
||||
margin: const EdgeInsets.symmetric(vertical: 15),
|
||||
height: 1,
|
||||
color: ColorsManager.greyColor,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
));
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ enum OperationDialogType {
|
||||
temperature,
|
||||
onOff,
|
||||
integerSteps,
|
||||
counterSteps,
|
||||
listOfOptions,
|
||||
none,
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ class FlushFunctionsHelper {
|
||||
operationName: 'Min Detection Distance',
|
||||
code: 'near_detection',
|
||||
functionValue: functionValue,
|
||||
operationDialogType: OperationDialogType.integerSteps,
|
||||
operationDialogType: OperationDialogType.counterSteps,
|
||||
operationalValues: [
|
||||
SceneOperationalValue(
|
||||
icon: '',
|
||||
@ -136,10 +136,10 @@ class FlushFunctionsHelper {
|
||||
operationName: 'Max Detection Distance',
|
||||
code: 'far_detection',
|
||||
functionValue: functionValue,
|
||||
operationDialogType: OperationDialogType.integerSteps,
|
||||
operationDialogType: OperationDialogType.counterSteps,
|
||||
operationalValues: [
|
||||
SceneOperationalValue(
|
||||
icon: '',
|
||||
icon: Assets.assetsCelsiusDegrees,
|
||||
value: 0.0,
|
||||
description: "m",
|
||||
minValue: 0.0,
|
||||
@ -156,23 +156,17 @@ class FlushFunctionsHelper {
|
||||
operationName: 'Trigger Level',
|
||||
code: 'sensi_reduce',
|
||||
functionValue: functionValue,
|
||||
operationDialogType: OperationDialogType.listOfOptions,
|
||||
operationDialogType: OperationDialogType.counterSteps,
|
||||
operationalValues: [
|
||||
SceneOperationalValue(
|
||||
icon: Assets.assetsSensitivityOperationIcon,
|
||||
icon: Assets.assetsCelsiusDegrees,
|
||||
value: 1,
|
||||
description: 1.toString(),
|
||||
),
|
||||
SceneOperationalValue(
|
||||
icon: Assets.assetsSensitivityOperationIcon,
|
||||
value: 2,
|
||||
description: 2.toString(),
|
||||
),
|
||||
SceneOperationalValue(
|
||||
icon: Assets.assetsSensitivityOperationIcon,
|
||||
value: 3,
|
||||
description: 3.toString(),
|
||||
description: "",
|
||||
minValue: 1,
|
||||
maxValue: 3,
|
||||
stepValue: 1,
|
||||
),
|
||||
|
||||
],
|
||||
),
|
||||
SceneStaticFunction(
|
||||
@ -183,23 +177,17 @@ class FlushFunctionsHelper {
|
||||
operationName: 'Indent Level',
|
||||
code: 'occur_dist_reduce',
|
||||
functionValue: functionValue,
|
||||
operationDialogType: OperationDialogType.listOfOptions,
|
||||
operationDialogType: OperationDialogType.counterSteps,
|
||||
operationalValues: [
|
||||
SceneOperationalValue(
|
||||
icon: Assets.assetsSensitivityOperationIcon,
|
||||
icon: Assets.assetsCelsiusDegrees,
|
||||
value: 1,
|
||||
description: 1.toString(),
|
||||
),
|
||||
SceneOperationalValue(
|
||||
icon: Assets.assetsSensitivityOperationIcon,
|
||||
value: 2,
|
||||
description: 2.toString(),
|
||||
),
|
||||
SceneOperationalValue(
|
||||
icon: Assets.assetsSensitivityOperationIcon,
|
||||
value: 3,
|
||||
description: 3.toString(),
|
||||
description: "",
|
||||
minValue: 1,
|
||||
maxValue: 3,
|
||||
stepValue: 1,
|
||||
),
|
||||
|
||||
],
|
||||
),
|
||||
SceneStaticFunction(
|
||||
@ -210,7 +198,7 @@ class FlushFunctionsHelper {
|
||||
operationName: 'Target Confirm Time',
|
||||
code: 'presence_delay',
|
||||
functionValue: functionValue,
|
||||
operationDialogType: OperationDialogType.integerSteps,
|
||||
operationDialogType: OperationDialogType.counterSteps,
|
||||
operationalValues: [
|
||||
SceneOperationalValue(
|
||||
icon: '',
|
||||
@ -235,7 +223,7 @@ class FlushFunctionsHelper {
|
||||
SceneOperationalValue(
|
||||
icon: '',
|
||||
value: 20.0,
|
||||
description: "",
|
||||
description: "sec",
|
||||
minValue: 20.0,
|
||||
maxValue: 300.0,
|
||||
stepValue: 1.0,
|
||||
|
@ -8,6 +8,7 @@ import 'package:syncrow_app/features/scene/model/create_automation_model.dart';
|
||||
import 'package:syncrow_app/features/scene/model/create_scene_model.dart';
|
||||
import 'package:syncrow_app/features/scene/model/scene_static_function.dart';
|
||||
import 'package:syncrow_app/features/scene/widgets/alert_dialogs/alert_dialog_countdown.dart';
|
||||
import 'package:syncrow_app/features/scene/widgets/alert_dialogs/alert_dialog_counter.dart';
|
||||
import 'package:syncrow_app/features/scene/widgets/alert_dialogs/alert_dialog_functions_body.dart';
|
||||
import 'package:syncrow_app/features/scene/widgets/alert_dialogs/alert_dialog_slider_steps.dart';
|
||||
import 'package:syncrow_app/features/scene/widgets/alert_dialogs/alert_dialog_temperature_body.dart';
|
||||
@ -132,7 +133,7 @@ mixin SceneLogicHelper {
|
||||
'presence_delay' ||
|
||||
action.executorProperty!.functionCode == 'none_delay') {
|
||||
action.executorProperty!.functionValue =
|
||||
action.executorProperty!.functionValue * 10;
|
||||
(action.executorProperty!.functionValue * 10).round();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -193,12 +194,12 @@ mixin SceneLogicHelper {
|
||||
if (action.executorProperty!.functionCode == 'near_detection' ||
|
||||
action.executorProperty!.functionCode == 'far_detection') {
|
||||
action.executorProperty!.functionValue =
|
||||
action.executorProperty!.functionValue * 100;
|
||||
(action.executorProperty!.functionValue * 100).round();
|
||||
} else if (action.executorProperty!.functionCode ==
|
||||
'presence_delay' ||
|
||||
action.executorProperty!.functionCode == 'none_delay') {
|
||||
action.executorProperty!.functionValue =
|
||||
action.executorProperty!.functionValue * 10;
|
||||
(action.executorProperty!.functionValue * 10).round();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -234,6 +235,13 @@ mixin SceneLogicHelper {
|
||||
functionValue: functionValue ?? taskItem.functionValue,
|
||||
isAutomation: isAutomation,
|
||||
);
|
||||
} else if (taskItem.operationDialogType ==
|
||||
OperationDialogType.counterSteps) {
|
||||
return AlertDialogCounterSteps(
|
||||
taskItem: taskItem,
|
||||
functionValue: functionValue ?? taskItem.functionValue,
|
||||
isAutomation: isAutomation,
|
||||
);
|
||||
}
|
||||
|
||||
return AlertDialogFunctionsOperationsBody(
|
||||
|
@ -1748,7 +1748,7 @@ mixin SceneOperationsDataHelper {
|
||||
action.deviceName,
|
||||
Assets.flushIcon,
|
||||
'Min Detection distance',
|
||||
OperationDialogType.integerSteps,
|
||||
OperationDialogType.counterSteps,
|
||||
_createMinDetection(),
|
||||
isAutomation,
|
||||
comparator,
|
||||
@ -1763,7 +1763,7 @@ mixin SceneOperationsDataHelper {
|
||||
action.deviceName,
|
||||
Assets.flushIcon,
|
||||
'Max Detection distance',
|
||||
OperationDialogType.integerSteps,
|
||||
OperationDialogType.counterSteps,
|
||||
_createFarDetection(),
|
||||
isAutomation,
|
||||
comparator,
|
||||
@ -1778,7 +1778,7 @@ mixin SceneOperationsDataHelper {
|
||||
action.deviceName,
|
||||
Assets.flushIcon,
|
||||
"Trigger Level",
|
||||
OperationDialogType.listOfOptions,
|
||||
OperationDialogType.counterSteps,
|
||||
_createTriggerLevelFunction(),
|
||||
isAutomation,
|
||||
comparator,
|
||||
@ -1789,19 +1789,12 @@ mixin SceneOperationsDataHelper {
|
||||
List<SceneOperationalValue> _createTriggerLevelFunction() {
|
||||
return [
|
||||
SceneOperationalValue(
|
||||
icon: Assets.assetsSensitivityOperationIcon,
|
||||
description: "1",
|
||||
value: 1,
|
||||
),
|
||||
SceneOperationalValue(
|
||||
icon: Assets.assetsSensitivityOperationIcon,
|
||||
description: "2",
|
||||
value: 2,
|
||||
),
|
||||
SceneOperationalValue(
|
||||
icon: Assets.assetsSensitivityOperationIcon,
|
||||
description: "3",
|
||||
value: 3,
|
||||
icon: Assets.assetsCelsiusDegrees,
|
||||
value: 0,
|
||||
description: '',
|
||||
minValue: 0,
|
||||
maxValue: 3,
|
||||
stepValue: 1,
|
||||
),
|
||||
];
|
||||
}
|
||||
@ -1813,7 +1806,7 @@ mixin SceneOperationsDataHelper {
|
||||
action.deviceName,
|
||||
Assets.flushIcon,
|
||||
'Indent Level',
|
||||
OperationDialogType.listOfOptions,
|
||||
OperationDialogType.counterSteps,
|
||||
_createIndentLevelFunction(),
|
||||
isAutomation,
|
||||
comparator,
|
||||
@ -1824,19 +1817,12 @@ mixin SceneOperationsDataHelper {
|
||||
List<SceneOperationalValue> _createIndentLevelFunction() {
|
||||
return [
|
||||
SceneOperationalValue(
|
||||
icon: Assets.assetsSensitivityOperationIcon,
|
||||
description: "1",
|
||||
value: 1,
|
||||
),
|
||||
SceneOperationalValue(
|
||||
icon: Assets.assetsSensitivityOperationIcon,
|
||||
description: "2",
|
||||
value: 2,
|
||||
),
|
||||
SceneOperationalValue(
|
||||
icon: Assets.assetsSensitivityOperationIcon,
|
||||
description: "3",
|
||||
value: 3,
|
||||
icon: Assets.assetsCelsiusDegrees,
|
||||
value: 0,
|
||||
description: '',
|
||||
minValue: 0,
|
||||
maxValue: 3,
|
||||
stepValue: 1,
|
||||
),
|
||||
];
|
||||
}
|
||||
@ -1848,7 +1834,7 @@ mixin SceneOperationsDataHelper {
|
||||
action.deviceName,
|
||||
Assets.flushIcon,
|
||||
'Target Confirm Time',
|
||||
OperationDialogType.integerSteps,
|
||||
OperationDialogType.counterSteps,
|
||||
_targetConfirmTimeFun(),
|
||||
isAutomation,
|
||||
comparator,
|
||||
@ -1876,7 +1862,7 @@ mixin SceneOperationsDataHelper {
|
||||
SceneOperationalValue(
|
||||
icon: Assets.flushIcon,
|
||||
value: 0.0,
|
||||
description: '',
|
||||
description: 'sec',
|
||||
minValue: 20,
|
||||
maxValue: 300,
|
||||
stepValue: 1,
|
||||
@ -1892,7 +1878,7 @@ mixin SceneOperationsDataHelper {
|
||||
value: 0.0,
|
||||
minValue: 0.0,
|
||||
maxValue: 9.5,
|
||||
stepValue: 1,
|
||||
stepValue: 0.10,
|
||||
),
|
||||
];
|
||||
}
|
||||
@ -1905,7 +1891,7 @@ mixin SceneOperationsDataHelper {
|
||||
value: 0.0,
|
||||
minValue: 0.0,
|
||||
maxValue: 9.5,
|
||||
stepValue: 1,
|
||||
stepValue: 0.10,
|
||||
),
|
||||
];
|
||||
}
|
||||
@ -1915,7 +1901,7 @@ mixin SceneOperationsDataHelper {
|
||||
SceneOperationalValue(
|
||||
icon: Assets.assetsCelsiusDegrees,
|
||||
value: 0.0,
|
||||
description: '',
|
||||
description: 'sec',
|
||||
minValue: 0.0,
|
||||
maxValue: 0.5,
|
||||
stepValue: 0.1,
|
||||
|
@ -90,44 +90,25 @@ class Action {
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
static Action? fromJson(Map<String, dynamic> json) {
|
||||
// Safely extract required fields with null checks
|
||||
final String? actionExecutor = json["actionExecutor"] as String?;
|
||||
final String? entityId = json["entityId"] as String?;
|
||||
final String? productType = json['productType'] as String?;
|
||||
final String? deviceName = json['deviceName'] as String?;
|
||||
|
||||
// Skip invalid actions with missing required fields
|
||||
if (actionExecutor == null ||
|
||||
entityId == null ||
|
||||
productType == null ||
|
||||
deviceName == null) {
|
||||
|
||||
if (actionExecutor == null || entityId == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Handle actions with 'name' and 'type'
|
||||
if (json['name'] != null && json['type'] != null) {
|
||||
return Action(
|
||||
actionExecutor: actionExecutor,
|
||||
entityId: entityId,
|
||||
name: json['name'] as String?,
|
||||
type: json['type'] as String?,
|
||||
productType: productType,
|
||||
deviceName: deviceName,
|
||||
);
|
||||
}
|
||||
|
||||
// Handle actions with 'executorProperty'
|
||||
if (json["executorProperty"] != null) {
|
||||
return Action(
|
||||
actionExecutor: actionExecutor,
|
||||
entityId: entityId,
|
||||
executorProperty: ExecutorProperty.fromJson(json["executorProperty"]),
|
||||
productType: productType,
|
||||
deviceName: deviceName,
|
||||
);
|
||||
}
|
||||
|
||||
return null; // Skip invalid actions
|
||||
return Action(
|
||||
actionExecutor: actionExecutor,
|
||||
entityId: entityId,
|
||||
executorProperty: json["executorProperty"] != null
|
||||
? ExecutorProperty.fromJson(json["executorProperty"])
|
||||
: null,
|
||||
name: json['name'] as String?,
|
||||
type: json['type'] as String?,
|
||||
productType: json['productType'] as String? ?? 'unknown',
|
||||
deviceName: json['deviceName'] as String? ?? 'Delay Action',
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
|
@ -27,12 +27,15 @@ class RoutinesView extends StatelessWidget {
|
||||
builder: (context, state) {
|
||||
final selectedSpace = HomeCubit.getInstance().selectedSpace;
|
||||
if (state is DeleteSceneSuccess) {
|
||||
if (state.success) _loadScenesAndAutomations(context, selectedSpace);
|
||||
if (state.success)
|
||||
_loadScenesAndAutomations(context, selectedSpace);
|
||||
}
|
||||
if (state is CreateSceneWithTasks) {
|
||||
if (state.success) {
|
||||
_loadScenesAndAutomations(context, selectedSpace);
|
||||
context.read<SmartSceneSelectBloc>().add(const SmartSceneClearEvent());
|
||||
context
|
||||
.read<SmartSceneSelectBloc>()
|
||||
.add(const SmartSceneClearEvent());
|
||||
}
|
||||
}
|
||||
return BlocListener<SceneBloc, SceneState>(
|
||||
@ -72,30 +75,28 @@ class RoutinesView extends StatelessWidget {
|
||||
data: Theme.of(context).copyWith(
|
||||
dividerColor: Colors.transparent,
|
||||
),
|
||||
child: Expanded(
|
||||
child: ListView(
|
||||
children: [
|
||||
RoutinesExpansionTile(
|
||||
title: 'Tap to run routines',
|
||||
emptyRoutinesMessage:
|
||||
'No scenes have been added yet',
|
||||
routines: state.scenes,
|
||||
loadingStates: state.loadingStates,
|
||||
loadingSceneId: state.loadingSceneId,
|
||||
disablePlayButton: false,
|
||||
),
|
||||
RoutinesExpansionTile(
|
||||
title: 'Automation',
|
||||
emptyRoutinesMessage:
|
||||
'No automations have been added yet',
|
||||
routines: state.automationList,
|
||||
loadingStates: state.loadingStates,
|
||||
loadingSceneId: state.loadingSceneId,
|
||||
disablePlayButton: true,
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
],
|
||||
),
|
||||
child: ListView(
|
||||
children: [
|
||||
RoutinesExpansionTile(
|
||||
title: 'Tap to run automations',
|
||||
emptyRoutinesMessage:
|
||||
'No scenes have been added yet',
|
||||
routines: state.scenes,
|
||||
loadingStates: state.loadingStates,
|
||||
loadingSceneId: state.loadingSceneId,
|
||||
disablePlayButton: false,
|
||||
),
|
||||
RoutinesExpansionTile(
|
||||
title: 'Automation',
|
||||
emptyRoutinesMessage:
|
||||
'No automations have been added yet',
|
||||
routines: state.automationList,
|
||||
loadingStates: state.loadingStates,
|
||||
loadingSceneId: state.loadingSceneId,
|
||||
disablePlayButton: true,
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -111,7 +112,8 @@ class RoutinesView extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
void _loadScenesAndAutomations(BuildContext context, SpaceModel? selectedSpace) {
|
||||
void _loadScenesAndAutomations(
|
||||
BuildContext context, SpaceModel? selectedSpace) {
|
||||
context.read<SceneBloc>()
|
||||
..add(LoadScenes(selectedSpace!.id, selectedSpace, showInDevice: false))
|
||||
..add(LoadAutomation(selectedSpace.id, selectedSpace.community.uuid));
|
||||
|
@ -100,7 +100,7 @@ class SceneView extends StatelessWidget {
|
||||
initiallyExpanded: true,
|
||||
iconColor: ColorsManager.grayColor,
|
||||
title: const BodyMedium(
|
||||
text: 'Tap to run routines',
|
||||
text: 'Tap to run automations',
|
||||
),
|
||||
children: [
|
||||
if (scenes.isNotEmpty)
|
||||
|
@ -0,0 +1,243 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart';
|
||||
import 'package:syncrow_app/features/scene/model/scene_static_function.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/title_medium.dart';
|
||||
import 'package:syncrow_app/utils/context_extension.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
|
||||
class AlertDialogCounterSteps extends StatefulWidget {
|
||||
const AlertDialogCounterSteps({
|
||||
super.key,
|
||||
this.functionValue,
|
||||
required this.taskItem,
|
||||
required this.isAutomation,
|
||||
});
|
||||
|
||||
final dynamic functionValue;
|
||||
final SceneStaticFunction taskItem;
|
||||
final bool isAutomation;
|
||||
|
||||
@override
|
||||
State<AlertDialogCounterSteps> createState() =>
|
||||
_AlertDialogCounterStepsState();
|
||||
}
|
||||
|
||||
class _AlertDialogCounterStepsState extends State<AlertDialogCounterSteps> {
|
||||
double? groupValue;
|
||||
int selectedToggleIndex = 1;
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
final createSceneBloc = context.read<CreateSceneBloc>();
|
||||
|
||||
if (widget.taskItem.comparator != null) {
|
||||
selectedToggleIndex = _comparatorToIndex(widget.taskItem.comparator);
|
||||
}
|
||||
|
||||
if (widget.isAutomation) {
|
||||
final automationTempTaskList = createSceneBloc.automationTempTasksList;
|
||||
final automationComparatorValues =
|
||||
createSceneBloc.automationComparatorValues;
|
||||
|
||||
for (var element in automationTempTaskList) {
|
||||
if (element.code == widget.taskItem.code) {
|
||||
groupValue = element.functionValue;
|
||||
selectedToggleIndex =
|
||||
_comparatorToIndex(automationComparatorValues[element.code]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (widget.taskItem.code == 'temp_current') {
|
||||
groupValue = widget.functionValue != null
|
||||
? _normalizeValue(
|
||||
double.tryParse(widget.functionValue.toString()) ??
|
||||
widget.taskItem.operationalValues[0].minValue,
|
||||
)
|
||||
: widget.taskItem.operationalValues[0].minValue;
|
||||
} else {
|
||||
groupValue = widget.functionValue != null
|
||||
? _normalizeValue(widget.functionValue)
|
||||
: _normalizeValue(widget.taskItem.operationalValues[0].minValue);
|
||||
}
|
||||
|
||||
setState(() {});
|
||||
|
||||
context.read<CreateSceneBloc>().add(
|
||||
SelectedValueEvent(
|
||||
value: _deNormalizeValue(groupValue),
|
||||
code: widget.taskItem.code,
|
||||
isAutomation: widget.isAutomation,
|
||||
comparator: _indexToComparator(selectedToggleIndex),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
int _comparatorToIndex(String? comparator) {
|
||||
switch (comparator) {
|
||||
case "<":
|
||||
return 0;
|
||||
case "==":
|
||||
return 1;
|
||||
case ">":
|
||||
return 2;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
String _indexToComparator(int index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return "<";
|
||||
case 1:
|
||||
return "==";
|
||||
case 2:
|
||||
return ">";
|
||||
default:
|
||||
return "==";
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (widget.isAutomation)
|
||||
ToggleButtons(
|
||||
isSelected: [
|
||||
selectedToggleIndex == 0,
|
||||
selectedToggleIndex == 1,
|
||||
selectedToggleIndex == 2,
|
||||
],
|
||||
onPressed: (index) {
|
||||
setState(() {
|
||||
selectedToggleIndex = index;
|
||||
});
|
||||
context.read<CreateSceneBloc>().add(
|
||||
SelectedValueEvent(
|
||||
value: _deNormalizeValue(groupValue),
|
||||
code: widget.taskItem.code,
|
||||
isAutomation: widget.isAutomation,
|
||||
comparator: _indexToComparator(selectedToggleIndex),
|
||||
),
|
||||
);
|
||||
},
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
selectedColor: Colors.white,
|
||||
color: ColorsManager.blackColor,
|
||||
fillColor: ColorsManager.primaryColorWithOpacity,
|
||||
borderColor: ColorsManager.greyColor,
|
||||
constraints: BoxConstraints.tight(Size(70, 30)),
|
||||
children: const [
|
||||
SizedBox(width: 70, height: 30, child: Center(child: Text("<"))),
|
||||
SizedBox(width: 70, height: 30, child: Center(child: Text("="))),
|
||||
SizedBox(width: 70, height: 30, child: Center(child: Text(">"))),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
...widget.taskItem.operationalValues.map(
|
||||
(operation) => BlocBuilder<CreateSceneBloc, CreateSceneState>(
|
||||
builder: (context, state) {
|
||||
final step = operation.stepValue?.toDouble() ?? 1.0;
|
||||
final min = operation.minValue?.toDouble() ?? 0.0;
|
||||
final max = operation.maxValue?.toDouble() ?? 100.0;
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
const SizedBox(height: 12),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.remove),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
groupValue =
|
||||
((groupValue ?? min) - step).clamp(min, max);
|
||||
});
|
||||
context.read<CreateSceneBloc>().add(
|
||||
SelectedValueEvent(
|
||||
value: _deNormalizeValue(groupValue),
|
||||
code: widget.taskItem.code,
|
||||
isAutomation: widget.isAutomation,
|
||||
comparator:
|
||||
_indexToComparator(selectedToggleIndex),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
TitleMedium(
|
||||
text: groupValue != null
|
||||
? (groupValue! % 1 == 0
|
||||
? groupValue!.toStringAsFixed(0)
|
||||
: groupValue!.toStringAsFixed(1))
|
||||
: "0",
|
||||
style: context.titleMedium.copyWith(
|
||||
color: ColorsManager.primaryColorWithOpacity,
|
||||
fontSize: 30,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
TitleMedium(
|
||||
text: operation.description.toString(),
|
||||
style: context.titleMedium.copyWith(
|
||||
color: ColorsManager.primaryColorWithOpacity,
|
||||
fontSize: 30,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.add),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
groupValue =
|
||||
((groupValue ?? 0) + step).clamp(min, max);
|
||||
});
|
||||
context.read<CreateSceneBloc>().add(
|
||||
SelectedValueEvent(
|
||||
value: _deNormalizeValue(groupValue),
|
||||
code: widget.taskItem.code,
|
||||
isAutomation: widget.isAutomation,
|
||||
comparator:
|
||||
_indexToComparator(selectedToggleIndex),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
double _normalizeValue(dynamic value) {
|
||||
if ((widget.taskItem.code == "temp_set" && value > 199) ||
|
||||
(widget.taskItem.code == "temp_current" && value >= -99.0)) {
|
||||
return (value) / 10;
|
||||
}
|
||||
return value.toDouble();
|
||||
}
|
||||
|
||||
double _deNormalizeValue(double? value) {
|
||||
if (widget.taskItem.code == "temp_set" ||
|
||||
widget.taskItem.code == "temp_current") {
|
||||
return (value ?? 0) * 10;
|
||||
}
|
||||
return value ?? 0;
|
||||
}
|
||||
}
|
@ -23,7 +23,7 @@ class DeleteRoutineDialog extends StatelessWidget {
|
||||
height: 10,
|
||||
),
|
||||
const BodyLarge(
|
||||
text: 'Delete Routine',
|
||||
text: 'Delete Automation',
|
||||
fontWeight: FontWeight.w700,
|
||||
fontColor: ColorsManager.red,
|
||||
fontSize: 16,
|
||||
@ -39,7 +39,7 @@ class DeleteRoutineDialog extends StatelessWidget {
|
||||
child: Column(
|
||||
children: [
|
||||
Center(child: const Text('Are you sure you want to ')),
|
||||
Center(child: const Text('delete the routine?'))
|
||||
Center(child: const Text('delete the automation?'))
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -52,7 +52,7 @@ class DeleteRoutineButton extends StatelessWidget {
|
||||
},
|
||||
child: const Center(
|
||||
child: Text(
|
||||
'Remove Routine',
|
||||
'Remove Automation',
|
||||
style: TextStyle(color: ColorsManager.red),
|
||||
))
|
||||
// : SceneListTile(
|
||||
|
@ -9,7 +9,7 @@ class EmptyDevicesWidget extends StatelessWidget {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 48),
|
||||
child: Text(
|
||||
"No routines.\nEnable 'Show on Home Screen' to add routines",
|
||||
"No automations.\nEnable 'Show on Home Screen' to add automations",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: ColorsManager.grayColor,
|
||||
|
@ -23,7 +23,7 @@ class EmptyRoutinesWidget extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
BodyMedium(
|
||||
text: 'No Routines yet',
|
||||
text: 'No automations yet',
|
||||
fontColor: ColorsManager.textGray,
|
||||
),
|
||||
],
|
||||
|
@ -495,6 +495,7 @@ class Assets {
|
||||
/// assets/icons/MenuIcons/HomeManagementIcons/CreateHome.svg
|
||||
static const String assetsIconsMenuIconsHomeManagementIconsCreateHome =
|
||||
"assets/icons/MenuIcons/HomeManagementIcons/CreateHome.svg";
|
||||
static const String assetsIconsMenuBookingSystem = 'assets/icons/Booking.svg';
|
||||
|
||||
/// Assets for assetsIconsMenuIconsHomeManagementIconsJoinAHome
|
||||
/// assets/icons/MenuIcons/HomeManagementIcons/joinAHome.svg
|
||||
|
@ -10,7 +10,7 @@ import 'package:syncrow_app/utils/helpers/localization_helpers.dart';
|
||||
import 'my_app.dart';
|
||||
|
||||
|
||||
const String buildNumber = '1.0.30+17';
|
||||
const String buildNumber = '1.0.30+18';
|
||||
|
||||
void main() {
|
||||
//to observe the state of the blocs in the output console
|
||||
|
@ -234,4 +234,7 @@ abstract class ApiEndpoints {
|
||||
'/projects/{projectUuid}/communities/{communityUuid}/spaces/{spaceUuid}/devices';
|
||||
|
||||
static const String getReportLogs = '/devices/{uuid}/report-logs?code={code}';
|
||||
|
||||
//booking System APIs
|
||||
static const String upcomingBookings = '/bookings/{uuid}/';
|
||||
}
|
||||
|
4
lib/utils/helpers/app_size.dart
Normal file
4
lib/utils/helpers/app_size.dart
Normal file
@ -0,0 +1,4 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
double deviceHeight(BuildContext context) => MediaQuery.sizeOf(context).height;
|
||||
double deviceWidth(BuildContext context) => MediaQuery.sizeOf(context).width;
|
@ -4,8 +4,8 @@ class StringsManager {
|
||||
|
||||
static const String dashboard = 'Dashboard';
|
||||
static const String devices = 'Devices';
|
||||
static const String routine = 'Routines';
|
||||
static const String tapToRunRoutine = 'Tap to run routine';
|
||||
static const String routine = 'Automation';
|
||||
static const String tapToRunRoutine = 'Tap to run automation';
|
||||
static const String wizard = 'Wizard';
|
||||
static const String active = 'Active';
|
||||
static const String current = 'Current';
|
||||
@ -39,6 +39,6 @@ class StringsManager {
|
||||
'Example: when an unusual activity is detected.';
|
||||
static const String functions = "Functions";
|
||||
static const String firstLaunch = "firstLaunch";
|
||||
static const String deleteScene = 'Remove Routine';
|
||||
static const String deleteScene = 'Remove Automation';
|
||||
static const String deleteAutomation = 'Delete Automation';
|
||||
}
|
||||
|
@ -207,7 +207,7 @@ abstract class ThemeManager {
|
||||
),
|
||||
|
||||
///card theme
|
||||
cardTheme: const CardTheme(
|
||||
cardTheme: const CardThemeData(
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(20)),
|
||||
@ -372,7 +372,7 @@ abstract class ThemeManager {
|
||||
|
||||
///card theme
|
||||
//TODO edit card theme
|
||||
cardTheme: const CardTheme(
|
||||
cardTheme: const CardThemeData(
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(20)),
|
||||
|
@ -5,7 +5,7 @@ description: This is the mobile application project, developed with Flutter for
|
||||
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||
publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
||||
|
||||
version: 1.0.30+16
|
||||
version: 1.0.30+18
|
||||
|
||||
environment:
|
||||
sdk: ">=3.0.6 <4.0.0"
|
||||
|
Reference in New Issue
Block a user