feat: Implement notifications feature, enhance profile settings with new sections, and integrate Firebase configurations for multiple environments.
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:real_estate_mobile/features/notifications/data/models/notification_model.dart';
|
||||
import 'package:real_estate_mobile/features/notifications/data/notification_repository.dart';
|
||||
|
||||
/// Unread notification count — polled every 30 seconds.
|
||||
final unreadCountProvider =
|
||||
StateNotifierProvider<UnreadCountNotifier, int>((ref) {
|
||||
return UnreadCountNotifier(NotificationRepository());
|
||||
});
|
||||
|
||||
class UnreadCountNotifier extends StateNotifier<int> {
|
||||
final NotificationRepository _repo;
|
||||
Timer? _timer;
|
||||
|
||||
UnreadCountNotifier(this._repo) : super(0) {
|
||||
refresh();
|
||||
_timer = Timer.periodic(const Duration(seconds: 30), (_) => refresh());
|
||||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
try {
|
||||
final count = await _repo.getUnreadCount();
|
||||
if (mounted) state = count;
|
||||
} catch (_) {
|
||||
// Silently fail — badge just won't update
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_timer?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// Notification list state.
|
||||
class NotificationListState {
|
||||
final List<AppNotification> notifications;
|
||||
final bool isLoading;
|
||||
final bool isLoadingMore;
|
||||
final String? error;
|
||||
final String filter; // 'all' | 'unread'
|
||||
final int currentPage;
|
||||
final int totalPages;
|
||||
|
||||
const NotificationListState({
|
||||
this.notifications = const [],
|
||||
this.isLoading = false,
|
||||
this.isLoadingMore = false,
|
||||
this.error,
|
||||
this.filter = 'all',
|
||||
this.currentPage = 1,
|
||||
this.totalPages = 1,
|
||||
});
|
||||
|
||||
NotificationListState copyWith({
|
||||
List<AppNotification>? notifications,
|
||||
bool? isLoading,
|
||||
bool? isLoadingMore,
|
||||
String? error,
|
||||
String? filter,
|
||||
int? currentPage,
|
||||
int? totalPages,
|
||||
}) {
|
||||
return NotificationListState(
|
||||
notifications: notifications ?? this.notifications,
|
||||
isLoading: isLoading ?? this.isLoading,
|
||||
isLoadingMore: isLoadingMore ?? this.isLoadingMore,
|
||||
error: error,
|
||||
filter: filter ?? this.filter,
|
||||
currentPage: currentPage ?? this.currentPage,
|
||||
totalPages: totalPages ?? this.totalPages,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final notificationListProvider =
|
||||
StateNotifierProvider<NotificationListNotifier, NotificationListState>(
|
||||
(ref) {
|
||||
return NotificationListNotifier(NotificationRepository(), ref);
|
||||
});
|
||||
|
||||
class NotificationListNotifier extends StateNotifier<NotificationListState> {
|
||||
final NotificationRepository _repo;
|
||||
final Ref _ref;
|
||||
|
||||
NotificationListNotifier(this._repo, this._ref)
|
||||
: super(const NotificationListState());
|
||||
|
||||
Future<void> loadNotifications({String filter = 'all'}) async {
|
||||
state = state.copyWith(isLoading: true, error: null, filter: filter);
|
||||
try {
|
||||
final response =
|
||||
await _repo.getNotifications(filter: filter, page: 1, limit: 20);
|
||||
state = state.copyWith(
|
||||
notifications: response.notifications,
|
||||
isLoading: false,
|
||||
currentPage: response.currentPage,
|
||||
totalPages: response.totalPages,
|
||||
);
|
||||
} catch (e) {
|
||||
state = state.copyWith(isLoading: false, error: e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> loadMore() async {
|
||||
if (state.isLoadingMore || state.currentPage >= state.totalPages) return;
|
||||
|
||||
state = state.copyWith(isLoadingMore: true);
|
||||
try {
|
||||
final nextPage = state.currentPage + 1;
|
||||
final response = await _repo.getNotifications(
|
||||
filter: state.filter, page: nextPage, limit: 20);
|
||||
state = state.copyWith(
|
||||
notifications: [...state.notifications, ...response.notifications],
|
||||
isLoadingMore: false,
|
||||
currentPage: response.currentPage,
|
||||
totalPages: response.totalPages,
|
||||
);
|
||||
} catch (_) {
|
||||
state = state.copyWith(isLoadingMore: false);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> markAsRead(String notificationId) async {
|
||||
try {
|
||||
await _repo.markAsRead(notificationId);
|
||||
state = state.copyWith(
|
||||
notifications: state.notifications.map((n) {
|
||||
if (n.id == notificationId) return n.copyWith(read: true);
|
||||
return n;
|
||||
}).toList(),
|
||||
);
|
||||
_ref.read(unreadCountProvider.notifier).refresh();
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
Future<void> markAllAsRead() async {
|
||||
try {
|
||||
await _repo.markAllAsRead();
|
||||
state = state.copyWith(
|
||||
notifications:
|
||||
state.notifications.map((n) => n.copyWith(read: true)).toList(),
|
||||
);
|
||||
_ref.read(unreadCountProvider.notifier).refresh();
|
||||
} catch (_) {}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user