| class NotificationDTO { | |||||
| int id; | |||||
| String subject; | |||||
| String message; | |||||
| int tbCropId; | |||||
| int tbEntityId; | |||||
| String contents; | |||||
| String createdDate; | |||||
| String sendDate; | |||||
| int isRead; | |||||
| NotificationDTO( | |||||
| {this.id, | |||||
| this.subject, | |||||
| this.message, | |||||
| this.tbCropId, | |||||
| this.tbEntityId, | |||||
| this.contents, | |||||
| this.createdDate, | |||||
| this.sendDate, | |||||
| this.isRead}); | |||||
| NotificationDTO.clone(NotificationDTO noti) { | |||||
| this.id = noti.id; | |||||
| this.contents = noti.contents; | |||||
| this.tbCropId = noti.tbCropId; | |||||
| this.tbEntityId = noti.tbEntityId; | |||||
| this.subject = noti.subject; | |||||
| this.message = noti.message; | |||||
| this.createdDate = noti.createdDate; | |||||
| this.sendDate = noti.sendDate; | |||||
| this.isRead = noti.isRead; | |||||
| } | |||||
| NotificationDTO.fromJson(Map<String, dynamic> json) { | |||||
| id = json['id']; | |||||
| subject = json['subject']; | |||||
| message = json['message']; | |||||
| tbCropId = json['tbCropId']; | |||||
| tbEntityId = json['tbEntityId']; | |||||
| contents = json['contents']; | |||||
| createdDate = json['createdDate']; | |||||
| sendDate = json['sendDate']; | |||||
| isRead = json['isRead']; | |||||
| } | |||||
| Map<String, dynamic> toJson() { | |||||
| final Map<String, dynamic> data = new Map<String, dynamic>(); | |||||
| data['id'] = this.id; | |||||
| data['subject'] = this.subject; | |||||
| data['message'] = this.message; | |||||
| data['tbCropId'] = this.tbCropId; | |||||
| data['tbEntityId'] = this.tbEntityId; | |||||
| data['contents'] = this.contents; | |||||
| data['createdDate'] = this.createdDate; | |||||
| data['sendDate'] = this.sendDate; | |||||
| data['isRead'] = this.isRead; | |||||
| return data; | |||||
| } | |||||
| } |
| import 'NotificationDTO.dart'; | |||||
| class NotificationObjectDTO { | |||||
| int numberUnreadPage; | |||||
| int numberReadPage; | |||||
| int numberUnreadTotal; | |||||
| int numberReadTotal; | |||||
| List<NotificationDTO> notificationDTO; | |||||
| NotificationObjectDTO( | |||||
| {this.numberUnreadPage, | |||||
| this.numberReadPage, | |||||
| this.numberUnreadTotal, | |||||
| this.numberReadTotal, | |||||
| this.notificationDTO}); | |||||
| NotificationObjectDTO.fromJson(Map<String, dynamic> json) { | |||||
| numberUnreadPage = json['numberUnreadPage']; | |||||
| numberReadPage = json['numberReadPage']; | |||||
| numberUnreadTotal = json['numberUnreadTotal']; | |||||
| numberReadTotal = json['numberReadTotal']; | |||||
| if (json['tbnotificationDTOs'] != null) { | |||||
| notificationDTO = new List<NotificationDTO>(); | |||||
| json['tbnotificationDTOs'].forEach((v) { | |||||
| notificationDTO.add(new NotificationDTO.fromJson(v)); | |||||
| }); | |||||
| } | |||||
| } | |||||
| Map<String, dynamic> toJson() { | |||||
| final Map<String, dynamic> data = new Map<String, dynamic>(); | |||||
| data['numberUnreadPage'] = this.numberUnreadPage; | |||||
| data['numberReadPage'] = this.numberReadPage; | |||||
| data['numberUnreadTotal'] = this.numberUnreadTotal; | |||||
| data['numberReadTotal'] = this.numberReadTotal; | |||||
| if (this.notificationDTO != null) { | |||||
| data['tbnotificationDTOs'] = | |||||
| this.notificationDTO.map((v) => v.toJson()).toList(); | |||||
| } | |||||
| return data; | |||||
| } | |||||
| } |
| class UpdateNoti { | |||||
| int id; | |||||
| int isRead; | |||||
| UpdateNoti({this.id, this.isRead}); | |||||
| UpdateNoti.fromJson(Map<String, dynamic> json) { | |||||
| id = json['id']; | |||||
| isRead = json['isRead']; | |||||
| } | |||||
| Map<String, dynamic> toJson() { | |||||
| final Map<String, dynamic> data = new Map<String, dynamic>(); | |||||
| data['id'] = this.id; | |||||
| data['isRead'] = this.isRead; | |||||
| return data; | |||||
| } | |||||
| } |
| @override | @override | ||||
| Future onResponse(Response response) { | Future onResponse(Response response) { | ||||
| // log("onResponse: $response"); | |||||
| log("onResponse: $response"); | |||||
| return super.onResponse(response); | return super.onResponse(response); | ||||
| } | } | ||||
| @override | @override | ||||
| Future onError(DioError err) { | Future onError(DioError err) { | ||||
| // log("onError: $err\n" | |||||
| // "Response: ${err.response}"); | |||||
| log("onError: $err\n" | |||||
| "Response: ${err.response}"); | |||||
| return super.onError(err); | return super.onError(err); | ||||
| } | } | ||||
| } | } |
| import 'package:farm_tpf/custom_model/EnvironmentParameter.dart'; | import 'package:farm_tpf/custom_model/EnvironmentParameter.dart'; | ||||
| import 'package:farm_tpf/custom_model/Harvest.dart'; | import 'package:farm_tpf/custom_model/Harvest.dart'; | ||||
| import 'package:farm_tpf/custom_model/Supply.dart'; | import 'package:farm_tpf/custom_model/Supply.dart'; | ||||
| import 'package:farm_tpf/custom_model/UpdateNoti.dart'; | |||||
| import 'package:farm_tpf/custom_model/WaterType.dart'; | import 'package:farm_tpf/custom_model/WaterType.dart'; | ||||
| import 'package:farm_tpf/custom_model/account.dart'; | import 'package:farm_tpf/custom_model/account.dart'; | ||||
| import 'package:farm_tpf/custom_model/password.dart'; | import 'package:farm_tpf/custom_model/password.dart'; | ||||
| @GET("/api/listDeviceForActivity") | @GET("/api/listDeviceForActivity") | ||||
| Future<List<Device>> getDeviceForActivity({@DioOptions() Options options}); | Future<List<Device>> getDeviceForActivity({@DioOptions() Options options}); | ||||
| @PUT("/api/notifications/update-all") | |||||
| Future<void> updateAllNotification(@Body() String status); | |||||
| @PUT("/api/notifications/update") | |||||
| Future<void> updateNoti(@Body() UpdateNoti updateNoti); | |||||
| //Crop | //Crop | ||||
| @GET( | @GET( | ||||
| "/api/tb-crops-detail-for-app/{cropId}?page={page}&size={size}&sort=executeDate,DESC") | "/api/tb-crops-detail-for-app/{cropId}?page={page}&size={size}&sort=executeDate,DESC") |
| return value; | return value; | ||||
| } | } | ||||
| @override | |||||
| updateAllNotification(status) async { | |||||
| ArgumentError.checkNotNull(status, 'status'); | |||||
| const _extra = <String, dynamic>{}; | |||||
| final queryParameters = <String, dynamic>{}; | |||||
| final _data = status; | |||||
| await _dio.request<void>('/api/notifications/update-all', | |||||
| queryParameters: queryParameters, | |||||
| options: RequestOptions( | |||||
| method: 'PUT', | |||||
| headers: <String, dynamic>{}, | |||||
| extra: _extra, | |||||
| baseUrl: baseUrl), | |||||
| data: _data); | |||||
| return null; | |||||
| } | |||||
| @override | |||||
| updateNoti(updateNoti) async { | |||||
| ArgumentError.checkNotNull(updateNoti, 'updateNoti'); | |||||
| const _extra = <String, dynamic>{}; | |||||
| final queryParameters = <String, dynamic>{}; | |||||
| final _data = <String, dynamic>{}; | |||||
| _data.addAll(updateNoti?.toJson() ?? <String, dynamic>{}); | |||||
| await _dio.request<void>('/api/notifications/update', | |||||
| queryParameters: queryParameters, | |||||
| options: RequestOptions( | |||||
| method: 'PUT', | |||||
| headers: <String, dynamic>{}, | |||||
| extra: _extra, | |||||
| baseUrl: baseUrl), | |||||
| data: _data); | |||||
| return null; | |||||
| } | |||||
| @override | @override | ||||
| getCropDetail(cropId, {page = 0, size = 20}) async { | getCropDetail(cropId, {page = 0, size = 20}) async { | ||||
| ArgumentError.checkNotNull(cropId, 'cropId'); | ArgumentError.checkNotNull(cropId, 'cropId'); |
| import 'package:farm_tpf/custom_model/Device.dart'; | import 'package:farm_tpf/custom_model/Device.dart'; | ||||
| import 'package:farm_tpf/custom_model/EnvironmentParameter.dart'; | import 'package:farm_tpf/custom_model/EnvironmentParameter.dart'; | ||||
| import 'package:farm_tpf/custom_model/Harvest.dart'; | import 'package:farm_tpf/custom_model/Harvest.dart'; | ||||
| import 'package:farm_tpf/custom_model/NotificationDTO.dart'; | |||||
| import 'package:farm_tpf/custom_model/NotificationObjectDTO.dart'; | |||||
| import 'package:farm_tpf/custom_model/Supply.dart'; | import 'package:farm_tpf/custom_model/Supply.dart'; | ||||
| import 'package:farm_tpf/custom_model/UpdateNoti.dart'; | |||||
| import 'package:farm_tpf/custom_model/WaterType.dart'; | import 'package:farm_tpf/custom_model/WaterType.dart'; | ||||
| import 'package:farm_tpf/custom_model/user.dart'; | import 'package:farm_tpf/custom_model/user.dart'; | ||||
| import 'package:farm_tpf/custom_model/user_request.dart'; | import 'package:farm_tpf/custom_model/user_request.dart'; | ||||
| return client.getDeviceForActivity(options: op); | return client.getDeviceForActivity(options: op); | ||||
| } | } | ||||
| Future<void> updateAllNotification(String status) { | |||||
| final client = RestClient(dio); | |||||
| return client.updateAllNotification(status); | |||||
| } | |||||
| Future<void> updateNoti(UpdateNoti updateNoti) { | |||||
| final client = RestClient(dio); | |||||
| return client.updateNoti(updateNoti); | |||||
| } | |||||
| Future<NotificationObjectDTO> getNotifications( | |||||
| {int page = 0, int size = 20}) async { | |||||
| var url = ConstCommon.baseUrl + | |||||
| "/api/notifications-current-user?page=$page&size=$size&sort=sendDate,DESC"; | |||||
| var response = await dio.get(url); | |||||
| final value = NotificationObjectDTO.fromJson(response.data); | |||||
| return value; | |||||
| } | |||||
| Object getInstanceClass() { | Object getInstanceClass() { | ||||
| var instanceClass; | var instanceClass; | ||||
| if (1 == 1) { | if (1 == 1) { |
| import 'dart:async'; | |||||
| import 'package:bloc/bloc.dart'; | |||||
| import 'package:equatable/equatable.dart'; | |||||
| import 'package:farm_tpf/custom_model/NotificationDTO.dart'; | |||||
| import 'package:farm_tpf/custom_model/NotificationObjectDTO.dart'; | |||||
| import 'package:farm_tpf/custom_model/UpdateNoti.dart'; | |||||
| import 'package:farm_tpf/data/api/app_exception.dart'; | |||||
| import 'package:farm_tpf/data/repository/repository.dart'; | |||||
| import 'package:farm_tpf/utils/const_string.dart'; | |||||
| import 'package:meta/meta.dart'; | |||||
| part 'noti_event.dart'; | |||||
| part 'noti_state.dart'; | |||||
| class NotiBloc extends Bloc<NotiEvent, NotiState> { | |||||
| final Repository repository; | |||||
| NotiBloc({@required this.repository}) : super(NotiInitial()); | |||||
| static int pageSize = 20; | |||||
| @override | |||||
| Stream<NotiState> mapEventToState( | |||||
| NotiEvent event, | |||||
| ) async* { | |||||
| if (event is DataFetched && | |||||
| !(state is NotiSuccess && (state as NotiSuccess).hasReachedMax)) { | |||||
| try { | |||||
| if (state is NotiInitial) { | |||||
| yield NotiLoadding(); | |||||
| final response = | |||||
| await repository.getNotifications(page: 0, size: pageSize); | |||||
| yield NotiSuccess( | |||||
| unread: response.numberUnreadTotal, | |||||
| read: response.numberReadTotal, | |||||
| items: response.notificationDTO, | |||||
| page: 0, | |||||
| hasReachedMax: | |||||
| response.notificationDTO.length < pageSize ? true : false); | |||||
| } | |||||
| if (state is NotiSuccess) { | |||||
| final currentState = state as NotiSuccess; | |||||
| int page = currentState.page + 1; | |||||
| final response = | |||||
| await repository.getNotifications(page: page, size: pageSize); | |||||
| yield response.notificationDTO.isEmpty | |||||
| ? currentState.copyWith(hasReachedMax: true) | |||||
| : NotiSuccess( | |||||
| unread: response.numberUnreadTotal, | |||||
| read: response.numberReadTotal, | |||||
| items: currentState.items + response.notificationDTO, | |||||
| page: currentState.page + 1, | |||||
| hasReachedMax: false); | |||||
| } | |||||
| } catch (e) { | |||||
| yield NotiFailure(errorString: AppException.handleError(e)); | |||||
| } | |||||
| } | |||||
| if (event is OnRefresh) { | |||||
| yield NotiLoadding(); | |||||
| try { | |||||
| final response = | |||||
| await repository.getNotifications(page: 0, size: pageSize); | |||||
| List<NotificationDTO> items = new List<NotificationDTO>(); | |||||
| response.notificationDTO | |||||
| .forEach((e) => items.add(NotificationDTO.clone(e))); | |||||
| yield NotiSuccess( | |||||
| unread: response.numberUnreadTotal, | |||||
| read: response.numberReadTotal, | |||||
| items: items, | |||||
| page: 0, | |||||
| hasReachedMax: | |||||
| response.notificationDTO.length < pageSize ? true : false); | |||||
| } catch (e) { | |||||
| yield NotiFailure(errorString: AppException.handleError(e)); | |||||
| } | |||||
| } | |||||
| if (event is OnUpdate) { | |||||
| yield NotiLoadding(); | |||||
| try { | |||||
| //Change status notification if mark read | |||||
| if (event.currentItemId != null) { | |||||
| var updateNoti = UpdateNoti() | |||||
| ..id = event.currentItemId | |||||
| ..isRead = 1; | |||||
| await repository.updateNoti(updateNoti); | |||||
| } | |||||
| yield NotiSuccess( | |||||
| unread: event.unread, | |||||
| read: event.read, | |||||
| items: event.currentItems, | |||||
| page: event.currentPage, | |||||
| hasReachedMax: event.hasReachedMax); | |||||
| } catch (e) { | |||||
| yield NotiFailure(errorString: exception_common); | |||||
| } | |||||
| } else if (event is MarkAllNotificationUpdate) { | |||||
| yield NotiLoadding(); | |||||
| try { | |||||
| await repository.updateAllNotification(event.status); | |||||
| final response = | |||||
| await repository.getNotifications(page: 0, size: pageSize); | |||||
| List<NotificationDTO> items = new List<NotificationDTO>(); | |||||
| response.notificationDTO | |||||
| .forEach((e) => items.add(NotificationDTO.clone(e))); | |||||
| yield NotiSuccess( | |||||
| unread: response.numberUnreadTotal, | |||||
| read: response.numberReadTotal, | |||||
| items: items, | |||||
| page: 0, | |||||
| hasReachedMax: | |||||
| response.notificationDTO.length < pageSize ? true : false); | |||||
| } catch (e) { | |||||
| yield NotiFailure(errorString: exception_common); | |||||
| } | |||||
| } else if (event is ReceiveDataFromSocket) { | |||||
| List<NotificationDTO> updatedItems = new List<NotificationDTO>(); | |||||
| event.updatedItemObject.notificationDTO.forEach((e) { | |||||
| updatedItems.add(NotificationDTO.clone(e)); | |||||
| }); | |||||
| event.currentItems.forEach((e) { | |||||
| updatedItems.add(NotificationDTO.clone(e)); | |||||
| }); | |||||
| yield NotiSuccess( | |||||
| unread: event.updatedItemObject.numberUnreadTotal, | |||||
| read: event.updatedItemObject.numberReadTotal, | |||||
| items: updatedItems, | |||||
| page: event.page, | |||||
| hasReachedMax: updatedItems.length < pageSize ? true : false); | |||||
| } | |||||
| } | |||||
| } |
| part of 'noti_bloc.dart'; | |||||
| abstract class NotiEvent extends Equatable { | |||||
| const NotiEvent(); | |||||
| @override | |||||
| List<Object> get props => []; | |||||
| } | |||||
| class DataFetched extends NotiEvent {} | |||||
| class OnRefresh extends NotiEvent {} | |||||
| class OnUpdate<T> extends NotiEvent { | |||||
| final int unread; | |||||
| final int read; | |||||
| final int currentItemId; | |||||
| final List<T> currentItems; | |||||
| final int currentPage; | |||||
| final bool hasReachedMax; | |||||
| OnUpdate( | |||||
| {@required this.unread, | |||||
| @required this.read, | |||||
| this.currentItemId, | |||||
| @required this.currentItems, | |||||
| @required this.currentPage, | |||||
| @required this.hasReachedMax}); | |||||
| } | |||||
| class MarkAllNotificationUpdate extends NotiEvent { | |||||
| final String status; | |||||
| MarkAllNotificationUpdate({@required this.status}); | |||||
| } | |||||
| class ReceiveDataFromSocket extends NotiEvent { | |||||
| final List<NotificationDTO> currentItems; | |||||
| final int page; | |||||
| final bool hasReachedMax; | |||||
| final NotificationObjectDTO updatedItemObject; | |||||
| ReceiveDataFromSocket( | |||||
| {@required this.currentItems, | |||||
| this.page, | |||||
| this.hasReachedMax, | |||||
| this.updatedItemObject}); | |||||
| } |
| part of 'noti_bloc.dart'; | |||||
| abstract class NotiState extends Equatable { | |||||
| const NotiState(); | |||||
| @override | |||||
| List<Object> get props => []; | |||||
| } | |||||
| class NotiInitial extends NotiState {} | |||||
| class NotiLoadding extends NotiState {} | |||||
| class NotiFailure extends NotiState { | |||||
| final String errorString; | |||||
| NotiFailure({@required this.errorString}); | |||||
| } | |||||
| class NotiSuccess<T> extends NotiState { | |||||
| final int unread; | |||||
| final int read; | |||||
| final List<T> items; | |||||
| final int page; | |||||
| final bool hasReachedMax; | |||||
| const NotiSuccess( | |||||
| {this.unread, this.read, this.items, this.page, this.hasReachedMax}); | |||||
| NotiSuccess copyWith({List<T> items, int page, bool hasReachedMax}) { | |||||
| return NotiSuccess( | |||||
| unread: unread ?? this.unread, | |||||
| read: read ?? this.read, | |||||
| items: items ?? this.items, | |||||
| page: page ?? this.page, | |||||
| hasReachedMax: hasReachedMax ?? this.hasReachedMax); | |||||
| } | |||||
| @override | |||||
| List<Object> get props => [items, hasReachedMax]; | |||||
| } |
| import 'package:farm_tpf/custom_model/NotificationDTO.dart'; | |||||
| import 'package:farm_tpf/data/repository/repository.dart'; | |||||
| import 'package:farm_tpf/presentation/custom_widgets/bottom_loader.dart'; | |||||
| import 'package:farm_tpf/presentation/custom_widgets/loading_list_page.dart'; | |||||
| import 'package:farm_tpf/presentation/screens/plot_detail/sc_plot_detail.dart'; | |||||
| import 'package:farm_tpf/presentation/screens/plot_detail/sc_plot_information.dart'; | |||||
| import 'package:farm_tpf/utils/pref.dart'; | |||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_bloc/flutter_bloc.dart'; | |||||
| import 'package:farm_tpf/utils/formatter.dart'; | |||||
| import 'bloc/noti_bloc.dart'; | |||||
| class NotificationScreen extends StatefulWidget { | class NotificationScreen extends StatefulWidget { | ||||
| @override | @override | ||||
| } | } | ||||
| class _NotificationScreenState extends State<NotificationScreen> { | class _NotificationScreenState extends State<NotificationScreen> { | ||||
| @override | |||||
| Widget build(BuildContext context) { | |||||
| return BlocProvider( | |||||
| create: (context) => | |||||
| NotiBloc(repository: Repository())..add(DataFetched()), | |||||
| child: HoldInfinityWidget(), | |||||
| ); | |||||
| } | |||||
| } | |||||
| class HoldInfinityWidget extends StatelessWidget { | |||||
| final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>(); | |||||
| @override | @override | ||||
| Widget build(BuildContext context) { | Widget build(BuildContext context) { | ||||
| return Scaffold( | return Scaffold( | ||||
| appBar: AppBar( | |||||
| title: Text("Thông báo"), | |||||
| key: _scaffoldKey, | |||||
| appBar: AppBar( | |||||
| centerTitle: true, | |||||
| title: Text("Thông báo"), | |||||
| actions: <Widget>[ | |||||
| IconButton( | |||||
| icon: Icon(Icons.done_all), | |||||
| onPressed: () { | |||||
| BlocProvider.of<NotiBloc>(context) | |||||
| .add(MarkAllNotificationUpdate(status: "1")); | |||||
| }) | |||||
| ], | |||||
| ), | |||||
| body: InfinityView()); | |||||
| } | |||||
| } | |||||
| class InfinityView extends StatefulWidget { | |||||
| @override | |||||
| _InfinityViewState createState() => _InfinityViewState(); | |||||
| } | |||||
| class _InfinityViewState extends State<InfinityView> { | |||||
| final _scrollController = ScrollController(); | |||||
| final _scrollThreshold = 250.0; | |||||
| NotiBloc _notiBloc; | |||||
| List<NotificationDTO> currentItems = new List<NotificationDTO>(); | |||||
| int latestId = 0; | |||||
| int currentPage = 0; | |||||
| bool currentHasReachedMax = true; | |||||
| bool isUpdatingFromApi = true; | |||||
| var pref = LocalPref(); | |||||
| // final SocketService socketService = injector.get<SocketService>(); | |||||
| var token; | |||||
| Future<Null> getSharedPrefs() async { | |||||
| _scrollController.addListener(() { | |||||
| final maxScroll = _scrollController.position.maxScrollExtent; | |||||
| final currentScroll = _scrollController.position.pixels; | |||||
| if (maxScroll - currentScroll < _scrollThreshold) { | |||||
| _notiBloc.add(DataFetched()); | |||||
| } | |||||
| }); | |||||
| token = await pref.getString(DATA_CONST.TOKEN_KEY); | |||||
| // socketService.initial(token); | |||||
| _notiBloc = BlocProvider.of<NotiBloc>(context); | |||||
| } | |||||
| @override | |||||
| void initState() { | |||||
| getSharedPrefs(); | |||||
| super.initState(); | |||||
| } | |||||
| @override | |||||
| Widget build(BuildContext context) { | |||||
| return BlocConsumer<NotiBloc, NotiState>( | |||||
| listener: (context, state) { | |||||
| //Handle Socket | |||||
| /* | |||||
| if (state is NotiLoadding) { | |||||
| isUpdatingFromApi = true; | |||||
| } | |||||
| if (state is NotiSuccess) { | |||||
| currentItems = List<NotificationDTO>.from(state.items); | |||||
| latestId = int.parse(currentItems[0].id) > latestId | |||||
| ? int.parse(currentItems[0].id) | |||||
| : latestId; | |||||
| socketService.createSocketNotificationConnection(latestId, (data) { | |||||
| print("receive data"); | |||||
| if (isUpdatingFromApi == false) { | |||||
| print("Update from socket"); | |||||
| var notis = | |||||
| NotificationObjectDTO.fromJson(data, NotificationDTO()); | |||||
| _notiBloc.add(ReceiveDataFromSocket( | |||||
| currentItems: currentItems, | |||||
| page: currentPage, | |||||
| hasReachedMax: currentHasReachedMax, | |||||
| updatedItemObject: notis)); | |||||
| } else { | |||||
| //dont need update from socket | |||||
| print("dont need update from socket"); | |||||
| } | |||||
| }, (error) {}); | |||||
| isUpdatingFromApi = false; | |||||
| } | |||||
| */ | |||||
| }, | |||||
| builder: (context, state) { | |||||
| if (state is NotiFailure) { | |||||
| return Center(child: Text(state.errorString)); | |||||
| } | |||||
| if (state is NotiSuccess) { | |||||
| if (state.items.isEmpty) { | |||||
| return Center(child: Text("Dữ liệu rỗng")); | |||||
| } | |||||
| currentItems = List<NotificationDTO>.from(state.items); | |||||
| currentPage = state.page; | |||||
| currentHasReachedMax = state.hasReachedMax; | |||||
| return Column( | |||||
| children: <Widget>[ | |||||
| Container( | |||||
| child: | |||||
| countNotification(unread: state.unread, read: state.read), | |||||
| ), | |||||
| Expanded( | |||||
| child: RefreshIndicator( | |||||
| child: ListView.builder( | |||||
| itemBuilder: (BuildContext context, int index) { | |||||
| return index >= state.items.length | |||||
| ? BottomLoader() | |||||
| : ItemInfinityWidget( | |||||
| unread: state.unread, | |||||
| read: state.read, | |||||
| currentItems: currentItems, | |||||
| item: state.items[index], | |||||
| currentPage: state.page, | |||||
| currentReachedMax: state.hasReachedMax, | |||||
| ); | |||||
| }, | |||||
| itemCount: state.hasReachedMax | |||||
| ? state.items.length | |||||
| : state.items.length + 1, | |||||
| controller: _scrollController, | |||||
| ), | |||||
| onRefresh: () async { | |||||
| _notiBloc.add(OnRefresh()); | |||||
| })) | |||||
| ], | |||||
| ); | |||||
| } | |||||
| return Center( | |||||
| child: LoadingListPage(), | |||||
| ); | |||||
| }, | |||||
| ); | |||||
| } | |||||
| Widget countNotification({num unread, num read}) { | |||||
| return Container( | |||||
| child: Row( | |||||
| mainAxisAlignment: MainAxisAlignment.center, | |||||
| mainAxisSize: MainAxisSize.max, | |||||
| children: <Widget>[ | |||||
| Expanded( | |||||
| child: OutlineButton( | |||||
| child: RichText( | |||||
| text: TextSpan( | |||||
| text: "Chưa đọc ", | |||||
| style: TextStyle( | |||||
| color: Colors.black, fontWeight: FontWeight.bold), | |||||
| children: <TextSpan>[ | |||||
| TextSpan( | |||||
| text: "($unread)", | |||||
| style: TextStyle(color: Colors.blue)), | |||||
| ])), | |||||
| onPressed: () {})), | |||||
| Expanded( | |||||
| child: OutlineButton( | |||||
| child: RichText( | |||||
| text: TextSpan( | |||||
| text: "Đã đọc ", | |||||
| style: TextStyle( | |||||
| color: Colors.black, fontWeight: FontWeight.bold), | |||||
| children: <TextSpan>[ | |||||
| TextSpan( | |||||
| text: "($read)", | |||||
| style: TextStyle(color: Colors.grey)), | |||||
| ])), | |||||
| onPressed: () {})) | |||||
| ], | |||||
| ), | ), | ||||
| ); | ); | ||||
| } | } | ||||
| @override | |||||
| void dispose() { | |||||
| _scrollController.dispose(); | |||||
| // socketService.disconnect(); | |||||
| super.dispose(); | |||||
| } | |||||
| } | |||||
| class ItemInfinityWidget extends StatelessWidget { | |||||
| final int unread; | |||||
| final int read; | |||||
| final NotificationDTO item; | |||||
| final List<NotificationDTO> currentItems; | |||||
| final int currentPage; | |||||
| final bool currentReachedMax; | |||||
| const ItemInfinityWidget( | |||||
| {Key key, | |||||
| @required this.unread, | |||||
| @required this.read, | |||||
| @required this.currentItems, | |||||
| @required this.item, | |||||
| @required this.currentPage, | |||||
| @required this.currentReachedMax}) | |||||
| : super(key: key); | |||||
| @override | |||||
| Widget build(BuildContext context) { | |||||
| return GestureDetector( | |||||
| child: Card( | |||||
| margin: EdgeInsets.all(4.0), | |||||
| child: ListTile( | |||||
| title: Text(item.subject), | |||||
| subtitle: Column( | |||||
| crossAxisAlignment: CrossAxisAlignment.start, | |||||
| children: <Widget>[ | |||||
| Text(item.message), | |||||
| Text( | |||||
| item.sendDate.format_DDMMYY_HHmm(), | |||||
| style: TextStyle( | |||||
| color: item.isRead == 1 ? Colors.grey : Colors.blue), | |||||
| ), | |||||
| ], | |||||
| ), | |||||
| leading: Icon( | |||||
| Icons.notifications_active, | |||||
| color: item.isRead == 1 ? Colors.grey : Colors.blue, | |||||
| ))), | |||||
| onTap: () { | |||||
| if (item.contents == "ENV_UPDATE") { | |||||
| Navigator.push( | |||||
| context, | |||||
| MaterialPageRoute( | |||||
| builder: (BuildContext context) => | |||||
| PlotDetailScreen(cropId: item.tbCropId))).then((value) { | |||||
| if (item.isRead == 0) { | |||||
| _updateReadNotification( | |||||
| context: context, | |||||
| unread: unread, | |||||
| read: read, | |||||
| item: item, | |||||
| currentItems: currentItems, | |||||
| currentPage: currentPage, | |||||
| currentReachedMax: currentReachedMax); | |||||
| } | |||||
| }); | |||||
| } else if (item.contents == "PIC_UPDATE") { | |||||
| Navigator.push( | |||||
| context, | |||||
| MaterialPageRoute( | |||||
| builder: (BuildContext context) => PlotInformationScreen( | |||||
| cropId: item.tbCropId, | |||||
| ))).then((value) { | |||||
| if (item.isRead == 0) { | |||||
| _updateReadNotification( | |||||
| context: context, | |||||
| unread: unread, | |||||
| read: read, | |||||
| item: item, | |||||
| currentItems: currentItems, | |||||
| currentPage: currentPage, | |||||
| currentReachedMax: currentReachedMax); | |||||
| } | |||||
| }); | |||||
| } else {} | |||||
| }); | |||||
| } | |||||
| _updateReadNotification( | |||||
| {BuildContext context, | |||||
| int unread, | |||||
| int read, | |||||
| NotificationDTO item, | |||||
| List<NotificationDTO> currentItems, | |||||
| int currentPage, | |||||
| bool currentReachedMax}) { | |||||
| List<NotificationDTO> updatedItems = new List<NotificationDTO>(); | |||||
| currentItems.forEach((e) { | |||||
| if (e.id == item.id) { | |||||
| e.isRead = 1; | |||||
| } else {} | |||||
| updatedItems.add(NotificationDTO.clone(e)); | |||||
| }); | |||||
| BlocProvider.of<NotiBloc>(context).add(OnUpdate<NotificationDTO>( | |||||
| unread: unread - 1, | |||||
| read: read + 1, | |||||
| currentItemId: item.id, | |||||
| currentItems: updatedItems, | |||||
| currentPage: currentPage, | |||||
| hasReachedMax: currentReachedMax)); | |||||
| } | |||||
| } | } |