feat: Implement notifications feature, enhance profile settings with new sections, and integrate Firebase configurations for multiple environments.
This commit is contained in:
@@ -48,13 +48,23 @@ class ProfileRepository {
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> getAvatarUploadUrl(String role, String fileName) async {
|
||||
/// Get presigned upload URL for avatar.
|
||||
/// Returns { uploadUrl: String, key: String }
|
||||
Future<Map<String, String>> getAvatarUploadUrl(
|
||||
String role, String fileName, String contentType) async {
|
||||
try {
|
||||
final endpoint = role == 'AGENT'
|
||||
? '/upload/avatar-presigned-url'
|
||||
: '/upload/user-avatar-presigned-url';
|
||||
final response = await _dio.post(endpoint, data: {'fileName': fileName});
|
||||
return response.data['data']['url'] as String;
|
||||
final response = await _dio.post(endpoint, data: {
|
||||
'filename': fileName,
|
||||
'contentType': contentType,
|
||||
});
|
||||
final data = response.data['data'] as Map<String, dynamic>;
|
||||
return {
|
||||
'uploadUrl': data['uploadUrl'] as String,
|
||||
'key': data['key'] as String,
|
||||
};
|
||||
} on DioException catch (e) {
|
||||
if (e.error is ApiException) throw e.error as ApiException;
|
||||
throw ApiException(
|
||||
@@ -63,4 +73,257 @@ class ProfileRepository {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Upload file bytes directly to S3 presigned URL.
|
||||
Future<void> uploadToS3(
|
||||
String uploadUrl, List<int> fileBytes, String contentType) async {
|
||||
try {
|
||||
await Dio().put(
|
||||
uploadUrl,
|
||||
data: Stream.fromIterable(fileBytes.map((e) => [e])),
|
||||
options: Options(
|
||||
headers: {
|
||||
'Content-Type': contentType,
|
||||
'Content-Length': fileBytes.length,
|
||||
},
|
||||
),
|
||||
);
|
||||
} on DioException catch (e) {
|
||||
throw ApiException(
|
||||
message: e.message ?? 'Failed to upload file',
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete avatar from S3: DELETE /upload/{encodedKey}
|
||||
Future<void> deleteAvatar(String key) async {
|
||||
try {
|
||||
final encodedKey = Uri.encodeComponent(key);
|
||||
await _dio.delete('/upload/$encodedKey');
|
||||
} on DioException catch (_) {
|
||||
// S3 delete failure is non-critical
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetch privacy preferences.
|
||||
/// Agent: GET /agents/preferences/privacy
|
||||
/// User: GET /users/preferences/privacy
|
||||
Future<Map<String, dynamic>> getPrivacyPreferences(String role) async {
|
||||
try {
|
||||
final endpoint = role == 'AGENT'
|
||||
? '/agents/preferences/privacy'
|
||||
: '/users/preferences/privacy';
|
||||
final response = await _dio.get(endpoint);
|
||||
return response.data['data'] as Map<String, dynamic>;
|
||||
} on DioException catch (e) {
|
||||
if (e.error is ApiException) throw e.error as ApiException;
|
||||
throw ApiException(
|
||||
message: e.message ?? 'Failed to fetch privacy preferences',
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Update privacy preferences.
|
||||
/// Agent: PUT /agents/preferences/privacy
|
||||
/// User: PUT /users/preferences/privacy
|
||||
Future<Map<String, dynamic>> updatePrivacyPreferences(
|
||||
String role,
|
||||
Map<String, dynamic> data,
|
||||
) async {
|
||||
try {
|
||||
final endpoint = role == 'AGENT'
|
||||
? '/agents/preferences/privacy'
|
||||
: '/users/preferences/privacy';
|
||||
final response = await _dio.put(endpoint, data: data);
|
||||
return response.data['data'] as Map<String, dynamic>;
|
||||
} on DioException catch (e) {
|
||||
if (e.error is ApiException) throw e.error as ApiException;
|
||||
throw ApiException(
|
||||
message: e.message ?? 'Failed to update privacy preferences',
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete account.
|
||||
/// Agent: DELETE /agents/account
|
||||
/// User: DELETE /users/account
|
||||
Future<void> deleteAccount(String role) async {
|
||||
try {
|
||||
final endpoint =
|
||||
role == 'AGENT' ? '/agents/account' : '/users/account';
|
||||
await _dio.delete(endpoint);
|
||||
} on DioException catch (e) {
|
||||
if (e.error is ApiException) throw e.error as ApiException;
|
||||
throw ApiException(
|
||||
message: e.message ?? 'Failed to delete account',
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Change password: POST /auth/change-password
|
||||
Future<void> changePassword(
|
||||
String currentPassword, String newPassword) async {
|
||||
try {
|
||||
await _dio.post('/auth/change-password', data: {
|
||||
'currentPassword': currentPassword,
|
||||
'newPassword': newPassword,
|
||||
});
|
||||
} on DioException catch (e) {
|
||||
if (e.error is ApiException) throw e.error as ApiException;
|
||||
throw ApiException(
|
||||
message: e.response?.data?['message'] ??
|
||||
e.message ??
|
||||
'Failed to change password',
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetch notification preferences.
|
||||
/// Agent: GET /agents/preferences/notifications
|
||||
/// User: GET /users/preferences/notifications
|
||||
Future<Map<String, dynamic>> getNotificationPreferences(String role) async {
|
||||
try {
|
||||
final endpoint = role == 'AGENT'
|
||||
? '/agents/preferences/notifications'
|
||||
: '/users/preferences/notifications';
|
||||
final response = await _dio.get(endpoint);
|
||||
return response.data['data'] as Map<String, dynamic>;
|
||||
} on DioException catch (e) {
|
||||
if (e.error is ApiException) throw e.error as ApiException;
|
||||
throw ApiException(
|
||||
message: e.message ?? 'Failed to fetch notification preferences',
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Update notification preferences.
|
||||
/// Agent: PUT /agents/preferences/notifications
|
||||
/// User: PUT /users/preferences/notifications
|
||||
Future<Map<String, dynamic>> updateNotificationPreferences(
|
||||
String role,
|
||||
Map<String, dynamic> data,
|
||||
) async {
|
||||
try {
|
||||
final endpoint = role == 'AGENT'
|
||||
? '/agents/preferences/notifications'
|
||||
: '/users/preferences/notifications';
|
||||
final response = await _dio.put(endpoint, data: data);
|
||||
return response.data['data'] as Map<String, dynamic>;
|
||||
} on DioException catch (e) {
|
||||
if (e.error is ApiException) throw e.error as ApiException;
|
||||
throw ApiException(
|
||||
message: e.message ?? 'Failed to update notification preferences',
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate testimonial link: POST /testimonials/generate-link
|
||||
Future<String> generateTestimonialLink() async {
|
||||
try {
|
||||
final response = await _dio.post('/testimonials/generate-link');
|
||||
return response.data['data']['token'] as String;
|
||||
} on DioException catch (e) {
|
||||
if (e.error is ApiException) throw e.error as ApiException;
|
||||
throw ApiException(
|
||||
message: e.message ?? 'Failed to generate testimonial link',
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get agent's testimonials: GET /testimonials/my?sort=latest|oldest
|
||||
Future<List<Map<String, dynamic>>> getMyTestimonials(
|
||||
{String sort = 'latest'}) async {
|
||||
try {
|
||||
final response =
|
||||
await _dio.get('/testimonials/my', queryParameters: {'sort': sort});
|
||||
final list = response.data['data'] as List<dynamic>;
|
||||
return list.cast<Map<String, dynamic>>();
|
||||
} on DioException catch (e) {
|
||||
if (e.error is ApiException) throw e.error as ApiException;
|
||||
throw ApiException(
|
||||
message: e.message ?? 'Failed to fetch testimonials',
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get current subscription: GET /stripe/subscription
|
||||
Future<Map<String, dynamic>?> getSubscription() async {
|
||||
try {
|
||||
final response = await _dio.get('/stripe/subscription');
|
||||
return response.data['data'] as Map<String, dynamic>?;
|
||||
} on DioException catch (e) {
|
||||
if (e.response?.statusCode == 404) return null;
|
||||
if (e.error is ApiException) throw e.error as ApiException;
|
||||
throw ApiException(
|
||||
message: e.message ?? 'Failed to fetch subscription',
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Create Stripe checkout session: POST /stripe/create-checkout-session
|
||||
Future<String> createCheckoutSession(String planId) async {
|
||||
try {
|
||||
final response = await _dio.post('/stripe/create-checkout-session',
|
||||
data: {'planId': planId});
|
||||
return response.data['data']['url'] as String;
|
||||
} on DioException catch (e) {
|
||||
if (e.error is ApiException) throw e.error as ApiException;
|
||||
throw ApiException(
|
||||
message: e.message ?? 'Failed to create checkout session',
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Create Stripe billing portal: POST /stripe/create-portal-session
|
||||
Future<String> createPortalSession() async {
|
||||
try {
|
||||
final response = await _dio.post('/stripe/create-portal-session');
|
||||
return response.data['data']['url'] as String;
|
||||
} on DioException catch (e) {
|
||||
if (e.error is ApiException) throw e.error as ApiException;
|
||||
throw ApiException(
|
||||
message: e.message ?? 'Failed to create billing portal',
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Cancel subscription: POST /stripe/cancel-subscription
|
||||
Future<void> cancelSubscription() async {
|
||||
try {
|
||||
await _dio.post('/stripe/cancel-subscription');
|
||||
} on DioException catch (e) {
|
||||
if (e.error is ApiException) throw e.error as ApiException;
|
||||
throw ApiException(
|
||||
message: e.message ?? 'Failed to cancel subscription',
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get available plans: GET /stripe/plans
|
||||
Future<List<Map<String, dynamic>>> getStripePlans() async {
|
||||
try {
|
||||
final response = await _dio.get('/stripe/plans');
|
||||
final list = response.data['data'] as List<dynamic>;
|
||||
return list.cast<Map<String, dynamic>>();
|
||||
} on DioException catch (e) {
|
||||
if (e.error is ApiException) throw e.error as ApiException;
|
||||
throw ApiException(
|
||||
message: e.message ?? 'Failed to fetch plans',
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user