Files
mobile-app/lib/features/profile/data/profile_repository.dart

330 lines
11 KiB
Dart
Raw Normal View History

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';
class ProfileRepository {
final Dio _dio = ApiClient.instance.dio;
/// Fetch profile based on role.
/// Agent: GET /agents/profile/me
/// User: GET /users/profile/me
Future<Map<String, dynamic>> getMyProfile(String role) async {
try {
final endpoint = role == 'AGENT'
? '/agents/profile/me'
: '/users/profile/me';
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 profile',
statusCode: e.response?.statusCode,
);
}
}
/// Update profile based on role.
/// Agent: PUT /agents/profile
/// User: PATCH /users/profile/me
Future<Map<String, dynamic>> updateProfile(
String role,
Map<String, dynamic> data,
) async {
try {
final Response response;
if (role == 'AGENT') {
response = await _dio.put('/agents/profile', data: data);
} else {
response = await _dio.patch('/users/profile/me', 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 profile',
statusCode: e.response?.statusCode,
);
}
}
/// 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,
'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(
message: e.message ?? 'Failed to get upload URL',
statusCode: e.response?.statusCode,
);
}
}
/// 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,
);
}
}
}