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,53 @@
|
||||
class AppNotification {
|
||||
final String id;
|
||||
final String userId;
|
||||
final String type; // 'connection' | 'message' | 'system' | 'update' | 'request'
|
||||
final String title;
|
||||
final String description;
|
||||
final bool read;
|
||||
final String? actionUrl;
|
||||
final Map<String, String>? data;
|
||||
final DateTime createdAt;
|
||||
|
||||
const AppNotification({
|
||||
required this.id,
|
||||
required this.userId,
|
||||
required this.type,
|
||||
required this.title,
|
||||
required this.description,
|
||||
required this.read,
|
||||
this.actionUrl,
|
||||
this.data,
|
||||
required this.createdAt,
|
||||
});
|
||||
|
||||
factory AppNotification.fromJson(Map<String, dynamic> json) {
|
||||
return AppNotification(
|
||||
id: json['id'] as String,
|
||||
userId: json['userId'] as String? ?? '',
|
||||
type: json['type'] as String? ?? 'system',
|
||||
title: json['title'] as String? ?? '',
|
||||
description: json['description'] as String? ?? '',
|
||||
read: json['read'] as bool? ?? false,
|
||||
actionUrl: json['actionUrl'] as String?,
|
||||
data: json['data'] != null
|
||||
? Map<String, String>.from(json['data'] as Map)
|
||||
: null,
|
||||
createdAt: DateTime.parse(json['createdAt'] as String),
|
||||
);
|
||||
}
|
||||
|
||||
AppNotification copyWith({bool? read}) {
|
||||
return AppNotification(
|
||||
id: id,
|
||||
userId: userId,
|
||||
type: type,
|
||||
title: title,
|
||||
description: description,
|
||||
read: read ?? this.read,
|
||||
actionUrl: actionUrl,
|
||||
data: data,
|
||||
createdAt: createdAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
103
lib/features/notifications/data/notification_repository.dart
Normal file
103
lib/features/notifications/data/notification_repository.dart
Normal file
@@ -0,0 +1,103 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:real_estate_mobile/core/network/api_client.dart';
|
||||
import 'package:real_estate_mobile/core/network/api_exceptions.dart';
|
||||
import 'package:real_estate_mobile/features/notifications/data/models/notification_model.dart';
|
||||
|
||||
class NotificationRepository {
|
||||
final Dio _dio = ApiClient.instance.dio;
|
||||
|
||||
/// Fetch notifications with optional filter and pagination.
|
||||
/// GET /notifications?filter=all|unread&page=1&limit=20
|
||||
Future<NotificationsResponse> getNotifications({
|
||||
String filter = 'all',
|
||||
int page = 1,
|
||||
int limit = 20,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _dio.get('/notifications', queryParameters: {
|
||||
'filter': filter,
|
||||
'page': page,
|
||||
'limit': limit,
|
||||
});
|
||||
final data = response.data['data'] as Map<String, dynamic>;
|
||||
return NotificationsResponse.fromJson(data);
|
||||
} on DioException catch (e) {
|
||||
if (e.error is ApiException) throw e.error as ApiException;
|
||||
throw ApiException(
|
||||
message: e.message ?? 'Failed to fetch notifications',
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get unread notification count.
|
||||
/// GET /notifications/unread-count
|
||||
Future<int> getUnreadCount() async {
|
||||
try {
|
||||
final response = await _dio.get('/notifications/unread-count');
|
||||
final data = response.data['data'] as Map<String, dynamic>;
|
||||
return data['unreadCount'] as int? ?? 0;
|
||||
} on DioException catch (e) {
|
||||
if (e.error is ApiException) throw e.error as ApiException;
|
||||
throw ApiException(
|
||||
message: e.message ?? 'Failed to fetch unread count',
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Mark a single notification as read.
|
||||
/// PATCH /notifications/:id/read
|
||||
Future<void> markAsRead(String notificationId) async {
|
||||
try {
|
||||
await _dio.patch('/notifications/$notificationId/read');
|
||||
} on DioException catch (e) {
|
||||
if (e.error is ApiException) throw e.error as ApiException;
|
||||
throw ApiException(
|
||||
message: e.message ?? 'Failed to mark notification as read',
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Mark all notifications as read.
|
||||
/// PATCH /notifications/read-all
|
||||
Future<void> markAllAsRead() async {
|
||||
try {
|
||||
await _dio.patch('/notifications/read-all');
|
||||
} on DioException catch (e) {
|
||||
if (e.error is ApiException) throw e.error as ApiException;
|
||||
throw ApiException(
|
||||
message: e.message ?? 'Failed to mark all as read',
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NotificationsResponse {
|
||||
final List<AppNotification> notifications;
|
||||
final int unreadCount;
|
||||
final int totalPages;
|
||||
final int currentPage;
|
||||
|
||||
const NotificationsResponse({
|
||||
required this.notifications,
|
||||
required this.unreadCount,
|
||||
required this.totalPages,
|
||||
required this.currentPage,
|
||||
});
|
||||
|
||||
factory NotificationsResponse.fromJson(Map<String, dynamic> json) {
|
||||
final list = (json['notifications'] as List<dynamic>?)
|
||||
?.map((e) => AppNotification.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
[];
|
||||
return NotificationsResponse(
|
||||
notifications: list,
|
||||
unreadCount: json['unreadCount'] as int? ?? 0,
|
||||
totalPages: json['totalPages'] as int? ?? 1,
|
||||
currentPage: json['currentPage'] as int? ?? 1,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user