Fixed notification pagination for all and unread
This commit is contained in:
@@ -5,80 +5,80 @@ import 'package:tm_app/data/repositories/notification_repository.dart';
|
|||||||
import 'package:tm_app/data/services/model/notification__response/notification_response_model.dart';
|
import 'package:tm_app/data/services/model/notification__response/notification_response_model.dart';
|
||||||
import 'package:tm_app/views/notification/strings/notification_strings.dart';
|
import 'package:tm_app/views/notification/strings/notification_strings.dart';
|
||||||
|
|
||||||
|
|
||||||
class NotificationViewModel with ChangeNotifier {
|
class NotificationViewModel with ChangeNotifier {
|
||||||
final NotificationsRepository _repository;
|
final NotificationsRepository _repository;
|
||||||
|
|
||||||
NotificationViewModel({required NotificationsRepository repositoryViewModel})
|
NotificationViewModel({required NotificationsRepository repositoryViewModel})
|
||||||
: _repository = repositoryViewModel {
|
: _repository = repositoryViewModel {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
getNotifications();
|
getNotifications();
|
||||||
getUnreadNotifications();
|
getUnreadNotifications();
|
||||||
|
getImportantNotifications();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _isLoading = false;
|
|
||||||
String? _errorMessage;
|
|
||||||
NotificationResponseModel? _notificationResponseModel;
|
|
||||||
|
|
||||||
int currentPage = 1;
|
|
||||||
static const int _pageSize = 10;
|
static const int _pageSize = 10;
|
||||||
int _totalPages = 0;
|
|
||||||
bool _hasMoreData = true;
|
|
||||||
|
|
||||||
bool get isLoading => _isLoading;
|
String? _errorMessage;
|
||||||
String? get errorMessage => _errorMessage;
|
String? get errorMessage => _errorMessage;
|
||||||
NotificationResponseModel? get notificationResponse =>
|
|
||||||
_notificationResponseModel;
|
|
||||||
bool get hasMoreData => _hasMoreData;
|
|
||||||
int get totalPages => _totalPages;
|
|
||||||
|
|
||||||
NotificationResponseModel? _unreadNotificationResponse;
|
// All
|
||||||
bool _isUnreadLoading = false;
|
NotificationResponseModel? allNotificationResponse;
|
||||||
|
bool isLoadingAll = false;
|
||||||
|
bool isMoreLoadingAll = false;
|
||||||
|
bool hasMoreAll = true;
|
||||||
|
int currentPageAll = 1;
|
||||||
|
int totalPagesAll = 0;
|
||||||
|
|
||||||
NotificationResponseModel? get unreadNotificationResponse =>
|
// Unread
|
||||||
_unreadNotificationResponse;
|
NotificationResponseModel? unreadNotificationResponse;
|
||||||
bool get isUnreadLoading => _isUnreadLoading;
|
bool isLoadingUnread = false;
|
||||||
|
bool isMoreLoadingUnread = false;
|
||||||
|
bool hasMoreUnread = true;
|
||||||
|
int currentPageUnread = 1;
|
||||||
|
int totalPagesUnread = 0;
|
||||||
|
|
||||||
bool _isMoreLoading = false;
|
// Important
|
||||||
bool get isMoreLoading => _isMoreLoading;
|
NotificationResponseModel? importantNotificationResponse;
|
||||||
|
bool isLoadingImportant = false;
|
||||||
|
bool isMoreLoadingImportant = false;
|
||||||
|
bool hasMoreImportant = true;
|
||||||
|
int currentPageImportant = 1;
|
||||||
|
int totalPagesImportant = 0;
|
||||||
|
|
||||||
/// Fetch notifications
|
|
||||||
Future<void> getNotifications({int page = 1, bool isMobile = false}) async {
|
Future<void> getNotifications({int page = 1, bool isPagination = false}) async {
|
||||||
if (isMobile && page > 1) {
|
if (isPagination) {
|
||||||
_isMoreLoading = true;
|
isMoreLoadingAll = true;
|
||||||
} else {
|
} else {
|
||||||
_isLoading = true;
|
isLoadingAll = true;
|
||||||
}
|
}
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
|
||||||
final int offset = _pageSize * page;
|
final int offset = (page - 1) * _pageSize;
|
||||||
|
final result = await _repository.getNotifications(limit: _pageSize, offset: offset);
|
||||||
final result = await _repository.getNotifications(
|
|
||||||
limit: _pageSize,
|
|
||||||
offset: offset,
|
|
||||||
);
|
|
||||||
|
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case Ok<NotificationResponseModel>():
|
case Ok<NotificationResponseModel>():
|
||||||
if (isMobile && page > 1) {
|
final data = result.value;
|
||||||
final oldNotifications =
|
if (isPagination && allNotificationResponse != null) {
|
||||||
_notificationResponseModel?.data?.notifications ?? [];
|
final old = allNotificationResponse?.data?.notifications ?? [];
|
||||||
|
final fresh = data.data?.notifications ?? [];
|
||||||
final newNotifications = result.value.data?.notifications ?? [];
|
allNotificationResponse = data.copyWith(
|
||||||
|
data: data.data?.copyWith(
|
||||||
_notificationResponseModel = result.value.copyWith(
|
notifications: [...old, ...fresh],
|
||||||
data: result.value.data?.copyWith(
|
|
||||||
notifications: [...oldNotifications, ...newNotifications],
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
_notificationResponseModel = result.value;
|
allNotificationResponse = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentPage = page;
|
_updateImportantFromAll();
|
||||||
_hasMoreData =
|
|
||||||
(result.value.data?.notifications?.length ?? 0) >= _pageSize;
|
currentPageAll = page;
|
||||||
_totalPages = result.value.data?.meta?.pages ?? 1;
|
totalPagesAll = data.data?.meta?.pages ?? 1;
|
||||||
|
hasMoreAll = (data.data?.notifications?.length ?? 0) >= _pageSize;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Error<NotificationResponseModel>():
|
case Error<NotificationResponseModel>():
|
||||||
@@ -86,22 +86,136 @@ class NotificationViewModel with ChangeNotifier {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
_isLoading = false;
|
isLoadingAll = false;
|
||||||
_isMoreLoading = false;
|
isMoreLoadingAll = false;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Jump to page
|
Future<void> getUnreadNotifications({int page = 1, bool isPagination = false}) async {
|
||||||
Future<void> jumpToPage(int page) async {
|
if (isPagination) {
|
||||||
if (_totalPages == 0 || page < 1 || page > _totalPages) {
|
isMoreLoadingUnread = true;
|
||||||
|
} else {
|
||||||
|
isLoadingUnread = true;
|
||||||
|
}
|
||||||
|
notifyListeners();
|
||||||
|
|
||||||
|
final int offset = (page - 1) * _pageSize;
|
||||||
|
final result = await _repository.getUnreadNotifications(limit: _pageSize, offset: offset);
|
||||||
|
|
||||||
|
switch (result) {
|
||||||
|
case Ok<NotificationResponseModel>():
|
||||||
|
final data = result.value;
|
||||||
|
if (isPagination && unreadNotificationResponse != null) {
|
||||||
|
final old = unreadNotificationResponse?.data?.notifications ?? [];
|
||||||
|
final fresh = data.data?.notifications ?? [];
|
||||||
|
unreadNotificationResponse = data.copyWith(
|
||||||
|
data: data.data?.copyWith(
|
||||||
|
notifications: [...old, ...fresh],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
unreadNotificationResponse = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentPageUnread = page;
|
||||||
|
totalPagesUnread = data.data?.meta?.pages ?? 1;
|
||||||
|
hasMoreUnread = (data.data?.notifications?.length ?? 0) >= _pageSize;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Error<NotificationResponseModel>():
|
||||||
|
_errorMessage = result.error.toString();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoadingUnread = false;
|
||||||
|
isMoreLoadingUnread = false;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> getImportantNotifications({int page = 1, bool isPagination = false}) async {
|
||||||
|
if (isPagination) {
|
||||||
|
isMoreLoadingImportant = true;
|
||||||
|
} else {
|
||||||
|
isLoadingImportant = true;
|
||||||
|
}
|
||||||
|
notifyListeners();
|
||||||
|
|
||||||
|
final int offset = (page - 1) * _pageSize;
|
||||||
|
|
||||||
|
final result = await _repository.getUnreadNotifications(limit: _pageSize, offset: offset);
|
||||||
|
|
||||||
|
switch (result) {
|
||||||
|
case Ok<NotificationResponseModel>():
|
||||||
|
final data = result.value;
|
||||||
|
if (isPagination && importantNotificationResponse != null) {
|
||||||
|
final old = importantNotificationResponse?.data?.notifications ?? [];
|
||||||
|
final fresh = data.data?.notifications ?? [];
|
||||||
|
importantNotificationResponse = data.copyWith(
|
||||||
|
data: data.data?.copyWith(
|
||||||
|
notifications: [...old, ...fresh],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
importantNotificationResponse = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentPageImportant = page;
|
||||||
|
totalPagesImportant = data.data?.meta?.pages ?? 1;
|
||||||
|
hasMoreImportant = (data.data?.notifications?.length ?? 0) >= _pageSize;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Error<NotificationResponseModel>():
|
||||||
|
|
||||||
|
_errorMessage = result.error.toString();
|
||||||
|
|
||||||
|
_updateImportantFromAll();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoadingImportant = false;
|
||||||
|
isMoreLoadingImportant = false;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _updateImportantFromAll() {
|
||||||
|
final all = allNotificationResponse?.data?.notifications ?? [];
|
||||||
|
final filtered = (all.where((n) {
|
||||||
|
final p = n.priority ?? '';
|
||||||
|
return p.toLowerCase() == 'important';
|
||||||
|
}).toList());
|
||||||
|
|
||||||
|
if (allNotificationResponse != null) {
|
||||||
|
importantNotificationResponse = allNotificationResponse!.copyWith(
|
||||||
|
data: allNotificationResponse!.data!.copyWith(notifications: filtered),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
importantNotificationResponse = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> jumpToPageAll(int page) async {
|
||||||
|
if (totalPagesAll == 0 || page < 1 || page > totalPagesAll) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await getNotifications(page: page);
|
await getNotifications(page: page);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark all as read
|
Future<void> jumpToPageUnread(int page) async {
|
||||||
|
if (totalPagesUnread == 0 || page < 1 || page > totalPagesUnread) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await getUnreadNotifications(page: page);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> jumpToPageImportant(int page) async {
|
||||||
|
if (totalPagesImportant == 0 || page < 1 || page > totalPagesImportant) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await getImportantNotifications(page: page);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> markAllAsRead(BuildContext context) async {
|
Future<void> markAllAsRead(BuildContext context) async {
|
||||||
_isLoading = true;
|
isLoadingAll = true;
|
||||||
_errorMessage = null;
|
_errorMessage = null;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
|
||||||
@@ -115,35 +229,39 @@ class NotificationViewModel with ChangeNotifier {
|
|||||||
case Ok<Map<String, dynamic>>():
|
case Ok<Map<String, dynamic>>():
|
||||||
final response = result.value;
|
final response = result.value;
|
||||||
final success = response['success'] as bool? ?? false;
|
final success = response['success'] as bool? ?? false;
|
||||||
final message =
|
final message = response['message'] as String? ?? NotificationStrings.unknownResponse;
|
||||||
response['message'] as String? ??
|
|
||||||
NotificationStrings.unknownResponse;
|
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
if (_notificationResponseModel?.data?.notifications != null) {
|
if (allNotificationResponse?.data?.notifications != null) {
|
||||||
final updatedNotifications =
|
final updatedNotifications = allNotificationResponse!.data!.notifications!
|
||||||
_notificationResponseModel!.data!.notifications!
|
.map((n) => n.copyWith(seen: true))
|
||||||
.map((n) => n.copyWith(seen: true))
|
.toList();
|
||||||
.toList();
|
|
||||||
|
|
||||||
_notificationResponseModel = _notificationResponseModel!.copyWith(
|
allNotificationResponse = allNotificationResponse!.copyWith(
|
||||||
data: _notificationResponseModel!.data!.copyWith(
|
data: allNotificationResponse!.data!.copyWith(
|
||||||
notifications: updatedNotifications,
|
notifications: updatedNotifications,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
_unreadNotificationResponse = _unreadNotificationResponse?.copyWith(
|
unreadNotificationResponse = unreadNotificationResponse?.copyWith(
|
||||||
data: _unreadNotificationResponse?.data?.copyWith(
|
data: unreadNotificationResponse?.data?.copyWith(notifications: []),
|
||||||
notifications: [],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (importantNotificationResponse?.data?.notifications != null) {
|
||||||
|
final updatedImportant = importantNotificationResponse!.data!.notifications!
|
||||||
|
.map((n) => n.copyWith(seen: true))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
importantNotificationResponse = importantNotificationResponse!.copyWith(
|
||||||
|
data: importantNotificationResponse!.data!.copyWith(
|
||||||
|
notifications: updatedImportant,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
AppToast.success(
|
AppToast.success(context, NotificationStrings.allNotificationMarkedAsRead);
|
||||||
context,
|
|
||||||
NotificationStrings.allNotificationMarkedAsRead,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_errorMessage = message;
|
_errorMessage = message;
|
||||||
@@ -161,7 +279,7 @@ class NotificationViewModel with ChangeNotifier {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
_isLoading = false;
|
isLoadingAll = false;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,28 +300,4 @@ class NotificationViewModel with ChangeNotifier {
|
|||||||
return '${difference.inDays} ${NotificationStrings.daysAgo}';
|
return '${difference.inDays} ${NotificationStrings.daysAgo}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> getUnreadNotifications({int page = 1}) async {
|
|
||||||
_isUnreadLoading = true;
|
|
||||||
notifyListeners();
|
|
||||||
|
|
||||||
final int offset = _pageSize * page;
|
|
||||||
|
|
||||||
final result = await _repository.getUnreadNotifications(
|
|
||||||
limit: _pageSize,
|
|
||||||
offset: offset,
|
|
||||||
);
|
|
||||||
|
|
||||||
switch (result) {
|
|
||||||
case Ok<NotificationResponseModel>():
|
|
||||||
_unreadNotificationResponse = result.value;
|
|
||||||
break;
|
|
||||||
case Error<NotificationResponseModel>():
|
|
||||||
_errorMessage = result.error.toString();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
_isUnreadLoading = false;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,26 @@ class _DesktopNotificationPageState extends State<DesktopNotificationPage>
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final viewModel = context.watch<NotificationViewModel>();
|
final viewModel = context.watch<NotificationViewModel>();
|
||||||
|
|
||||||
|
final activeIndex = controller.index;
|
||||||
|
|
||||||
|
int currentPage = 1;
|
||||||
|
int totalPages = 1;
|
||||||
|
bool isLoading = false;
|
||||||
|
|
||||||
|
if (activeIndex == 0) {
|
||||||
|
currentPage = viewModel.currentPageAll;
|
||||||
|
totalPages = viewModel.totalPagesAll;
|
||||||
|
isLoading = viewModel.isLoadingAll;
|
||||||
|
} else if (activeIndex == 1) {
|
||||||
|
currentPage = viewModel.currentPageUnread;
|
||||||
|
totalPages = viewModel.totalPagesUnread;
|
||||||
|
isLoading = viewModel.isLoadingUnread;
|
||||||
|
} else {
|
||||||
|
currentPage = viewModel.currentPageImportant;
|
||||||
|
totalPages = viewModel.totalPagesImportant;
|
||||||
|
isLoading = viewModel.isLoadingImportant;
|
||||||
|
}
|
||||||
|
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
backgroundColor: AppColors.backgroundColor,
|
backgroundColor: AppColors.backgroundColor,
|
||||||
@@ -60,6 +80,7 @@ class _DesktopNotificationPageState extends State<DesktopNotificationPage>
|
|||||||
NotificationStrings.unread,
|
NotificationStrings.unread,
|
||||||
NotificationStrings.important,
|
NotificationStrings.important,
|
||||||
],
|
],
|
||||||
|
onTap: (_) => setState(() {}),
|
||||||
),
|
),
|
||||||
SizedBox(height: 12.0.h()),
|
SizedBox(height: 12.0.h()),
|
||||||
Padding(
|
Padding(
|
||||||
@@ -93,27 +114,25 @@ class _DesktopNotificationPageState extends State<DesktopNotificationPage>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(height: 12.0.h()),
|
SizedBox(height: 12.0.h()),
|
||||||
viewModel.isLoading
|
if (isLoading)
|
||||||
? Container()
|
const Expanded(
|
||||||
: Expanded(
|
child: Center(
|
||||||
child: TabBarView(
|
child: CircularProgressIndicator(
|
||||||
controller: controller,
|
color: AppColors.secondary50,
|
||||||
children: const [
|
|
||||||
NotificationAllTab(),
|
|
||||||
NotificationUnreadTab(),
|
|
||||||
NotificationImportantTab(),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (viewModel.isLoading) ...[
|
)
|
||||||
const Spacer(),
|
else
|
||||||
const Center(
|
Expanded(
|
||||||
child: CircularProgressIndicator(
|
child: TabBarView(
|
||||||
color: AppColors.secondary50,
|
controller: controller,
|
||||||
|
children: const [
|
||||||
|
NotificationAllTab(),
|
||||||
|
NotificationUnreadTab(),
|
||||||
|
NotificationImportantTab(),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
|
||||||
],
|
|
||||||
SizedBox(height: 10.0.h()),
|
SizedBox(height: 10.0.h()),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
@@ -131,14 +150,19 @@ class _DesktopNotificationPageState extends State<DesktopNotificationPage>
|
|||||||
onTap: () async {
|
onTap: () async {
|
||||||
final selectedPage = await showDialog<int>(
|
final selectedPage = await showDialog<int>(
|
||||||
context: context,
|
context: context,
|
||||||
builder:
|
builder: (context) => PageSelectionDialog(
|
||||||
(context) => PageSelectionDialog(
|
totalPages: totalPages,
|
||||||
totalPages: viewModel.totalPages,
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (selectedPage != null) {
|
if (selectedPage != null) {
|
||||||
viewModel.jumpToPage(selectedPage);
|
if (activeIndex == 0) {
|
||||||
|
viewModel.jumpToPageAll(selectedPage);
|
||||||
|
} else if (activeIndex == 1) {
|
||||||
|
viewModel.jumpToPageUnread(selectedPage);
|
||||||
|
} else {
|
||||||
|
viewModel.jumpToPageImportant(selectedPage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
@@ -153,7 +177,7 @@ class _DesktopNotificationPageState extends State<DesktopNotificationPage>
|
|||||||
children: [
|
children: [
|
||||||
SizedBox(width: 5.0.w()),
|
SizedBox(width: 5.0.w()),
|
||||||
Text(
|
Text(
|
||||||
viewModel.currentPage.toString(),
|
currentPage.toString(),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14.0.sp(),
|
fontSize: 14.0.sp(),
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
@@ -181,7 +205,7 @@ class _DesktopNotificationPageState extends State<DesktopNotificationPage>
|
|||||||
),
|
),
|
||||||
SizedBox(width: 5.0.w()),
|
SizedBox(width: 5.0.w()),
|
||||||
Text(
|
Text(
|
||||||
viewModel.totalPages.toString(),
|
totalPages.toString(),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13.0.sp(),
|
fontSize: 13.0.sp(),
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
|
|||||||
@@ -21,22 +21,37 @@ class MobileNotificationPage extends StatefulWidget {
|
|||||||
class _MobileNotificationPageState extends State<MobileNotificationPage>
|
class _MobileNotificationPageState extends State<MobileNotificationPage>
|
||||||
with SingleTickerProviderStateMixin {
|
with SingleTickerProviderStateMixin {
|
||||||
late TabController controller;
|
late TabController controller;
|
||||||
final ScrollController _scrollController = ScrollController();
|
|
||||||
|
final ScrollController _scrollAll = ScrollController();
|
||||||
|
final ScrollController _scrollUnread = ScrollController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
controller = TabController(length: 3, vsync: this);
|
controller = TabController(length: 3, vsync: this);
|
||||||
|
|
||||||
_scrollController.addListener(() {
|
final viewModel = context.read<NotificationViewModel>();
|
||||||
final viewModel = context.read<NotificationViewModel>();
|
|
||||||
if (_scrollController.position.pixels >=
|
_scrollAll.addListener(() {
|
||||||
_scrollController.position.maxScrollExtent - 100 &&
|
if (_scrollAll.position.pixels >=
|
||||||
!viewModel.isLoading &&
|
_scrollAll.position.maxScrollExtent - 100 &&
|
||||||
viewModel.hasMoreData) {
|
!viewModel.isLoadingAll &&
|
||||||
|
viewModel.hasMoreAll) {
|
||||||
viewModel.getNotifications(
|
viewModel.getNotifications(
|
||||||
page: viewModel.currentPage + 1,
|
page: viewModel.currentPageAll + 1,
|
||||||
isMobile: true,
|
isPagination: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_scrollUnread.addListener(() {
|
||||||
|
if (_scrollUnread.position.pixels >=
|
||||||
|
_scrollUnread.position.maxScrollExtent - 100 &&
|
||||||
|
!viewModel.isLoadingUnread &&
|
||||||
|
viewModel.hasMoreUnread) {
|
||||||
|
viewModel.getUnreadNotifications(
|
||||||
|
page: viewModel.currentPageUnread + 1,
|
||||||
|
isPagination: true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -45,7 +60,8 @@ class _MobileNotificationPageState extends State<MobileNotificationPage>
|
|||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
controller.dispose();
|
controller.dispose();
|
||||||
_scrollController.dispose();
|
_scrollAll.dispose();
|
||||||
|
_scrollUnread.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,37 +114,43 @@ class _MobileNotificationPageState extends State<MobileNotificationPage>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(height: 12.0.h()),
|
SizedBox(height: 12.0.h()),
|
||||||
viewModel.isLoading
|
|
||||||
? Container()
|
Expanded(
|
||||||
: Expanded(
|
child: TabBarView(
|
||||||
child: TabBarView(
|
controller: controller,
|
||||||
controller: controller,
|
children: [
|
||||||
children: [
|
NotificationAllTab(scrollController: _scrollAll),
|
||||||
NotificationAllTab(scrollController: _scrollController),
|
NotificationUnreadTab(scrollController: _scrollUnread),
|
||||||
NotificationUnreadTab(scrollController: _scrollController),
|
const NotificationImportantTab(),
|
||||||
NotificationImportantTab(
|
],
|
||||||
scrollController: _scrollController,
|
),
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
if (viewModel.isMoreLoadingAll)
|
||||||
),
|
|
||||||
if (viewModel.isMoreLoading) ...[
|
|
||||||
const Padding(
|
const Padding(
|
||||||
padding: EdgeInsets.symmetric(vertical: 16),
|
padding: EdgeInsets.symmetric(vertical: 16),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: CircularProgressIndicator(color: AppColors.secondary50),
|
child: CircularProgressIndicator(color: AppColors.secondary50),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
|
||||||
if (viewModel.isLoading &&
|
if (viewModel.isMoreLoadingUnread)
|
||||||
(viewModel.notificationResponse?.data?.notifications?.isEmpty ??
|
const Padding(
|
||||||
true)) ...[
|
padding: EdgeInsets.symmetric(vertical: 16),
|
||||||
const Spacer(),
|
child: Center(
|
||||||
const Center(
|
child: CircularProgressIndicator(color: AppColors.secondary50),
|
||||||
child: CircularProgressIndicator(color: AppColors.secondary50),
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
if ((viewModel.isLoadingAll &&
|
||||||
|
(viewModel.allNotificationResponse?.data?.notifications?.isEmpty ?? true)) ||
|
||||||
|
(viewModel.isLoadingUnread &&
|
||||||
|
(viewModel.unreadNotificationResponse?.data?.notifications?.isEmpty ?? true)))
|
||||||
|
const Expanded(
|
||||||
|
child: Center(
|
||||||
|
child: CircularProgressIndicator(color: AppColors.secondary50),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ class TabletNotificationPage extends StatefulWidget {
|
|||||||
class _TabletNotificationPageState extends State<TabletNotificationPage>
|
class _TabletNotificationPageState extends State<TabletNotificationPage>
|
||||||
with SingleTickerProviderStateMixin {
|
with SingleTickerProviderStateMixin {
|
||||||
late TabController controller;
|
late TabController controller;
|
||||||
|
final GlobalKey<ScaffoldState> key = GlobalKey();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -41,7 +42,25 @@ class _TabletNotificationPageState extends State<TabletNotificationPage>
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final viewModel = context.watch<NotificationViewModel>();
|
final viewModel = context.watch<NotificationViewModel>();
|
||||||
final GlobalKey<ScaffoldState> key = GlobalKey();
|
final activeIndex = controller.index;
|
||||||
|
|
||||||
|
int currentPage = 1;
|
||||||
|
int totalPages = 1;
|
||||||
|
bool isLoading = false;
|
||||||
|
|
||||||
|
if (activeIndex == 0) {
|
||||||
|
currentPage = viewModel.currentPageAll;
|
||||||
|
totalPages = viewModel.totalPagesAll;
|
||||||
|
isLoading = viewModel.isLoadingAll;
|
||||||
|
} else if (activeIndex == 1) {
|
||||||
|
currentPage = viewModel.currentPageUnread;
|
||||||
|
totalPages = viewModel.totalPagesUnread;
|
||||||
|
isLoading = viewModel.isLoadingUnread;
|
||||||
|
} else {
|
||||||
|
currentPage = viewModel.currentPageImportant;
|
||||||
|
totalPages = viewModel.totalPagesImportant;
|
||||||
|
isLoading = viewModel.isLoadingImportant;
|
||||||
|
}
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
key: key,
|
key: key,
|
||||||
@@ -63,6 +82,7 @@ class _TabletNotificationPageState extends State<TabletNotificationPage>
|
|||||||
NotificationStrings.unread,
|
NotificationStrings.unread,
|
||||||
NotificationStrings.important,
|
NotificationStrings.important,
|
||||||
],
|
],
|
||||||
|
onTap: (_) => setState(() {}),
|
||||||
),
|
),
|
||||||
SizedBox(height: 12.0.h()),
|
SizedBox(height: 12.0.h()),
|
||||||
Padding(
|
Padding(
|
||||||
@@ -96,27 +116,25 @@ class _TabletNotificationPageState extends State<TabletNotificationPage>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(height: 12.0.h()),
|
SizedBox(height: 12.0.h()),
|
||||||
viewModel.isLoading
|
if (isLoading)
|
||||||
? Container()
|
const Expanded(
|
||||||
: Expanded(
|
child: Center(
|
||||||
child: TabBarView(
|
child: CircularProgressIndicator(
|
||||||
controller: controller,
|
color: AppColors.secondary50,
|
||||||
children: const [
|
|
||||||
NotificationAllTab(),
|
|
||||||
NotificationUnreadTab(),
|
|
||||||
NotificationImportantTab(),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (viewModel.isLoading) ...[
|
)
|
||||||
const Spacer(),
|
else
|
||||||
const Center(
|
Expanded(
|
||||||
child: CircularProgressIndicator(
|
child: TabBarView(
|
||||||
color: AppColors.secondary50,
|
controller: controller,
|
||||||
|
children: const [
|
||||||
|
NotificationAllTab(),
|
||||||
|
NotificationUnreadTab(),
|
||||||
|
NotificationImportantTab(),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
|
||||||
],
|
|
||||||
SizedBox(height: 10.0.h()),
|
SizedBox(height: 10.0.h()),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
@@ -134,14 +152,19 @@ class _TabletNotificationPageState extends State<TabletNotificationPage>
|
|||||||
onTap: () async {
|
onTap: () async {
|
||||||
final selectedPage = await showDialog<int>(
|
final selectedPage = await showDialog<int>(
|
||||||
context: context,
|
context: context,
|
||||||
builder:
|
builder: (context) => PageSelectionDialog(
|
||||||
(context) => PageSelectionDialog(
|
totalPages: totalPages,
|
||||||
totalPages: viewModel.totalPages,
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (selectedPage != null) {
|
if (selectedPage != null) {
|
||||||
viewModel.jumpToPage(selectedPage);
|
if (activeIndex == 0) {
|
||||||
|
viewModel.jumpToPageAll(selectedPage);
|
||||||
|
} else if (activeIndex == 1) {
|
||||||
|
viewModel.jumpToPageUnread(selectedPage);
|
||||||
|
} else {
|
||||||
|
viewModel.jumpToPageImportant(selectedPage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
@@ -153,7 +176,7 @@ class _TabletNotificationPageState extends State<TabletNotificationPage>
|
|||||||
children: [
|
children: [
|
||||||
SizedBox(width: 5.0.w()),
|
SizedBox(width: 5.0.w()),
|
||||||
Text(
|
Text(
|
||||||
viewModel.currentPage.toString(),
|
currentPage.toString(),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14.0.sp(),
|
fontSize: 14.0.sp(),
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
@@ -184,7 +207,7 @@ class _TabletNotificationPageState extends State<TabletNotificationPage>
|
|||||||
),
|
),
|
||||||
SizedBox(width: 5.0.w()),
|
SizedBox(width: 5.0.w()),
|
||||||
Text(
|
Text(
|
||||||
viewModel.totalPages.toString(),
|
totalPages.toString(),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13.0.sp(),
|
fontSize: 13.0.sp(),
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
|
|||||||
@@ -22,4 +22,5 @@ class NotificationStrings {
|
|||||||
static const String reject = 'reject';
|
static const String reject = 'reject';
|
||||||
static const String info = 'info';
|
static const String info = 'info';
|
||||||
static const String noUnreadNotifications = 'No unread notifications';
|
static const String noUnreadNotifications = 'No unread notifications';
|
||||||
|
static const String noImportantNotifications = 'No important notifications';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class NotificationAllTab extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final viewModel = context.watch<NotificationViewModel>();
|
final viewModel = context.watch<NotificationViewModel>();
|
||||||
final notifications =
|
final notifications =
|
||||||
viewModel.notificationResponse?.data?.notifications ?? [];
|
viewModel.allNotificationResponse?.data?.notifications ?? [];
|
||||||
|
|
||||||
return ListView.builder(
|
return ListView.builder(
|
||||||
controller: scrollController,
|
controller: scrollController,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tm_app/view_models/notification_view_model.dart';
|
import 'package:tm_app/view_models/notification_view_model.dart';
|
||||||
|
import 'package:tm_app/views/notification/strings/notification_strings.dart';
|
||||||
import 'notification_card.dart';
|
import 'notification_card.dart';
|
||||||
|
|
||||||
class NotificationImportantTab extends StatelessWidget {
|
class NotificationImportantTab extends StatelessWidget {
|
||||||
@@ -12,13 +12,25 @@ class NotificationImportantTab extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final viewModel = context.watch<NotificationViewModel>();
|
final viewModel = context.watch<NotificationViewModel>();
|
||||||
final allNotifications =
|
|
||||||
viewModel.notificationResponse?.data?.notifications ?? [];
|
|
||||||
|
|
||||||
final importantNotifications = allNotifications
|
if (viewModel.isLoadingImportant) {
|
||||||
.where((n) => n.priority?.toLowerCase() == 'important')
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
|
||||||
|
final importantFromApi = viewModel.importantNotificationResponse?.data?.notifications ?? [];
|
||||||
|
|
||||||
|
final allNotifications = viewModel.allNotificationResponse?.data?.notifications ?? [];
|
||||||
|
|
||||||
|
final source = importantFromApi.isNotEmpty ? importantFromApi : allNotifications;
|
||||||
|
|
||||||
|
final importantNotifications = source
|
||||||
|
.where((n) => (n.priority ?? '').toLowerCase() == 'important')
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
|
if (importantNotifications.isEmpty) {
|
||||||
|
return const Center(child: Text(NotificationStrings.noImportantNotifications));
|
||||||
|
}
|
||||||
|
|
||||||
return ListView.builder(
|
return ListView.builder(
|
||||||
controller: scrollController,
|
controller: scrollController,
|
||||||
itemCount: importantNotifications.length,
|
itemCount: importantNotifications.length,
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class NotificationUnreadTab extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final viewModel = context.watch<NotificationViewModel>();
|
final viewModel = context.watch<NotificationViewModel>();
|
||||||
|
|
||||||
if (viewModel.isUnreadLoading) {
|
if (viewModel.isLoadingUnread){
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,18 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import '../../core/theme/colors.dart';
|
import '../../core/theme/colors.dart';
|
||||||
import '../../core/utils/size_config.dart';
|
import '../../core/utils/size_config.dart';
|
||||||
|
|
||||||
class MainTabBar extends StatefulWidget {
|
class MainTabBar extends StatefulWidget {
|
||||||
const MainTabBar({required this.controller, required this.titles, super.key});
|
const MainTabBar({
|
||||||
|
required this.controller,
|
||||||
|
required this.titles,
|
||||||
|
this.onTap,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
final TabController controller;
|
final TabController controller;
|
||||||
final List<String> titles;
|
final List<String> titles;
|
||||||
|
final ValueChanged<int>? onTap;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<MainTabBar> createState() => _MainTabBarState();
|
State<MainTabBar> createState() => _MainTabBarState();
|
||||||
@@ -27,6 +32,8 @@ class _MainTabBarState extends State<MainTabBar> {
|
|||||||
),
|
),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: TabBar(
|
child: TabBar(
|
||||||
|
controller: widget.controller,
|
||||||
|
onTap: widget.onTap,
|
||||||
indicatorSize: TabBarIndicatorSize.tab,
|
indicatorSize: TabBarIndicatorSize.tab,
|
||||||
dividerColor: Colors.transparent,
|
dividerColor: Colors.transparent,
|
||||||
indicator: BoxDecoration(
|
indicator: BoxDecoration(
|
||||||
@@ -40,7 +47,6 @@ class _MainTabBarState extends State<MainTabBar> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
controller: widget.controller,
|
|
||||||
labelColor: AppColors.mainBlue,
|
labelColor: AppColors.mainBlue,
|
||||||
unselectedLabelColor: AppColors.grey60,
|
unselectedLabelColor: AppColors.grey60,
|
||||||
labelStyle: TextStyle(fontSize: 16.0.sp(), fontWeight: FontWeight.w600),
|
labelStyle: TextStyle(fontSize: 16.0.sp(), fontWeight: FontWeight.w600),
|
||||||
|
|||||||
Reference in New Issue
Block a user