diff --git a/android/app/build.gradle b/android/app/build.gradle index 9dc6887d..8de28232 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -13,12 +13,12 @@ if (flutterRoot == null) { def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { - flutterVersionCode = '21' + flutterVersionCode = '22' } def flutterVersionName = localProperties.getProperty('flutter.versionName') if (flutterVersionName == null) { - flutterVersionName = '3.3.7' + flutterVersionName = '3.4.0' } def keystoreProperties = new Properties() diff --git a/assets/fonts/Lotura.ttf b/assets/fonts/Lotura.ttf index 147e6c60..8a214596 100644 Binary files a/assets/fonts/Lotura.ttf and b/assets/fonts/Lotura.ttf differ diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 494f79ad..e12dde01 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -126,4 +126,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 3765b805c03121794c6d132546df9366f592bc4d -COCOAPODS: 1.13.0 +COCOAPODS: 1.15.2 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 8cadf561..87f407ff 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -376,7 +376,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 3.3.7; + MARKETING_VERSION = 3.4.0; PRODUCT_BUNDLE_IDENTIFIER = com.osj.lotura; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; @@ -516,7 +516,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 3.3.7; + MARKETING_VERSION = 3.4.0; PRODUCT_BUNDLE_IDENTIFIER = com.osj.lotura; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; @@ -546,7 +546,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 3.3.7; + MARKETING_VERSION = 3.4.0; PRODUCT_BUNDLE_IDENTIFIER = com.osj.lotura; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; diff --git a/lib/data/data_source/apply/remote/remote_apply_data_source.dart b/lib/data/apply/data_source/remote/remote_apply_data_source.dart similarity index 70% rename from lib/data/data_source/apply/remote/remote_apply_data_source.dart rename to lib/data/apply/data_source/remote/remote_apply_data_source.dart index dde89d29..6b36fa8f 100644 --- a/lib/data/data_source/apply/remote/remote_apply_data_source.dart +++ b/lib/data/apply/data_source/remote/remote_apply_data_source.dart @@ -3,10 +3,10 @@ import 'dart:convert'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:http/http.dart' as http; -import 'package:lotura/data/dto/request/apply_cancel_request.dart'; -import 'package:lotura/data/dto/request/send_fcm_info_request.dart'; -import 'package:lotura/data/dto/response/apply_response.dart'; -import 'package:lotura/domain/entity/apply_entity.dart'; +import 'package:lotura/data/apply/dto/request/apply_cancel_request.dart'; +import 'package:lotura/data/apply/dto/request/send_fcm_info_request.dart'; +import 'package:lotura/data/apply/dto/response/apply_response.dart'; +import 'package:lotura/domain/apply/entity/apply_entity.dart'; import 'package:lotura/secret.dart'; class RemoteApplyDataSource { @@ -26,20 +26,19 @@ class RemoteApplyDataSource { {required SendFCMInfoRequest sendFCMInfoRequest}) async { sendFCMInfoRequest.token = await _getToken(); final response = await http.post(Uri.parse("$baseurl/push_request"), - body: sendFCMInfoRequest.toJson()); + headers: {"Content-Type": "application/json"}, + body: json.encode(sendFCMInfoRequest.toJson())); if (response.statusCode != 200 && response.statusCode != 304) { throw Exception(response.body); } } - Future> applyCancel( + Future applyCancel( {required ApplyCancelRequest applyCancelRequest}) async { applyCancelRequest.token = await _getToken(); final response = await http.post(Uri.parse("$baseurl/push_cancel"), - body: applyCancelRequest.toJson()); + headers: {"Content-Type": "application/json"}, + body: json.encode(applyCancelRequest.toJson())); if (response.statusCode != 200) throw Exception(response.body); - return (jsonDecode(response.body) as List) - .map((i) => ApplyResponse.fromJson(i).toEntity()) - .toList(); } } diff --git a/lib/data/dto/request/apply_cancel_request.dart b/lib/data/apply/dto/request/apply_cancel_request.dart similarity index 93% rename from lib/data/dto/request/apply_cancel_request.dart rename to lib/data/apply/dto/request/apply_cancel_request.dart index ac86a783..fce99b0c 100644 --- a/lib/data/dto/request/apply_cancel_request.dart +++ b/lib/data/apply/dto/request/apply_cancel_request.dart @@ -1,6 +1,6 @@ class ApplyCancelRequest { String? token; - String deviceId; + int deviceId; ApplyCancelRequest({this.token, required this.deviceId}); diff --git a/lib/data/dto/request/apply_request.dart b/lib/data/apply/dto/request/apply_request.dart similarity index 100% rename from lib/data/dto/request/apply_request.dart rename to lib/data/apply/dto/request/apply_request.dart diff --git a/lib/data/dto/request/get_apply_list_request.dart b/lib/data/apply/dto/request/get_apply_list_request.dart similarity index 100% rename from lib/data/dto/request/get_apply_list_request.dart rename to lib/data/apply/dto/request/get_apply_list_request.dart diff --git a/lib/data/dto/request/send_fcm_info_request.dart b/lib/data/apply/dto/request/send_fcm_info_request.dart similarity index 89% rename from lib/data/dto/request/send_fcm_info_request.dart rename to lib/data/apply/dto/request/send_fcm_info_request.dart index 327d00e9..d9ecbcb8 100644 --- a/lib/data/dto/request/send_fcm_info_request.dart +++ b/lib/data/apply/dto/request/send_fcm_info_request.dart @@ -1,7 +1,7 @@ class SendFCMInfoRequest { String? token; - String deviceId; - String expectState; + int deviceId; + int expectState; SendFCMInfoRequest( {this.token, required this.deviceId, required this.expectState}); diff --git a/lib/data/dto/response/apply_response.dart b/lib/data/apply/dto/response/apply_response.dart similarity index 56% rename from lib/data/dto/response/apply_response.dart rename to lib/data/apply/dto/response/apply_response.dart index b35704d5..12e5b933 100644 --- a/lib/data/dto/response/apply_response.dart +++ b/lib/data/apply/dto/response/apply_response.dart @@ -1,4 +1,5 @@ -import 'package:lotura/domain/entity/apply_entity.dart'; +import 'package:lotura/domain/apply/entity/apply_entity.dart'; +import 'package:lotura/main.dart'; class ApplyResponse { final int deviceId; @@ -13,6 +14,7 @@ class ApplyResponse { ); } - ApplyEntity toEntity() => - ApplyEntity(deviceId: deviceId, deviceType: deviceType); + ApplyEntity toEntity() => ApplyEntity( + deviceId: deviceId, + deviceType: deviceType == "WASH" ? DeviceType.wash : DeviceType.dry); } diff --git a/lib/data/apply/repository/apply_repository_impl.dart b/lib/data/apply/repository/apply_repository_impl.dart new file mode 100644 index 00000000..6956969c --- /dev/null +++ b/lib/data/apply/repository/apply_repository_impl.dart @@ -0,0 +1,28 @@ +import 'dart:async'; + +import 'package:lotura/data/apply/data_source/remote/remote_apply_data_source.dart'; +import 'package:lotura/data/apply/dto/request/apply_cancel_request.dart'; +import 'package:lotura/data/apply/dto/request/send_fcm_info_request.dart'; +import 'package:lotura/domain/apply/entity/apply_entity.dart'; +import 'package:lotura/domain/apply/repository/apply_repository.dart'; + +class ApplyRepositoryImpl implements ApplyRepository { + final RemoteApplyDataSource _remoteApplyDataSource; + + ApplyRepositoryImpl({required RemoteApplyDataSource remoteApplyDataSource}) + : _remoteApplyDataSource = remoteApplyDataSource; + + @override + Future> getApplyList() => + _remoteApplyDataSource.getApplyList(); + + @override + Future sendFCMInfo({required SendFCMInfoRequest sendFCMInfoRequest}) => + _remoteApplyDataSource.sendFCMInfo( + sendFCMInfoRequest: sendFCMInfoRequest); + + @override + Future applyCancel({required ApplyCancelRequest applyCancelRequest}) => + _remoteApplyDataSource.applyCancel( + applyCancelRequest: applyCancelRequest); +} diff --git a/lib/data/data_source/laundry/local/local_laundry_data_source.dart b/lib/data/laundry/data_source/local/local_laundry_data_source.dart similarity index 100% rename from lib/data/data_source/laundry/local/local_laundry_data_source.dart rename to lib/data/laundry/data_source/local/local_laundry_data_source.dart diff --git a/lib/data/data_source/laundry/remote/remote_laundry_data_source.dart b/lib/data/laundry/data_source/remote/remote_laundry_data_source.dart similarity index 89% rename from lib/data/data_source/laundry/remote/remote_laundry_data_source.dart rename to lib/data/laundry/data_source/remote/remote_laundry_data_source.dart index 0a96aa57..c4db5ee6 100644 --- a/lib/data/data_source/laundry/remote/remote_laundry_data_source.dart +++ b/lib/data/laundry/data_source/remote/remote_laundry_data_source.dart @@ -2,8 +2,8 @@ import 'dart:async'; import 'dart:convert'; import 'package:http/http.dart' as http; -import 'package:lotura/data/dto/response/laundry_response.dart'; -import 'package:lotura/domain/entity/laundry_entity.dart'; +import 'package:lotura/data/laundry/dto/response/laundry_response.dart'; +import 'package:lotura/domain/laundry/entity/laundry_entity.dart'; import 'package:lotura/secret.dart'; import 'package:web_socket_channel/web_socket_channel.dart'; diff --git a/lib/data/dto/response/laundry_response.dart b/lib/data/laundry/dto/response/laundry_response.dart similarity index 51% rename from lib/data/dto/response/laundry_response.dart rename to lib/data/laundry/dto/response/laundry_response.dart index 5f48e60f..df0364e8 100644 --- a/lib/data/dto/response/laundry_response.dart +++ b/lib/data/laundry/dto/response/laundry_response.dart @@ -1,12 +1,16 @@ -import 'package:lotura/domain/entity/laundry_entity.dart'; +import 'package:lotura/domain/laundry/entity/laundry_entity.dart'; +import 'package:lotura/main.dart'; class LaundryResponse { final int id; final int state; final String deviceType; - LaundryResponse( - {required this.id, required this.state, required this.deviceType}); + const LaundryResponse({ + required this.id, + required this.state, + required this.deviceType, + }); factory LaundryResponse.fromJson(Map json) { return LaundryResponse( @@ -19,8 +23,8 @@ class LaundryResponse { LaundryEntity toEntity() { return LaundryEntity( id: id, - state: state, - deviceType: deviceType, + state: CurrentState.values.elementAt(state), + deviceType: deviceType == "WASH" ? DeviceType.wash : DeviceType.dry, ); } } diff --git a/lib/data/repository/laundry_repository_impl.dart b/lib/data/laundry/repository/laundry_repository_impl.dart similarity index 80% rename from lib/data/repository/laundry_repository_impl.dart rename to lib/data/laundry/repository/laundry_repository_impl.dart index ba13ff80..c5f48c29 100644 --- a/lib/data/repository/laundry_repository_impl.dart +++ b/lib/data/laundry/repository/laundry_repository_impl.dart @@ -1,9 +1,9 @@ import 'dart:async'; -import 'package:lotura/data/data_source/laundry/local/local_laundry_data_source.dart'; -import 'package:lotura/data/data_source/laundry/remote/remote_laundry_data_source.dart'; -import 'package:lotura/domain/entity/laundry_entity.dart'; -import 'package:lotura/domain/repository/laundry_repository.dart'; +import 'package:lotura/data/laundry/data_source/local/local_laundry_data_source.dart'; +import 'package:lotura/data/laundry/data_source/remote/remote_laundry_data_source.dart'; +import 'package:lotura/domain/laundry/entity/laundry_entity.dart'; +import 'package:lotura/domain/laundry/repository/laundry_repository.dart'; class LaundryRepositoryImpl implements LaundryRepository { final LocalLaundryDataSource _localLaundryDataSource; diff --git a/lib/data/notice/data_source/local/local_notice_data_source.dart b/lib/data/notice/data_source/local/local_notice_data_source.dart new file mode 100644 index 00000000..2484f444 --- /dev/null +++ b/lib/data/notice/data_source/local/local_notice_data_source.dart @@ -0,0 +1,12 @@ +import 'package:hive/hive.dart'; + +class LocalNoticeDataSource { + final Box _box; + + const LocalNoticeDataSource({required Box box}) : _box = box; + + Future setValue({required String key, required int value}) => + _box.put(key, value); + + int? getValue({required String key}) => _box.get(key); +} diff --git a/lib/data/notice/data_source/remote/remote_notice_data_source.dart b/lib/data/notice/data_source/remote/remote_notice_data_source.dart new file mode 100644 index 00000000..f66931b4 --- /dev/null +++ b/lib/data/notice/data_source/remote/remote_notice_data_source.dart @@ -0,0 +1,19 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:http/http.dart' as http; +import 'package:lotura/data/notice/dto/response/notice_response.dart'; +import 'package:lotura/domain/notice/entity/notice_entity.dart'; +import 'package:lotura/secret.dart'; + +class RemoteNoticeDataSource { + Future> getNotice() async { + final response = await http.get(Uri.parse("$baseurl/notice")); + if (response.statusCode != 200) { + throw Exception(response.body); + } + return (jsonDecode(utf8.decode(response.bodyBytes)) as List) + .map((i) => NoticeResponse.fromJson(i).toEntity()) + .toList(); + } +} diff --git a/lib/data/notice/dto/response/notice_response.dart b/lib/data/notice/dto/response/notice_response.dart new file mode 100644 index 00000000..60e9db33 --- /dev/null +++ b/lib/data/notice/dto/response/notice_response.dart @@ -0,0 +1,33 @@ +import 'package:lotura/domain/notice/entity/notice_entity.dart'; + +class NoticeResponse { + final int id; + final String title; + final String contents; + final String date; + + NoticeResponse({ + required this.id, + required this.title, + required this.contents, + required this.date, + }); + + factory NoticeResponse.fromJson(Map json) { + return NoticeResponse( + id: json['id'], + title: json['title'], + contents: json['contents'], + date: json['date'], + ); + } + + NoticeEntity toEntity() { + return NoticeEntity( + noticeId: id, + title: title, + contents: contents, + date: date, + ); + } +} diff --git a/lib/data/notice/repository/notice_repository_impl.dart b/lib/data/notice/repository/notice_repository_impl.dart new file mode 100644 index 00000000..2f22c380 --- /dev/null +++ b/lib/data/notice/repository/notice_repository_impl.dart @@ -0,0 +1,26 @@ +import 'package:lotura/data/notice/data_source/local/local_notice_data_source.dart'; +import 'package:lotura/data/notice/data_source/remote/remote_notice_data_source.dart'; +import 'package:lotura/domain/notice/entity/notice_entity.dart'; +import 'package:lotura/domain/notice/repository/notice_repository.dart'; + +class NoticeRepositoryImpl implements NoticeRepository { + final RemoteNoticeDataSource _remoteNoticeDataSource; + final LocalNoticeDataSource _localNoticeDataSource; + + const NoticeRepositoryImpl({ + required RemoteNoticeDataSource remoteNoticeDataSource, + required LocalNoticeDataSource localNoticeDataSource, + }) : _remoteNoticeDataSource = remoteNoticeDataSource, + _localNoticeDataSource = localNoticeDataSource; + + @override + Future> getNotice() => _remoteNoticeDataSource.getNotice(); + + @override + int? getLastNoticeId({required String key}) => + _localNoticeDataSource.getValue(key: key); + + @override + Future setLastNoticeId({required String key, required int value}) => + _localNoticeDataSource.setValue(key: key, value: value); +} diff --git a/lib/data/repository/apply_repository_impl.dart b/lib/data/repository/apply_repository_impl.dart deleted file mode 100644 index b681dd91..00000000 --- a/lib/data/repository/apply_repository_impl.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'dart:async'; - -import 'package:lotura/data/data_source/apply/remote/remote_apply_data_source.dart'; -import 'package:lotura/data/dto/request/apply_cancel_request.dart'; -import 'package:lotura/data/dto/request/send_fcm_info_request.dart'; -import 'package:lotura/domain/entity/apply_entity.dart'; -import 'package:lotura/domain/repository/apply_repository.dart'; - -class ApplyRepositoryImpl implements ApplyRepository { - final RemoteApplyDataSource _remoteApplyDataSource; - - ApplyRepositoryImpl({required RemoteApplyDataSource remoteApplyDataSource}) - : _remoteApplyDataSource = remoteApplyDataSource; - - @override - Future> getApplyList() async => - _remoteApplyDataSource.getApplyList(); - - @override - Future sendFCMInfo( - {required SendFCMInfoRequest sendFCMInfoRequest}) async => - _remoteApplyDataSource.sendFCMInfo( - sendFCMInfoRequest: sendFCMInfoRequest); - - @override - Future> applyCancel( - {required ApplyCancelRequest applyCancelRequest}) async => - _remoteApplyDataSource.applyCancel( - applyCancelRequest: applyCancelRequest); -} diff --git a/lib/di/di.dart b/lib/di/di.dart index 2ae441f3..5ceab07f 100644 --- a/lib/di/di.dart +++ b/lib/di/di.dart @@ -2,23 +2,31 @@ import 'dart:async'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:hive/hive.dart'; -import 'package:lotura/data/data_source/apply/remote/remote_apply_data_source.dart'; -import 'package:lotura/data/data_source/laundry/local/local_laundry_data_source.dart'; -import 'package:lotura/data/data_source/laundry/remote/remote_laundry_data_source.dart'; -import 'package:lotura/data/repository/apply_repository_impl.dart'; -import 'package:lotura/data/repository/laundry_repository_impl.dart'; -import 'package:lotura/domain/entity/laundry_entity.dart'; -import 'package:lotura/domain/repository/apply_repository.dart'; -import 'package:lotura/domain/repository/laundry_repository.dart'; -import 'package:lotura/domain/use_case/apply_cancel_use_case.dart'; -import 'package:lotura/domain/use_case/get_all_laundry_list_use_case.dart'; -import 'package:lotura/domain/use_case/get_apply_list_use_case.dart'; -import 'package:lotura/domain/use_case/get_laundry_room_index_use_case.dart'; -import 'package:lotura/domain/use_case/get_laundry_status_use_case.dart'; -import 'package:lotura/domain/use_case/send_fcm_info_use_case.dart'; -import 'package:lotura/domain/use_case/update_laundry_room_index_use_case.dart'; +import 'package:lotura/data/apply/data_source/remote/remote_apply_data_source.dart'; +import 'package:lotura/data/apply/repository/apply_repository_impl.dart'; +import 'package:lotura/data/laundry/data_source/local/local_laundry_data_source.dart'; +import 'package:lotura/data/laundry/data_source/remote/remote_laundry_data_source.dart'; +import 'package:lotura/data/laundry/repository/laundry_repository_impl.dart'; +import 'package:lotura/data/notice/data_source/local/local_notice_data_source.dart'; +import 'package:lotura/data/notice/data_source/remote/remote_notice_data_source.dart'; +import 'package:lotura/data/notice/repository/notice_repository_impl.dart'; +import 'package:lotura/domain/apply/repository/apply_repository.dart'; +import 'package:lotura/domain/apply/use_case/apply_cancel_use_case.dart'; +import 'package:lotura/domain/apply/use_case/get_apply_list_use_case.dart'; +import 'package:lotura/domain/apply/use_case/send_fcm_info_use_case.dart'; +import 'package:lotura/domain/laundry/entity/laundry_entity.dart'; +import 'package:lotura/domain/laundry/repository/laundry_repository.dart'; +import 'package:lotura/domain/laundry/use_case/get_all_laundry_list_use_case.dart'; +import 'package:lotura/domain/laundry/use_case/get_laundry_room_index_use_case.dart'; +import 'package:lotura/domain/laundry/use_case/get_laundry_status_use_case.dart'; +import 'package:lotura/domain/laundry/use_case/update_laundry_room_index_use_case.dart'; +import 'package:lotura/domain/notice/repository/notice_repository.dart'; +import 'package:lotura/domain/notice/use_case/get_last_notice_id_use_case.dart'; +import 'package:lotura/domain/notice/use_case/get_notice_use_case.dart'; +import 'package:lotura/domain/notice/use_case/update_last_notice_id_use_case.dart'; import 'package:lotura/presentation/apply_page/bloc/apply_bloc.dart'; import 'package:lotura/presentation/laundry_room_page/bloc/laundry_bloc.dart'; +import 'package:lotura/presentation/notice_page/bloc/notice_bloc.dart'; import 'package:lotura/presentation/setting_page/bloc/room_bloc.dart'; Future> di() async { @@ -27,11 +35,15 @@ Future> di() async { LocalLaundryDataSource localLaundryDataSource = LocalLaundryDataSource(localDatabase: box); + LocalNoticeDataSource localNoticeDataSource = LocalNoticeDataSource(box: box); + RemoteLaundryDataSource remoteLaundryDataSource = RemoteLaundryDataSource( streamController: StreamController.broadcast()); RemoteApplyDataSource remoteApplyDataSource = RemoteApplyDataSource(); + RemoteNoticeDataSource remoteNoticeDataSource = RemoteNoticeDataSource(); + LaundryRepository laundryRepository = LaundryRepositoryImpl( localLaundryDataSource: localLaundryDataSource, remoteLaundryDataSource: remoteLaundryDataSource); @@ -39,6 +51,10 @@ Future> di() async { ApplyRepository applyRepository = ApplyRepositoryImpl(remoteApplyDataSource: remoteApplyDataSource); + NoticeRepository noticeRepository = NoticeRepositoryImpl( + remoteNoticeDataSource: remoteNoticeDataSource, + localNoticeDataSource: localNoticeDataSource); + GetLaundryStatusUseCase getLaundryStatusUseCase = GetLaundryStatusUseCase(laundryRepository: laundryRepository); @@ -60,6 +76,15 @@ Future> di() async { UpdateLaundryRoomIndexUseCase updateLaundryRoomIndexUseCase = UpdateLaundryRoomIndexUseCase(laundryRepository: laundryRepository); + GetNoticeUseCase getNoticeUseCase = + GetNoticeUseCase(noticeRepository: noticeRepository); + + GetLastNoticeIdUseCase getLastNoticeIdUseCase = + GetLastNoticeIdUseCase(noticeRepository: noticeRepository); + + UpdateLastNoticeIdUseCase updateLastNoticeIdUseCase = + UpdateLastNoticeIdUseCase(noticeRepository: noticeRepository); + return [ BlocProvider( create: (context) => ApplyBloc( @@ -75,5 +100,12 @@ Future> di() async { getLaundryRoomIndexUseCase: getLaundryRoomIndexUseCase, updateLaundryRoomIndexUseCase: updateLaundryRoomIndexUseCase), ), + BlocProvider( + create: (context) => NoticeBloc( + getNoticeUseCase: getNoticeUseCase, + getLastNoticeIdUseCase: getLastNoticeIdUseCase, + updateLastNoticeIdUseCase: updateLastNoticeIdUseCase, + ), + ), ]; } diff --git a/lib/domain/apply/entity/apply_entity.dart b/lib/domain/apply/entity/apply_entity.dart new file mode 100644 index 00000000..eddcfe5e --- /dev/null +++ b/lib/domain/apply/entity/apply_entity.dart @@ -0,0 +1,11 @@ +import 'package:lotura/main.dart'; + +class ApplyEntity { + final int deviceId; + final DeviceType deviceType; + + const ApplyEntity({ + required this.deviceId, + required this.deviceType, + }); +} diff --git a/lib/domain/apply/repository/apply_repository.dart b/lib/domain/apply/repository/apply_repository.dart new file mode 100644 index 00000000..5d321806 --- /dev/null +++ b/lib/domain/apply/repository/apply_repository.dart @@ -0,0 +1,13 @@ +import 'dart:async'; + +import 'package:lotura/data/apply/dto/request/apply_cancel_request.dart'; +import 'package:lotura/data/apply/dto/request/send_fcm_info_request.dart'; +import 'package:lotura/domain/apply/entity/apply_entity.dart'; + +abstract class ApplyRepository { + Future> getApplyList(); + + Future sendFCMInfo({required SendFCMInfoRequest sendFCMInfoRequest}); + + Future applyCancel({required ApplyCancelRequest applyCancelRequest}); +} diff --git a/lib/domain/apply/use_case/apply_cancel_use_case.dart b/lib/domain/apply/use_case/apply_cancel_use_case.dart new file mode 100644 index 00000000..eee26a9b --- /dev/null +++ b/lib/domain/apply/use_case/apply_cancel_use_case.dart @@ -0,0 +1,12 @@ +import 'package:lotura/data/apply/dto/request/apply_cancel_request.dart'; +import 'package:lotura/domain/apply/repository/apply_repository.dart'; + +class ApplyCancelUseCase { + final ApplyRepository _applyRepository; + + ApplyCancelUseCase({required ApplyRepository applyRepository}) + : _applyRepository = applyRepository; + + Future execute({required ApplyCancelRequest applyCancelRequest}) => + _applyRepository.applyCancel(applyCancelRequest: applyCancelRequest); +} diff --git a/lib/domain/use_case/get_apply_list_use_case.dart b/lib/domain/apply/use_case/get_apply_list_use_case.dart similarity index 65% rename from lib/domain/use_case/get_apply_list_use_case.dart rename to lib/domain/apply/use_case/get_apply_list_use_case.dart index 62dac1c0..a80296f0 100644 --- a/lib/domain/use_case/get_apply_list_use_case.dart +++ b/lib/domain/apply/use_case/get_apply_list_use_case.dart @@ -1,5 +1,5 @@ -import 'package:lotura/domain/entity/apply_entity.dart'; -import 'package:lotura/domain/repository/apply_repository.dart'; +import 'package:lotura/domain/apply/entity/apply_entity.dart'; +import 'package:lotura/domain/apply/repository/apply_repository.dart'; class GetApplyListUseCase { final ApplyRepository _applyRepository; diff --git a/lib/domain/apply/use_case/send_fcm_info_use_case.dart b/lib/domain/apply/use_case/send_fcm_info_use_case.dart new file mode 100644 index 00000000..38976ab2 --- /dev/null +++ b/lib/domain/apply/use_case/send_fcm_info_use_case.dart @@ -0,0 +1,12 @@ +import 'package:lotura/data/apply/dto/request/send_fcm_info_request.dart'; +import 'package:lotura/domain/apply/repository/apply_repository.dart'; + +class SendFCMInfoUseCase { + final ApplyRepository _applyRepository; + + SendFCMInfoUseCase({required ApplyRepository applyRepository}) + : _applyRepository = applyRepository; + + Future execute({required SendFCMInfoRequest sendFCMInfoRequest}) => + _applyRepository.sendFCMInfo(sendFCMInfoRequest: sendFCMInfoRequest); +} diff --git a/lib/domain/entity/apply_entity.dart b/lib/domain/entity/apply_entity.dart deleted file mode 100644 index c71ff0e7..00000000 --- a/lib/domain/entity/apply_entity.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:lotura/main.dart'; - -class ApplyEntity { - final int deviceId; - final String deviceType; - final Machine machine; - - ApplyEntity({required this.deviceId, required this.deviceType}) - : machine = deviceType == "WASH" ? Machine.wash : Machine.dry; -} diff --git a/lib/domain/entity/laundry_entity.dart b/lib/domain/entity/laundry_entity.dart deleted file mode 100644 index a6bd449b..00000000 --- a/lib/domain/entity/laundry_entity.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:lotura/main.dart'; - -class LaundryEntity { - final int id; - final CurrentState state; - final Machine deviceType; - - LaundryEntity({ - required this.id, - required int state, - required String deviceType, - }) : state = CurrentState.values.elementAt(state), - deviceType = deviceType == "WASH" ? Machine.wash : Machine.dry; -} diff --git a/lib/domain/laundry/entity/laundry_entity.dart b/lib/domain/laundry/entity/laundry_entity.dart new file mode 100644 index 00000000..81190d4a --- /dev/null +++ b/lib/domain/laundry/entity/laundry_entity.dart @@ -0,0 +1,13 @@ +import 'package:lotura/main.dart'; + +class LaundryEntity { + final int id; + final CurrentState state; + final DeviceType deviceType; + + const LaundryEntity({ + required this.id, + required this.state, + required this.deviceType, + }); +} diff --git a/lib/domain/repository/laundry_repository.dart b/lib/domain/laundry/repository/laundry_repository.dart similarity index 81% rename from lib/domain/repository/laundry_repository.dart rename to lib/domain/laundry/repository/laundry_repository.dart index e4d9a2fe..521f09a1 100644 --- a/lib/domain/repository/laundry_repository.dart +++ b/lib/domain/laundry/repository/laundry_repository.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import 'package:lotura/domain/entity/laundry_entity.dart'; +import 'package:lotura/domain/laundry/entity/laundry_entity.dart'; abstract class LaundryRepository { Stream get laundryList; diff --git a/lib/domain/use_case/get_all_laundry_list_use_case.dart b/lib/domain/laundry/use_case/get_all_laundry_list_use_case.dart similarity index 68% rename from lib/domain/use_case/get_all_laundry_list_use_case.dart rename to lib/domain/laundry/use_case/get_all_laundry_list_use_case.dart index 6e09a385..5a9c84fc 100644 --- a/lib/domain/use_case/get_all_laundry_list_use_case.dart +++ b/lib/domain/laundry/use_case/get_all_laundry_list_use_case.dart @@ -1,5 +1,5 @@ -import 'package:lotura/domain/entity/laundry_entity.dart'; -import 'package:lotura/domain/repository/laundry_repository.dart'; +import 'package:lotura/domain/laundry/entity/laundry_entity.dart'; +import 'package:lotura/domain/laundry/repository/laundry_repository.dart'; class GetAllLaundryListUseCase { final LaundryRepository _laundryRepository; diff --git a/lib/domain/use_case/get_laundry_room_index_use_case.dart b/lib/domain/laundry/use_case/get_laundry_room_index_use_case.dart similarity index 80% rename from lib/domain/use_case/get_laundry_room_index_use_case.dart rename to lib/domain/laundry/use_case/get_laundry_room_index_use_case.dart index 49e6a683..fd1d6030 100644 --- a/lib/domain/use_case/get_laundry_room_index_use_case.dart +++ b/lib/domain/laundry/use_case/get_laundry_room_index_use_case.dart @@ -1,4 +1,4 @@ -import 'package:lotura/domain/repository/laundry_repository.dart'; +import 'package:lotura/domain/laundry/repository/laundry_repository.dart'; class GetLaundryRoomIndexUseCase { final LaundryRepository _laundryRepository; diff --git a/lib/domain/use_case/get_laundry_status_use_case.dart b/lib/domain/laundry/use_case/get_laundry_status_use_case.dart similarity index 71% rename from lib/domain/use_case/get_laundry_status_use_case.dart rename to lib/domain/laundry/use_case/get_laundry_status_use_case.dart index 40a0e105..fe9d6b09 100644 --- a/lib/domain/use_case/get_laundry_status_use_case.dart +++ b/lib/domain/laundry/use_case/get_laundry_status_use_case.dart @@ -1,5 +1,5 @@ -import 'package:lotura/domain/entity/laundry_entity.dart'; -import 'package:lotura/domain/repository/laundry_repository.dart'; +import 'package:lotura/domain/laundry/entity/laundry_entity.dart'; +import 'package:lotura/domain/laundry/repository/laundry_repository.dart'; class GetLaundryStatusUseCase { final LaundryRepository _laundryRepository; diff --git a/lib/domain/use_case/update_laundry_room_index_use_case.dart b/lib/domain/laundry/use_case/update_laundry_room_index_use_case.dart similarity index 82% rename from lib/domain/use_case/update_laundry_room_index_use_case.dart rename to lib/domain/laundry/use_case/update_laundry_room_index_use_case.dart index 15692519..6c51a435 100644 --- a/lib/domain/use_case/update_laundry_room_index_use_case.dart +++ b/lib/domain/laundry/use_case/update_laundry_room_index_use_case.dart @@ -1,4 +1,4 @@ -import 'package:lotura/domain/repository/laundry_repository.dart'; +import 'package:lotura/domain/laundry/repository/laundry_repository.dart'; class UpdateLaundryRoomIndexUseCase { final LaundryRepository _laundryRepository; diff --git a/lib/domain/notice/entity/notice_entity.dart b/lib/domain/notice/entity/notice_entity.dart new file mode 100644 index 00000000..ae64171e --- /dev/null +++ b/lib/domain/notice/entity/notice_entity.dart @@ -0,0 +1,13 @@ +class NoticeEntity { + final int noticeId; + final String title; + final String contents; + final String date; + + const NoticeEntity({ + required this.noticeId, + required this.title, + required this.contents, + required this.date, + }); +} diff --git a/lib/domain/notice/repository/notice_repository.dart b/lib/domain/notice/repository/notice_repository.dart new file mode 100644 index 00000000..c9fb6f8b --- /dev/null +++ b/lib/domain/notice/repository/notice_repository.dart @@ -0,0 +1,11 @@ +import 'dart:async'; + +import 'package:lotura/domain/notice/entity/notice_entity.dart'; + +abstract class NoticeRepository { + Future> getNotice(); + + Future setLastNoticeId({required String key, required int value}); + + int? getLastNoticeId({required String key}); +} diff --git a/lib/domain/notice/use_case/get_last_notice_id_use_case.dart b/lib/domain/notice/use_case/get_last_notice_id_use_case.dart new file mode 100644 index 00000000..64f71ac4 --- /dev/null +++ b/lib/domain/notice/use_case/get_last_notice_id_use_case.dart @@ -0,0 +1,16 @@ +import 'package:lotura/domain/notice/entity/notice_entity.dart'; +import 'package:lotura/domain/notice/repository/notice_repository.dart'; + +class GetLastNoticeIdUseCase { + final NoticeRepository _noticeRepository; + + const GetLastNoticeIdUseCase({required NoticeRepository noticeRepository}) + : _noticeRepository = noticeRepository; + + bool execute({required List noticeList}) { + int lastNoticeId = + _noticeRepository.getLastNoticeId(key: "lastNoticeId") ?? -1; + int newNoticeId = noticeList.isEmpty ? -1 : noticeList.first.noticeId; + return lastNoticeId < newNoticeId; + } +} diff --git a/lib/domain/notice/use_case/get_notice_use_case.dart b/lib/domain/notice/use_case/get_notice_use_case.dart new file mode 100644 index 00000000..35681c60 --- /dev/null +++ b/lib/domain/notice/use_case/get_notice_use_case.dart @@ -0,0 +1,11 @@ +import 'package:lotura/domain/notice/entity/notice_entity.dart'; +import 'package:lotura/domain/notice/repository/notice_repository.dart'; + +class GetNoticeUseCase { + final NoticeRepository _noticeRepository; + + GetNoticeUseCase({required NoticeRepository noticeRepository}) + : _noticeRepository = noticeRepository; + + Future> execute() => _noticeRepository.getNotice(); +} diff --git a/lib/domain/notice/use_case/update_last_notice_id_use_case.dart b/lib/domain/notice/use_case/update_last_notice_id_use_case.dart new file mode 100644 index 00000000..ff2013b2 --- /dev/null +++ b/lib/domain/notice/use_case/update_last_notice_id_use_case.dart @@ -0,0 +1,14 @@ +import 'package:lotura/domain/notice/entity/notice_entity.dart'; +import 'package:lotura/domain/notice/repository/notice_repository.dart'; + +class UpdateLastNoticeIdUseCase { + final NoticeRepository _noticeRepository; + + UpdateLastNoticeIdUseCase({required NoticeRepository noticeRepository}) + : _noticeRepository = noticeRepository; + + Future execute({required List noticeList}) => + _noticeRepository.setLastNoticeId( + key: "lastNoticeId", + value: noticeList.isEmpty ? -1 : noticeList.first.noticeId); +} diff --git a/lib/domain/repository/apply_repository.dart b/lib/domain/repository/apply_repository.dart deleted file mode 100644 index 378494e0..00000000 --- a/lib/domain/repository/apply_repository.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'dart:async'; - -import 'package:lotura/data/dto/request/apply_cancel_request.dart'; -import 'package:lotura/data/dto/request/send_fcm_info_request.dart'; -import 'package:lotura/domain/entity/apply_entity.dart'; - -abstract class ApplyRepository { - Future> getApplyList(); - - Future sendFCMInfo({required SendFCMInfoRequest sendFCMInfoRequest}); - - Future> applyCancel( - {required ApplyCancelRequest applyCancelRequest}); -} diff --git a/lib/domain/use_case/apply_cancel_use_case.dart b/lib/domain/use_case/apply_cancel_use_case.dart deleted file mode 100644 index be3c40e5..00000000 --- a/lib/domain/use_case/apply_cancel_use_case.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:lotura/data/dto/request/apply_cancel_request.dart'; -import 'package:lotura/domain/entity/apply_entity.dart'; -import 'package:lotura/domain/repository/apply_repository.dart'; - -class ApplyCancelUseCase { - final ApplyRepository _applyRepository; - - ApplyCancelUseCase({required ApplyRepository applyRepository}) - : _applyRepository = applyRepository; - - Future> execute( - {required ApplyCancelRequest applyCancelRequest}) => - _applyRepository.applyCancel(applyCancelRequest: applyCancelRequest); -} diff --git a/lib/domain/use_case/send_fcm_info_use_case.dart b/lib/domain/use_case/send_fcm_info_use_case.dart deleted file mode 100644 index 1f40b616..00000000 --- a/lib/domain/use_case/send_fcm_info_use_case.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:lotura/data/dto/request/send_fcm_info_request.dart'; -import 'package:lotura/domain/entity/apply_entity.dart'; -import 'package:lotura/domain/repository/apply_repository.dart'; - -class SendFCMInfoUseCase { - final ApplyRepository _applyRepository; - - SendFCMInfoUseCase({required ApplyRepository applyRepository}) - : _applyRepository = applyRepository; - - Future> execute( - {required SendFCMInfoRequest sendFCMInfoRequest}) async { - await _applyRepository.sendFCMInfo(sendFCMInfoRequest: sendFCMInfoRequest); - return _applyRepository.getApplyList(); - } -} diff --git a/lib/init/fcm_init.dart b/lib/init/fcm_init.dart index 6cb19323..b4ffa3a0 100644 --- a/lib/init/fcm_init.dart +++ b/lib/init/fcm_init.dart @@ -3,7 +3,7 @@ import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; -import 'package:lotura/data/dto/request/get_apply_list_request.dart'; +import 'package:lotura/data/apply/dto/request/get_apply_list_request.dart'; import 'package:lotura/presentation/apply_page/bloc/apply_bloc.dart'; import 'package:lotura/presentation/apply_page/bloc/apply_event.dart'; diff --git a/lib/main.dart b/lib/main.dart index 077fbc4e..27bfe640 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -93,14 +93,26 @@ enum CurrentState { }); } -enum Machine { - wash(text: "세탁기", icon: LoturaIcons.laundry), - dry(text: "건조기", icon: LoturaIcons.dry); +enum DeviceType { + wash( + text: "세탁기", + icon: LoturaIcons.laundry, + imagePath: "assets/laundry_image.jpeg", + ), + dry( + text: "건조기", + icon: LoturaIcons.dry, + imagePath: "assets/dry_image.jpeg", + ); - final String text; + final String text, imagePath; final IconData icon; - const Machine({required this.text, required this.icon}); + const DeviceType({ + required this.text, + required this.icon, + required this.imagePath, + }); } enum RoomLocation { diff --git a/lib/presentation/apply_page/bloc/apply_bloc.dart b/lib/presentation/apply_page/bloc/apply_bloc.dart index 3f325b69..3c831d95 100644 --- a/lib/presentation/apply_page/bloc/apply_bloc.dart +++ b/lib/presentation/apply_page/bloc/apply_bloc.dart @@ -1,12 +1,15 @@ import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:lotura/domain/entity/apply_entity.dart'; -import 'package:lotura/domain/use_case/apply_cancel_use_case.dart'; -import 'package:lotura/domain/use_case/get_apply_list_use_case.dart'; -import 'package:lotura/domain/use_case/send_fcm_info_use_case.dart'; +import 'package:lotura/data/apply/dto/request/apply_cancel_request.dart'; +import 'package:lotura/data/apply/dto/request/send_fcm_info_request.dart'; +import 'package:lotura/domain/apply/entity/apply_entity.dart'; +import 'package:lotura/domain/apply/use_case/apply_cancel_use_case.dart'; +import 'package:lotura/domain/apply/use_case/get_apply_list_use_case.dart'; +import 'package:lotura/domain/apply/use_case/send_fcm_info_use_case.dart'; import 'package:lotura/presentation/apply_page/bloc/apply_event.dart'; +import 'package:lotura/presentation/apply_page/bloc/apply_model.dart'; import 'package:lotura/presentation/apply_page/bloc/apply_state.dart'; -class ApplyBloc extends Bloc>> { +class ApplyBloc extends Bloc> { final GetApplyListUseCase _getApplyListUseCase; final SendFCMInfoUseCase _sendFCMInfoUseCase; final ApplyCancelUseCase _applyCancelUseCase; @@ -24,35 +27,46 @@ class ApplyBloc extends Bloc>> { on(_applyCancelEventHandler); } - void _getApplyListEventHandler(GetApplyListEvent event, - Emitter>> emit) async { + void _getApplyListEventHandler( + GetApplyListEvent event, Emitter> emit) async { try { emit(Loading()); - emit(Loaded(data: await _getApplyListUseCase.execute())); + final applyList = await _getApplyListUseCase.execute(); + final applyModel = ApplyModel(applyList: applyList); + emit((Loaded(data: applyModel))); } catch (e) { emit(Error(errorMessage: e)); } } void _sendFCMEventHandler( - SendFCMEvent event, Emitter>> emit) async { + SendFCMEvent event, Emitter> emit) async { try { + List newApplyList = state.value.applyList; emit(Loading()); - final applyList = await _sendFCMInfoUseCase.execute( - sendFCMInfoRequest: event.sendFCMInfoRequest); - emit(Loaded(data: applyList)); + await _sendFCMInfoUseCase.execute( + sendFCMInfoRequest: + SendFCMInfoRequest(deviceId: event.deviceId, expectState: 1)); + newApplyList.add( + ApplyEntity(deviceId: event.deviceId, deviceType: event.deviceType)); + newApplyList.sort((a, b) => a.deviceId.compareTo(b.deviceId)); + final applyModel = ApplyModel(applyList: newApplyList); + emit(Loaded(data: applyModel)); } catch (e) { emit(Error(errorMessage: e)); } } - void _applyCancelEventHandler(ApplyCancelEvent event, - Emitter>> emit) async { + void _applyCancelEventHandler( + ApplyCancelEvent event, Emitter> emit) async { try { + List newApplyList = state.value.applyList; emit(Loading()); - final applyList = await _applyCancelUseCase.execute( - applyCancelRequest: event.applyCancelRequest); - emit(Loaded(data: applyList)); + await _applyCancelUseCase.execute( + applyCancelRequest: ApplyCancelRequest(deviceId: event.deviceId)); + newApplyList.removeWhere((e) => e.deviceId == event.deviceId); + final applyModel = ApplyModel(applyList: newApplyList); + emit(Loaded(data: applyModel)); } catch (e) { emit(Error(errorMessage: e)); } diff --git a/lib/presentation/apply_page/bloc/apply_event.dart b/lib/presentation/apply_page/bloc/apply_event.dart index 2fd01b90..85d5815b 100644 --- a/lib/presentation/apply_page/bloc/apply_event.dart +++ b/lib/presentation/apply_page/bloc/apply_event.dart @@ -1,6 +1,5 @@ -import 'package:lotura/data/dto/request/apply_cancel_request.dart'; -import 'package:lotura/data/dto/request/get_apply_list_request.dart'; -import 'package:lotura/data/dto/request/send_fcm_info_request.dart'; +import 'package:lotura/data/apply/dto/request/get_apply_list_request.dart'; +import 'package:lotura/main.dart'; abstract class ApplyEvent {} @@ -11,13 +10,19 @@ class GetApplyListEvent extends ApplyEvent { } class ApplyCancelEvent extends ApplyEvent { - final ApplyCancelRequest applyCancelRequest; + final int deviceId; - ApplyCancelEvent({required this.applyCancelRequest}); + ApplyCancelEvent({ + required this.deviceId, + }); } class SendFCMEvent extends ApplyEvent { - final SendFCMInfoRequest sendFCMInfoRequest; + final int deviceId; + final DeviceType deviceType; - SendFCMEvent({required this.sendFCMInfoRequest}); + SendFCMEvent({ + required this.deviceId, + required this.deviceType, + }); } diff --git a/lib/presentation/apply_page/bloc/apply_model.dart b/lib/presentation/apply_page/bloc/apply_model.dart new file mode 100644 index 00000000..354eb6d4 --- /dev/null +++ b/lib/presentation/apply_page/bloc/apply_model.dart @@ -0,0 +1,13 @@ +import 'package:lotura/domain/apply/entity/apply_entity.dart'; + +class ApplyModel { + final List applyList; + + ApplyModel({ + required this.applyList, + }); + + ApplyModel copyWith({List? applyList}) { + return ApplyModel(applyList: applyList ?? this.applyList); + } +} diff --git a/lib/presentation/apply_page/ui/view/apply_page.dart b/lib/presentation/apply_page/ui/view/apply_page.dart index 6ba0118b..1946a2b5 100644 --- a/lib/presentation/apply_page/ui/view/apply_page.dart +++ b/lib/presentation/apply_page/ui/view/apply_page.dart @@ -1,12 +1,18 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:lotura/domain/entity/apply_entity.dart'; import 'package:lotura/main.dart'; import 'package:lotura/presentation/apply_page/bloc/apply_bloc.dart'; +import 'package:lotura/presentation/apply_page/bloc/apply_model.dart'; import 'package:lotura/presentation/apply_page/bloc/apply_state.dart'; +import 'package:lotura/presentation/notice_page/bloc/notice_bloc.dart'; +import 'package:lotura/presentation/notice_page/bloc/notice_event.dart'; +import 'package:lotura/presentation/notice_page/bloc/notice_model.dart'; +import 'package:lotura/presentation/notice_page/bloc/notice_state.dart' as n; +import 'package:lotura/presentation/notice_page/ui/view/notice_page.dart'; import 'package:lotura/presentation/setting_page/ui/view/setting_page.dart'; import 'package:lotura/presentation/utils/lotura_colors.dart'; +import 'package:lotura/presentation/utils/lotura_icons.dart'; import 'package:lotura/presentation/utils/machine_card.dart'; class ApplyPage extends StatelessWidget { @@ -50,12 +56,46 @@ class ApplyPage extends StatelessWidget { ], ), actions: [ + IconButton( + onPressed: () { + context.read().add(UpdateLastNoticeIdEvent()); + Navigator.push(context, + MaterialPageRoute(builder: (context) => const NoticePage())); + }, + icon: BlocBuilder>( + builder: (context, state) => state.value.isNewNotice + ? Stack( + alignment: Alignment.topRight, + children: [ + Icon( + LoturaIcons.notice, + color: LoturaColors.black, + size: 24.0.r, + ), + Container( + width: 10.0.r, + height: 10.0.r, + decoration: const BoxDecoration( + color: Colors.blue, + shape: BoxShape.circle, + ), + ), + ], + ) + : Icon( + LoturaIcons.notice, + color: LoturaColors.black, + size: 24.0.r, + ), + ), + ), IconButton( onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (context) => const SettingPage())), - icon: const Icon( + icon: Icon( Icons.settings, color: LoturaColors.black, + size: 28.0.r, )), SizedBox(width: 24.0.w), ], @@ -78,7 +118,7 @@ class ApplyPage extends StatelessWidget { ), SizedBox(height: 20.0.h), Expanded( - child: BlocBuilder>>( + child: BlocBuilder>( builder: (context, state) { return switch (state) { Empty() => const Center(child: Text("비어있음")), @@ -89,9 +129,9 @@ class ApplyPage extends StatelessWidget { behavior: const ScrollBehavior().copyWith(overscroll: false), child: ListView.builder( - itemCount: state.value.length.isEven - ? state.value.length ~/ 2 - : state.value.length ~/ 2 + 1, + itemCount: state.value.applyList.length.isEven + ? state.value.applyList.length ~/ 2 + : state.value.applyList.length ~/ 2 + 1, itemBuilder: (context, index) { return Column( children: [ @@ -100,26 +140,38 @@ class ApplyPage extends StatelessWidget { MainAxisAlignment.spaceBetween, children: [ MachineCard( - index: state.value[index * 2].deviceId, + deviceId: state.value + .applyList[index * 2].deviceId, isEnableNotification: false, - isWoman: - state.value[index * 2].deviceId > 31 - ? true - : false, - machine: state.value[index * 2].machine, + isWoman: state + .value + .applyList[index * 2] + .deviceId > + 31 + ? true + : false, + deviceType: state.value + .applyList[index * 2].deviceType, state: CurrentState.working), - index * 2 + 1 < state.value.length + index * 2 + 1 < state.value.applyList.length ? MachineCard( - index: state - .value[index * 2 + 1].deviceId, + deviceId: state + .value + .applyList[index * 2 + 1] + .deviceId, isEnableNotification: false, - isWoman: state.value[index * 2 + 1] + isWoman: state + .value + .applyList[ + index * 2 + 1] .deviceId > 31 ? true : false, - machine: state - .value[index * 2 + 1].machine, + deviceType: state + .value + .applyList[index * 2 + 1] + .deviceType, state: CurrentState.working) : SizedBox( width: 185.0.w, diff --git a/lib/presentation/laundry_room_page/bloc/laundry_bloc.dart b/lib/presentation/laundry_room_page/bloc/laundry_bloc.dart index 6da2b012..1188b9a7 100644 --- a/lib/presentation/laundry_room_page/bloc/laundry_bloc.dart +++ b/lib/presentation/laundry_room_page/bloc/laundry_bloc.dart @@ -1,12 +1,12 @@ import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:lotura/domain/entity/laundry_entity.dart'; -import 'package:lotura/domain/use_case/get_all_laundry_list_use_case.dart'; -import 'package:lotura/domain/use_case/get_laundry_status_use_case.dart'; +import 'package:lotura/domain/laundry/entity/laundry_entity.dart'; +import 'package:lotura/domain/laundry/use_case/get_all_laundry_list_use_case.dart'; +import 'package:lotura/domain/laundry/use_case/get_laundry_status_use_case.dart'; import 'package:lotura/presentation/laundry_room_page/bloc/laundry_event.dart'; +import 'package:lotura/presentation/laundry_room_page/bloc/laundry_model.dart'; import 'package:lotura/presentation/laundry_room_page/bloc/laundry_state.dart'; -class LaundryBloc - extends Bloc>> { +class LaundryBloc extends Bloc> { final GetLaundryStatusUseCase _getLaundryStatusUseCase; final GetAllLaundryListUseCase _getAllLaundryListEventUseCase; @@ -20,21 +20,21 @@ class LaundryBloc on(_getAllLaundryListEventHandler); } - void _getLaundryEventHandler(GetLaundryEvent event, - Emitter>> emit) async { + void _getLaundryEventHandler( + GetLaundryEvent event, Emitter> emit) async { try { _getLaundryStatusUseCase.execute(); await for (var data in _getLaundryStatusUseCase.laundryList) { - final newState = Loaded( - data: state.value + final newLaundryModel = LaundryModel( + laundryList: state.value.laundryList .map((e) => e.id == data.id ? LaundryEntity( - id: data.id, - state: data.state.index, - deviceType: data.deviceType.name.toUpperCase()) + id: e.id, + state: data.state, + deviceType: data.deviceType) : e) .toList()); - emit(newState); + emit(Loaded(data: newLaundryModel)); } } catch (e) { emit(Error(error: e)); @@ -42,12 +42,12 @@ class LaundryBloc } void _getAllLaundryListEventHandler(GetAllLaundryListEvent event, - Emitter>> emit) async { + Emitter> emit) async { try { emit(Loading()); - final newState = - Loaded(data: await _getAllLaundryListEventUseCase.execute()); - emit(newState); + final newState = await _getAllLaundryListEventUseCase.execute(); + final newLaundryModel = LaundryModel(laundryList: newState); + emit(Loaded(data: newLaundryModel)); } catch (e) { emit(Error(error: e)); } diff --git a/lib/presentation/laundry_room_page/bloc/laundry_model.dart b/lib/presentation/laundry_room_page/bloc/laundry_model.dart new file mode 100644 index 00000000..6b8b6acd --- /dev/null +++ b/lib/presentation/laundry_room_page/bloc/laundry_model.dart @@ -0,0 +1,15 @@ +import 'package:lotura/domain/laundry/entity/laundry_entity.dart'; + +class LaundryModel { + final List laundryList; + + const LaundryModel({ + required this.laundryList, + }); + + LaundryModel copyWith({List? laundryList}) { + return LaundryModel( + laundryList: laundryList ?? this.laundryList, + ); + } +} diff --git a/lib/presentation/laundry_room_page/ui/view/laundry_room_page.dart b/lib/presentation/laundry_room_page/ui/view/laundry_room_page.dart index 1efbb35d..a058a23d 100644 --- a/lib/presentation/laundry_room_page/ui/view/laundry_room_page.dart +++ b/lib/presentation/laundry_room_page/ui/view/laundry_room_page.dart @@ -1,11 +1,17 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:lotura/domain/entity/laundry_entity.dart'; -import 'package:lotura/domain/entity/room_entity.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart' as s; +import 'package:lotura/domain/laundry/entity/laundry_entity.dart'; import 'package:lotura/main.dart'; import 'package:lotura/presentation/laundry_room_page/bloc/laundry_bloc.dart'; +import 'package:lotura/presentation/laundry_room_page/bloc/laundry_model.dart'; import 'package:lotura/presentation/laundry_room_page/bloc/laundry_state.dart'; +import 'package:lotura/presentation/notice_page/bloc/notice_bloc.dart'; +import 'package:lotura/presentation/notice_page/bloc/notice_event.dart'; +import 'package:lotura/presentation/notice_page/bloc/notice_model.dart'; +import 'package:lotura/presentation/notice_page/bloc/notice_state.dart' as n; +import 'package:lotura/presentation/notice_page/ui/view/notice_page.dart'; +import 'package:lotura/presentation/setting_page/bloc/laundry_room_model.dart'; import 'package:lotura/presentation/setting_page/bloc/room_bloc.dart'; import 'package:lotura/presentation/setting_page/bloc/room_event.dart'; import 'package:lotura/presentation/setting_page/bloc/room_state.dart'; @@ -33,7 +39,7 @@ class LaundryRoomPage extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder>( + return BlocBuilder>( builder: (context, roomBlocState) { if (roomBlocState is Changed) { return Scaffold( @@ -56,12 +62,51 @@ class LaundryRoomPage extends StatelessWidget { ], ), actions: [ + IconButton( + onPressed: () { + context.read().add(UpdateLastNoticeIdEvent()); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const NoticePage())); + }, + icon: BlocBuilder>( + builder: (context, state) => state.value.isNewNotice + ? Stack( + alignment: Alignment.topRight, + children: [ + Icon( + LoturaIcons.notice, + color: LoturaColors.black, + size: 24.0.r, + ), + Container( + width: 10.0.r, + height: 10.0.r, + decoration: const BoxDecoration( + color: Colors.blue, + shape: BoxShape.circle, + ), + ), + ], + ) + : Icon( + LoturaIcons.notice, + color: LoturaColors.black, + size: 24.0.r, + ), + ), + ), IconButton( onPressed: () => Navigator.push( context, MaterialPageRoute( builder: (context) => const SettingPage())), - icon: Icon(Icons.settings, color: LoturaColors.black), + icon: Icon( + Icons.settings, + color: LoturaColors.black, + size: 28.0.r, + ), ), SizedBox(width: 24.0.w), ], @@ -165,8 +210,7 @@ class LaundryRoomPage extends StatelessWidget { ], ), Expanded( - child: BlocBuilder>>( + child: BlocBuilder>( builder: (context, state) { return switch (state) { Empty() => const Center(child: Text("비어있음")), @@ -175,8 +219,8 @@ class LaundryRoomPage extends StatelessWidget { Error() => const Center(child: Text("인터넷 연결을 확인해주세요")), Loaded() => LaundryList( - list: state.data, - roomEntity: roomBlocState.value, + list: state.data.laundryList, + laundryRoomModel: roomBlocState.value, nfcData: nfcTagData, ), }; @@ -199,41 +243,41 @@ class LaundryList extends StatelessWidget { LaundryList({ super.key, required this.list, - required this.roomEntity, + required this.laundryRoomModel, required this.nfcData, }); final List list; - final LaundryRoomEntity roomEntity; + final LaundryRoomModel laundryRoomModel; final int nfcData; final Map placeIndex = {0: 0, 1: 16, 2: 31}; MachineWidget machineWidget( - {required LaundryRoomEntity roomState, - required int index, + {required LaundryRoomModel roomState, + required int deviceId, required CurrentState state, - required Machine machine}) => + required DeviceType deviceType}) => roomState.buttonView == ButtonView.image ? MachineCard( - index: index, + deviceId: deviceId, isEnableNotification: true, isWoman: roomState.roomLocation == RoomLocation.womanRoom, state: state, - machine: machine) + deviceType: deviceType) : MachineButton( - index: index, + deviceId: deviceId, isEnableNotification: true, isWoman: roomState.roomLocation == RoomLocation.womanRoom, state: state, - machine: machine); + deviceType: deviceType); @override Widget build(BuildContext context) { WidgetsBinding.instance.addPostFrameCallback( (_) { - if (nfcData != -1 && roomEntity.isNFCShowBottomSheet == false) { - if (roomEntity.showingBottomSheet == true) { + if (nfcData != -1 && laundryRoomModel.isNFCShowBottomSheet == false) { + if (laundryRoomModel.showingBottomSheet == true) { Navigator.of(context).pop(); } context.read().add(ShowBottomSheetEvent()); @@ -246,7 +290,7 @@ class LaundryList extends StatelessWidget { ), ), builder: (context) => OSJBottomSheet( - index: nfcData, + deviceId: nfcData, isEnableNotification: true, isWoman: nfcData > 31 ? true : false, state: list[nfcData - 1].state, @@ -259,7 +303,8 @@ class LaundryList extends StatelessWidget { return ScrollConfiguration( behavior: const ScrollBehavior().copyWith(overscroll: false), child: ListView.builder( - itemCount: roomEntity.roomLocation == RoomLocation.womanRoom ? 10 : 8, + itemCount: + laundryRoomModel.roomLocation == RoomLocation.womanRoom ? 10 : 8, itemBuilder: (context, index) { return Column( children: [ @@ -267,59 +312,66 @@ class LaundryList extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ machineWidget( - roomState: roomEntity, - index: list[placeIndex[roomEntity.roomLocation.index]! + - index] + roomState: laundryRoomModel, + deviceId: list[ + placeIndex[laundryRoomModel.roomLocation.index]! + + index] .id, - machine: list[placeIndex[roomEntity.roomLocation.index]! + - index] + deviceType: list[ + placeIndex[laundryRoomModel.roomLocation.index]! + + index] .deviceType, - state: list[placeIndex[roomEntity.roomLocation.index]! + - index] + state: list[ + placeIndex[laundryRoomModel.roomLocation.index]! + + index] .state), - roomEntity.buttonView.triangle, + laundryRoomModel.buttonView.triangle, machineWidget( - roomState: roomEntity, - index: placeIndex[roomEntity.roomLocation.index]! + + roomState: laundryRoomModel, + deviceId: placeIndex[laundryRoomModel.roomLocation.index]! + index + - (roomEntity.roomLocation == + (laundryRoomModel.roomLocation == RoomLocation.womanRoom ? 10 : 8) < 44 - ? list[placeIndex[roomEntity.roomLocation.index]! + + ? list[placeIndex[ + laundryRoomModel.roomLocation.index]! + index + - (roomEntity.roomLocation == + (laundryRoomModel.roomLocation == RoomLocation.womanRoom ? 10 : 8)] .id : -1, - machine: placeIndex[roomEntity.roomLocation.index]! + - index + - (roomEntity.roomLocation == - RoomLocation.womanRoom - ? 10 - : 8) < - 44 - ? list[placeIndex[roomEntity.roomLocation.index]! + - index + - (roomEntity.roomLocation == - RoomLocation.womanRoom - ? 10 - : 8)] - .deviceType - : Machine.dry, - state: placeIndex[roomEntity.roomLocation.index]! + + deviceType: + placeIndex[laundryRoomModel.roomLocation.index]! + + index + + (laundryRoomModel.roomLocation == + RoomLocation.womanRoom + ? 10 + : 8) < + 44 + ? list[placeIndex[ + laundryRoomModel.roomLocation.index]! + + index + + (laundryRoomModel.roomLocation == + RoomLocation.womanRoom + ? 10 + : 8)] + .deviceType + : DeviceType.dry, + state: placeIndex[laundryRoomModel.roomLocation.index]! + index + - (roomEntity.roomLocation == + (laundryRoomModel.roomLocation == RoomLocation.womanRoom ? 10 : 8) < 44 - ? list[placeIndex[roomEntity.roomLocation.index]! + + ? list[placeIndex[ + laundryRoomModel.roomLocation.index]! + index + - (roomEntity.roomLocation == + (laundryRoomModel.roomLocation == RoomLocation.womanRoom ? 10 : 8)] diff --git a/lib/presentation/notice_page/bloc/notice_bloc.dart b/lib/presentation/notice_page/bloc/notice_bloc.dart new file mode 100644 index 00000000..1e5c4413 --- /dev/null +++ b/lib/presentation/notice_page/bloc/notice_bloc.dart @@ -0,0 +1,56 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:lotura/domain/notice/entity/notice_entity.dart'; +import 'package:lotura/domain/notice/use_case/get_last_notice_id_use_case.dart'; +import 'package:lotura/domain/notice/use_case/get_notice_use_case.dart'; +import 'package:lotura/domain/notice/use_case/update_last_notice_id_use_case.dart'; +import 'package:lotura/presentation/notice_page/bloc/notice_event.dart'; +import 'package:lotura/presentation/notice_page/bloc/notice_model.dart'; +import 'package:lotura/presentation/notice_page/bloc/notice_state.dart'; + +class NoticeBloc extends Bloc> { + final GetNoticeUseCase _getNoticeUseCase; + final GetLastNoticeIdUseCase _getLastNoticeIdUseCase; + final UpdateLastNoticeIdUseCase _updateLastNoticeIdUseCase; + + NoticeBloc( + {required GetNoticeUseCase getNoticeUseCase, + required GetLastNoticeIdUseCase getLastNoticeIdUseCase, + required UpdateLastNoticeIdUseCase updateLastNoticeIdUseCase}) + : _getNoticeUseCase = getNoticeUseCase, + _getLastNoticeIdUseCase = getLastNoticeIdUseCase, + _updateLastNoticeIdUseCase = updateLastNoticeIdUseCase, + super(Empty( + data: const NoticeModel( + noticeList: [], + isNewNotice: false, + ))) { + on(_getNoticeEventHandler); + on(_updateLastNoticeIdEventHandler); + } + + void _getNoticeEventHandler( + GetNoticeEvent event, Emitter> emit) async { + try { + emit(Loading()); + final noticeList = await _getNoticeUseCase.execute(); + final isNewNotice = + _getLastNoticeIdUseCase.execute(noticeList: noticeList); + final newNoticeModel = + NoticeModel(noticeList: noticeList, isNewNotice: isNewNotice); + emit(Loaded(data: newNoticeModel)); + } catch (e) { + emit(Error(error: e)); + } + } + + void _updateLastNoticeIdEventHandler(UpdateLastNoticeIdEvent event, + Emitter> emit) async { + try { + await _updateLastNoticeIdUseCase.execute( + noticeList: state.value.noticeList); + emit(Loaded(data: state.value.copyWith(isNewNotice: false))); + } catch (e) { + emit(Error(error: e)); + } + } +} diff --git a/lib/presentation/notice_page/bloc/notice_event.dart b/lib/presentation/notice_page/bloc/notice_event.dart new file mode 100644 index 00000000..131095ed --- /dev/null +++ b/lib/presentation/notice_page/bloc/notice_event.dart @@ -0,0 +1,5 @@ +abstract class NoticeEvent {} + +class GetNoticeEvent extends NoticeEvent {} + +class UpdateLastNoticeIdEvent extends NoticeEvent {} diff --git a/lib/presentation/notice_page/bloc/notice_model.dart b/lib/presentation/notice_page/bloc/notice_model.dart new file mode 100644 index 00000000..e7bd26d9 --- /dev/null +++ b/lib/presentation/notice_page/bloc/notice_model.dart @@ -0,0 +1,18 @@ +import 'package:lotura/domain/notice/entity/notice_entity.dart'; + +class NoticeModel { + final List noticeList; + final bool isNewNotice; + + const NoticeModel({ + required this.noticeList, + required this.isNewNotice, + }); + + NoticeModel copyWith({List? noticeList, bool? isNewNotice}) { + return NoticeModel( + noticeList: noticeList ?? this.noticeList, + isNewNotice: isNewNotice ?? this.isNewNotice, + ); + } +} diff --git a/lib/presentation/notice_page/bloc/notice_state.dart b/lib/presentation/notice_page/bloc/notice_state.dart new file mode 100644 index 00000000..054215e1 --- /dev/null +++ b/lib/presentation/notice_page/bloc/notice_state.dart @@ -0,0 +1,35 @@ +enum NoticeStateEnum { empty, loading, error, loaded } + +sealed class NoticeState { + NoticeState({required this.noticeState, this.error, this.valueOrNull}); + + T? valueOrNull; + Object? error; + NoticeStateEnum noticeState; + + T get value => valueOrNull!; +} + +class Empty extends NoticeState { + final T data; + + Empty({required this.data}) : super(noticeState: NoticeStateEnum.empty); +} + +class Loading extends NoticeState { + Loading() : super(noticeState: NoticeStateEnum.loading); +} + +class Error extends NoticeState { + final Object error; + + Error({required this.error}) + : super(noticeState: NoticeStateEnum.error, error: error); +} + +class Loaded extends NoticeState { + final T data; + + Loaded({required this.data}) + : super(noticeState: NoticeStateEnum.loaded, valueOrNull: data); +} diff --git a/lib/presentation/notice_page/ui/view/notice_page.dart b/lib/presentation/notice_page/ui/view/notice_page.dart new file mode 100644 index 00000000..37f3f398 --- /dev/null +++ b/lib/presentation/notice_page/ui/view/notice_page.dart @@ -0,0 +1,74 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:lotura/presentation/notice_page/bloc/notice_bloc.dart'; +import 'package:lotura/presentation/notice_page/bloc/notice_model.dart'; +import 'package:lotura/presentation/notice_page/bloc/notice_state.dart'; +import 'package:lotura/presentation/notice_page/ui/widget/notice_list_tile.dart'; +import 'package:lotura/presentation/utils/lotura_colors.dart'; + +class NoticePage extends StatelessWidget { + const NoticePage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: LoturaColors.gray100, + appBar: AppBar( + backgroundColor: LoturaColors.gray100, + elevation: 0.0, + leadingWidth: 300.0.r, + leading: Row( + children: [ + IconButton( + padding: EdgeInsets.only(left: 24.0.r, right: 12.0.r), + onPressed: () => Navigator.pop(context), + icon: Icon( + Icons.keyboard_arrow_left, + color: LoturaColors.black, + size: 30.0.r, + ), + ), + Text( + "공지", + style: TextStyle( + fontSize: 24.0.sp, + color: LoturaColors.black, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ), + body: Padding( + padding: EdgeInsets.only( + left: 24.0.w, + right: 24.0.w, + top: 12.0.h, + bottom: 12.0.h, + ), + child: Padding( + padding: EdgeInsets.all(12.0.r), + child: BlocBuilder>( + builder: (context, state) => switch (state) { + Empty() => const Center(child: Text("비어있음")), + Loading() => const Center(child: CircularProgressIndicator()), + Error() => const Center(child: Text("인터넷 연결을 확인해주세요")), + Loaded(data: final data) => SizedBox( + width: double.infinity, + height: double.infinity, + child: ListView.builder( + itemCount: data.noticeList.length, + itemBuilder: (context, index) { + return NoticeListTile( + noticeEntity: data.noticeList[index]); + }, + ), + ), + }, + ), + ), + ), + ); + } +} diff --git a/lib/presentation/notice_page/ui/widget/notice_list_tile.dart b/lib/presentation/notice_page/ui/widget/notice_list_tile.dart new file mode 100644 index 00000000..e1561e8d --- /dev/null +++ b/lib/presentation/notice_page/ui/widget/notice_list_tile.dart @@ -0,0 +1,65 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:lotura/domain/notice/entity/notice_entity.dart'; +import 'package:lotura/presentation/utils/lotura_colors.dart'; + +class NoticeListTile extends StatefulWidget { + final NoticeEntity noticeEntity; + + const NoticeListTile({super.key, required this.noticeEntity}); + + @override + State createState() => _NoticeListTileState(); +} + +class _NoticeListTileState extends State { + bool _tap = false; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () => setState(() => _tap = !_tap), + child: Container( + margin: EdgeInsets.symmetric(vertical: 7.5.h), + padding: EdgeInsets.all(20.0.r), + decoration: const BoxDecoration( + color: LoturaColors.white, + borderRadius: BorderRadius.all(Radius.circular(12))), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(widget.noticeEntity.title, + style: TextStyle(fontSize: 16.0.sp)), + Text(widget.noticeEntity.date, + style: TextStyle(fontSize: 10.0.sp)), + ], + ), + Icon( + _tap ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down), + ], + ), + _tap + ? ListView( + padding: EdgeInsets.only(top: 10.0.r), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + children: [ + Text( + widget.noticeEntity.contents, + style: TextStyle(fontSize: 16.0.sp), + ), + ], + ) + : const SizedBox.shrink(), + ], + ), + ), + ); + } +} diff --git a/lib/domain/entity/room_entity.dart b/lib/presentation/setting_page/bloc/laundry_room_model.dart similarity index 87% rename from lib/domain/entity/room_entity.dart rename to lib/presentation/setting_page/bloc/laundry_room_model.dart index 151a7825..a1771fe4 100644 --- a/lib/domain/entity/room_entity.dart +++ b/lib/presentation/setting_page/bloc/laundry_room_model.dart @@ -1,11 +1,11 @@ import 'package:lotura/main.dart'; -class LaundryRoomEntity { +class LaundryRoomModel { final RoomLocation roomLocation; final ButtonView buttonView; final bool isClick, isNFCShowBottomSheet, showingBottomSheet; - const LaundryRoomEntity({ + const LaundryRoomModel({ required this.roomLocation, required this.buttonView, required this.isClick, @@ -13,13 +13,13 @@ class LaundryRoomEntity { required this.showingBottomSheet, }); - LaundryRoomEntity copyWith( + LaundryRoomModel copyWith( {RoomLocation? roomLocation, ButtonView? buttonView, bool? isClick, bool? isNFCShowBottomSheet, bool? showingBottomSheet}) { - return LaundryRoomEntity( + return LaundryRoomModel( roomLocation: roomLocation ?? this.roomLocation, buttonView: buttonView ?? this.buttonView, isClick: isClick ?? this.isClick, diff --git a/lib/presentation/setting_page/bloc/room_bloc.dart b/lib/presentation/setting_page/bloc/room_bloc.dart index b158dd1b..941a5b2f 100644 --- a/lib/presentation/setting_page/bloc/room_bloc.dart +++ b/lib/presentation/setting_page/bloc/room_bloc.dart @@ -1,12 +1,12 @@ import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:lotura/domain/entity/room_entity.dart'; -import 'package:lotura/domain/use_case/get_laundry_room_index_use_case.dart'; -import 'package:lotura/domain/use_case/update_laundry_room_index_use_case.dart'; +import 'package:lotura/domain/laundry/use_case/get_laundry_room_index_use_case.dart'; +import 'package:lotura/domain/laundry/use_case/update_laundry_room_index_use_case.dart'; import 'package:lotura/main.dart'; +import 'package:lotura/presentation/setting_page/bloc/laundry_room_model.dart'; import 'package:lotura/presentation/setting_page/bloc/room_event.dart'; import 'package:lotura/presentation/setting_page/bloc/room_state.dart'; -class RoomBloc extends Bloc> { +class RoomBloc extends Bloc> { final GetLaundryRoomIndexUseCase _getLaundryRoomIndexUseCase; final UpdateLaundryRoomIndexUseCase _updateLaundryRoomIndexUseCase; @@ -16,7 +16,7 @@ class RoomBloc extends Bloc> { : _getLaundryRoomIndexUseCase = getLaundryRoomIndexUseCase, _updateLaundryRoomIndexUseCase = updateLaundryRoomIndexUseCase, super(Initial( - data: const LaundryRoomEntity( + data: const LaundryRoomModel( roomLocation: RoomLocation.schoolSide, buttonView: ButtonView.icon, isClick: false, @@ -33,13 +33,13 @@ class RoomBloc extends Bloc> { } void _updateRoomIndexEventHandler( - UpdateRoomIndexEvent event, Emitter> emit) { + UpdateRoomIndexEvent event, Emitter> emit) { emit(Changed(data: state.value.copyWith(roomLocation: event.roomLocation))); _updateLaundryRoomIndexUseCase.execute(value: event.roomLocation.index); } void _getRoomIndexEventHandler( - GetRoomIndexEvent event, Emitter> emit) { + GetRoomIndexEvent event, Emitter> emit) { emit(Changed( data: state.value.copyWith( roomLocation: RoomLocation.values @@ -47,32 +47,32 @@ class RoomBloc extends Bloc> { } void _modifyRoomIndexEventHandler( - ModifyRoomIndexEvent event, Emitter> emit) { + ModifyRoomIndexEvent event, Emitter> emit) { emit(Changed(data: state.value.copyWith(roomLocation: event.roomLocation))); } void _modifyPlaceIconIndexEventHandler( - ModifyButtonViewEvent event, Emitter> emit) { + ModifyButtonViewEvent event, Emitter> emit) { emit(Changed(data: state.value.copyWith(buttonView: event.buttonView))); } void _showBottomSheetEventHandler( - ShowBottomSheetEvent event, Emitter> emit) { + ShowBottomSheetEvent event, Emitter> emit) { emit(Changed(data: state.value.copyWith(isNFCShowBottomSheet: true))); } void _initialShowBottomSheetEventHandler(InitialShowBottomSheetEvent event, - Emitter> emit) { + Emitter> emit) { emit(Changed(data: state.value.copyWith(isNFCShowBottomSheet: false))); } void _showingBottomSheetEventHandler(ShowingBottomSheetEvent event, - Emitter> emit) { + Emitter> emit) { emit(Changed(data: state.value.copyWith(showingBottomSheet: true))); } void _closingBottomSheetEventHandler(ClosingBottomSheetEvent event, - Emitter> emit) { + Emitter> emit) { emit(Changed(data: state.value.copyWith(showingBottomSheet: false))); } } diff --git a/lib/presentation/setting_page/ui/view/setting_page.dart b/lib/presentation/setting_page/ui/view/setting_page.dart index e4f299d1..19db7d0f 100644 --- a/lib/presentation/setting_page/ui/view/setting_page.dart +++ b/lib/presentation/setting_page/ui/view/setting_page.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:lotura/domain/entity/room_entity.dart'; +import 'package:lotura/presentation/setting_page/bloc/laundry_room_model.dart'; import 'package:lotura/presentation/setting_page/bloc/room_bloc.dart'; import 'package:lotura/presentation/setting_page/bloc/room_event.dart'; import 'package:lotura/presentation/setting_page/bloc/room_state.dart'; @@ -69,7 +69,7 @@ class _SettingPageState extends State { ), Row( children: [ - BlocBuilder>( + BlocBuilder>( builder: (context, state) { return switch (state) { Initial() => const SizedBox.shrink(), diff --git a/lib/presentation/setting_page/ui/widget/setting_page_bottom_sheet.dart b/lib/presentation/setting_page/ui/widget/setting_page_bottom_sheet.dart index 5541600d..41fbcb6c 100644 --- a/lib/presentation/setting_page/ui/widget/setting_page_bottom_sheet.dart +++ b/lib/presentation/setting_page/ui/widget/setting_page_bottom_sheet.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:lotura/domain/entity/room_entity.dart'; import 'package:lotura/main.dart'; +import 'package:lotura/presentation/setting_page/bloc/laundry_room_model.dart'; import 'package:lotura/presentation/setting_page/bloc/room_bloc.dart'; import 'package:lotura/presentation/setting_page/bloc/room_event.dart'; import 'package:lotura/presentation/setting_page/bloc/room_state.dart'; @@ -15,7 +15,7 @@ class SettingPageBottomSheet extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder>( + return BlocBuilder>( builder: (context, state) { if (state is Initial) { context.read().add(GetRoomIndexEvent()); diff --git a/lib/presentation/splash_page/ui/view/splash_page.dart b/lib/presentation/splash_page/ui/view/splash_page.dart index 74d34e6f..fead8004 100644 --- a/lib/presentation/splash_page/ui/view/splash_page.dart +++ b/lib/presentation/splash_page/ui/view/splash_page.dart @@ -6,12 +6,14 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:http/http.dart' as http; -import 'package:lotura/data/dto/request/get_apply_list_request.dart'; +import 'package:lotura/data/apply/dto/request/get_apply_list_request.dart'; import 'package:lotura/presentation/app_update_page/ui/app_update_page.dart'; import 'package:lotura/presentation/apply_page/bloc/apply_bloc.dart'; import 'package:lotura/presentation/apply_page/bloc/apply_event.dart'; import 'package:lotura/presentation/laundry_room_page/bloc/laundry_bloc.dart'; import 'package:lotura/presentation/laundry_room_page/bloc/laundry_event.dart'; +import 'package:lotura/presentation/notice_page/bloc/notice_bloc.dart'; +import 'package:lotura/presentation/notice_page/bloc/notice_event.dart'; import 'package:lotura/presentation/utils/bottom_navi.dart'; import 'package:lotura/presentation/utils/lotura_colors.dart'; import 'package:lotura/secret.dart'; @@ -61,11 +63,12 @@ class _SplashPageState extends State { void initState() { super.initState(); checkAppVersion(); + context.read().add(GetNoticeEvent()); context.read().add(GetAllLaundryListEvent()); - context.read().add(GetLaundryEvent()); context .read() .add(GetApplyListEvent(getApplyListRequest: GetApplyListRequest())); + context.read().add(GetLaundryEvent()); } @override diff --git a/lib/presentation/utils/bottom_navi.dart b/lib/presentation/utils/bottom_navi.dart index 3c499a86..c841d181 100644 --- a/lib/presentation/utils/bottom_navi.dart +++ b/lib/presentation/utils/bottom_navi.dart @@ -6,7 +6,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:hive_flutter/hive_flutter.dart'; -import 'package:lotura/data/dto/request/get_apply_list_request.dart'; +import 'package:lotura/data/apply/dto/request/get_apply_list_request.dart'; import 'package:lotura/main.dart'; import 'package:lotura/presentation/apply_page/bloc/apply_bloc.dart'; import 'package:lotura/presentation/apply_page/bloc/apply_event.dart'; diff --git a/lib/presentation/utils/lotura_icons.dart b/lib/presentation/utils/lotura_icons.dart index a5e68c9e..4e179b37 100644 --- a/lib/presentation/utils/lotura_icons.dart +++ b/lib/presentation/utils/lotura_icons.dart @@ -6,22 +6,24 @@ class LoturaIcons { static const _kFontFam = 'LoturaIcons'; static const String? _kFontPkg = null; - static const IconData list = + static const IconData checkCircle = IconData(0xe800, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData grid = + static const IconData disconnected = IconData(0xe801, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData laundry = - IconData(0xe802, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData dry = + IconData(0xe802, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData grid = IconData(0xe803, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData working = + static const IconData list = IconData(0xe804, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData checkCircle = + static const IconData laundry = + IconData(0xe805, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData working = IconData(0xe806, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData disconnected = - IconData(0xe807, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData cancelCircle = IconData(0xe808, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData notice = + IconData(0xf0f3, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData triangleUp = IconData(0xf311, fontFamily: _kFontFam, fontPackage: _kFontPkg); } diff --git a/lib/presentation/utils/machine_button.dart b/lib/presentation/utils/machine_button.dart index 98e1e71a..bef50249 100644 --- a/lib/presentation/utils/machine_button.dart +++ b/lib/presentation/utils/machine_button.dart @@ -6,11 +6,11 @@ import 'package:lotura/presentation/utils/machine_widget.dart'; class MachineButton extends MachineWidget { const MachineButton({ super.key, - required super.index, + required super.deviceId, required super.isEnableNotification, required super.isWoman, required super.state, - required super.machine, + required super.deviceType, }); @override @@ -33,17 +33,18 @@ class MachineButton extends MachineWidget { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Icon(machine.icon, + Icon(deviceType.icon, size: 24.0.r, color: LoturaColors.gray300), Row( children: [ - Text("${isWoman ? index - 31 : index}번", + Text("${isWoman ? deviceId - 31 : deviceId}번", style: TextStyle(fontSize: 16.0.sp)), SizedBox( - width: (isWoman ? index - 31 : index) < 10 + width: (isWoman ? deviceId - 31 : deviceId) < 10 ? 10.2.w : 5.0.w), - Text(machine.text, style: TextStyle(fontSize: 16.0.sp)), + Text(deviceType.text, + style: TextStyle(fontSize: 16.0.sp)), SizedBox(width: 8.0.w), Icon( state.icon, diff --git a/lib/presentation/utils/machine_card.dart b/lib/presentation/utils/machine_card.dart index f1226561..62c54966 100644 --- a/lib/presentation/utils/machine_card.dart +++ b/lib/presentation/utils/machine_card.dart @@ -6,10 +6,10 @@ import 'package:lotura/presentation/utils/osj_status_button.dart'; class MachineCard extends MachineWidget { const MachineCard({ super.key, - required super.index, + required super.deviceId, required super.isEnableNotification, required super.isWoman, - required super.machine, + required super.deviceType, required super.state, }); @@ -30,9 +30,7 @@ class MachineCard extends MachineWidget { children: [ SizedBox(height: 24.0.h), Image.asset( - machine.index == 0 - ? "assets/laundry_image.jpeg" - : "assets/dry_image.jpeg", + deviceType.imagePath, width: 120.0.r, height: 120.0.r, ), @@ -41,14 +39,14 @@ class MachineCard extends MachineWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - "${isWoman ? index - 31 : index}번 ", + "${isWoman ? deviceId - 31 : deviceId}번 ", style: TextStyle( fontSize: 16.0.sp, fontWeight: FontWeight.w500, ), ), Text( - machine.index == 0 ? "세탁기" : "건조기", + deviceType.text, style: TextStyle(fontSize: 16.0.sp), ), ], diff --git a/lib/presentation/utils/machine_widget.dart b/lib/presentation/utils/machine_widget.dart index 7d141a4c..3f1c04a0 100644 --- a/lib/presentation/utils/machine_widget.dart +++ b/lib/presentation/utils/machine_widget.dart @@ -1,27 +1,26 @@ import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart' as m; import 'package:lotura/main.dart'; import 'package:lotura/presentation/utils/osj_bottom_sheet.dart'; abstract class MachineWidget extends StatelessWidget { const MachineWidget({ super.key, - required this.index, + required this.deviceId, required this.isEnableNotification, required this.isWoman, required this.state, - required this.machine, + required this.deviceType, }); - final int index; + final int deviceId; final bool isEnableNotification, isWoman; final CurrentState state; - - final Machine machine; + final DeviceType deviceType; bool get isEmptyContainer => - (!isWoman && index == 32) || (isWoman && index == -1); + (!isWoman && deviceId == 32) || (isWoman && deviceId == -1); void showModalOSJBottomSheet({required BuildContext context}) => showModalBottomSheet( @@ -32,11 +31,11 @@ abstract class MachineWidget extends StatelessWidget { ), ), builder: (context) => OSJBottomSheet( - index: index, + deviceId: deviceId, isEnableNotification: isEnableNotification, isWoman: isWoman, state: state, - machine: machine, + machine: deviceType, ), ); } diff --git a/lib/presentation/utils/osj_bottom_sheet.dart b/lib/presentation/utils/osj_bottom_sheet.dart index f11d109e..7b6712c9 100644 --- a/lib/presentation/utils/osj_bottom_sheet.dart +++ b/lib/presentation/utils/osj_bottom_sheet.dart @@ -1,8 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:lotura/data/dto/request/apply_cancel_request.dart'; -import 'package:lotura/data/dto/request/send_fcm_info_request.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart' as s; import 'package:lotura/main.dart'; import 'package:lotura/presentation/apply_page/bloc/apply_bloc.dart'; import 'package:lotura/presentation/apply_page/bloc/apply_event.dart'; @@ -14,17 +12,17 @@ import 'package:lotura/presentation/utils/osj_text_button.dart'; class OSJBottomSheet extends StatefulWidget { const OSJBottomSheet({ super.key, - required this.index, + required this.deviceId, required this.isEnableNotification, required this.isWoman, required this.state, required this.machine, }); - final int index; + final int deviceId; final bool isEnableNotification, isWoman; final CurrentState state; - final Machine machine; + final DeviceType machine; @override State createState() => _OSJBottomSheetState(); @@ -36,31 +34,31 @@ class _OSJBottomSheetState extends State { if (isWoman) { switch (state) { case CurrentState.working: - return "여자 세탁실 ${widget.index - 31}번 ${widget.machine.text}를\n알림 설정 하실건가요?"; + return "여자 세탁실 ${widget.deviceId - 31}번 ${widget.machine.text}를\n알림 설정 하실건가요?"; case CurrentState.available: - return "여자 세탁실 ${widget.index - 31}번 ${widget.machine.text}는\n현재 사용 가능한 상태에요."; + return "여자 세탁실 ${widget.deviceId - 31}번 ${widget.machine.text}는\n현재 사용 가능한 상태에요."; case CurrentState.disconnected: - return "여자층 ${widget.index - 31}번 ${widget.machine.text}의 연결이 끊겨서\n상태를 확인할 수 없어요."; + return "여자층 ${widget.deviceId - 31}번 ${widget.machine.text}의 연결이 끊겨서\n상태를 확인할 수 없어요."; case CurrentState.breakdown: - return "여자 세탁실 ${widget.index - 31}번 ${widget.machine.text}는\n고장으로 인해 사용이 불가능해요."; + return "여자 세탁실 ${widget.deviceId - 31}번 ${widget.machine.text}는\n고장으로 인해 사용이 불가능해요."; } } else { switch (state) { case CurrentState.working: - return "${widget.index}번 ${widget.machine.text}를\n알림 설정 하실건가요?"; + return "${widget.deviceId}번 ${widget.machine.text}를\n알림 설정 하실건가요?"; case CurrentState.available: - return "${widget.index}번 ${widget.machine.text}는\n현재 사용 가능한 상태에요."; + return "${widget.deviceId}번 ${widget.machine.text}는\n현재 사용 가능한 상태에요."; case CurrentState.disconnected: - return "${widget.index}번 ${widget.machine.text}의 연결이 끊겨서\n상태를 확인할 수 없어요."; + return "${widget.deviceId}번 ${widget.machine.text}의 연결이 끊겨서\n상태를 확인할 수 없어요."; case CurrentState.breakdown: - return "${widget.index}번 ${widget.machine.text}는\n고장으로 인해 사용이 불가능해요."; + return "${widget.deviceId}번 ${widget.machine.text}는\n고장으로 인해 사용이 불가능해요."; } } } else { if (isWoman) { - return "여자 세탁실 ${widget.index - 31}번 ${widget.machine.text}의\n알림 설정을 해제하실건가요?"; + return "여자 세탁실 ${widget.deviceId - 31}번 ${widget.machine.text}의\n알림 설정을 해제하실건가요?"; } else { - return "${widget.index}번 ${widget.machine.text}의\n알림 설정을 해제하실건가요?"; + return "${widget.deviceId}번 ${widget.machine.text}의\n알림 설정을 해제하실건가요?"; } } } @@ -137,15 +135,13 @@ class _OSJBottomSheetState extends State { function: () { widget.isEnableNotification ? context.read().add(SendFCMEvent( - sendFCMInfoRequest: SendFCMInfoRequest( - deviceId: widget.index.toString(), - expectState: '1'))) - : context.read().add( - ApplyCancelEvent( - applyCancelRequest: - ApplyCancelRequest( - deviceId: widget.index - .toString()))); + deviceId: widget.deviceId, + deviceType: widget.machine)) + : context + .read() + .add(ApplyCancelEvent( + deviceId: widget.deviceId, + )); context .read() .add(ClosingBottomSheetEvent()); diff --git a/lib/presentation/utils/vibrating_widget.dart b/lib/presentation/utils/vibrating_widget.dart new file mode 100644 index 00000000..ac8722fd --- /dev/null +++ b/lib/presentation/utils/vibrating_widget.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +class VibratingWidget extends StatefulWidget { + const VibratingWidget({super.key, required this.child}); + + final Widget child; + + @override + State createState() => _VibratingWidgetState(); +} + +class _VibratingWidgetState extends State + with SingleTickerProviderStateMixin { + late AnimationController _animationController; + late Animation _animation; + + @override + void initState() { + super.initState(); + _animationController = AnimationController( + duration: const Duration(milliseconds: 100), vsync: this); + + _animation = Tween(begin: 0.0, end: 4.0.r).animate(_animationController); + + _animationController.repeat(); + } + + @override + void dispose() { + _animationController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return AnimatedBuilder( + animation: _animation, + builder: (context, child) => Transform.translate( + offset: Offset(_animation.value - 2.0.r, 0), + child: widget.child, + ), + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 19971eee..ad77423a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 3.3.7+21 +version: 3.4.0+20 environment: sdk: '>=3.1.3 <4.0.0'