Files
Eventify-frontend/lib/features/reviews/services/review_service.dart
Sicherhaven bc12fe70aa security: sanitize all error messages shown to users
Created centralized userFriendlyError() utility that converts raw
exceptions into clean, user-friendly messages. Strips hostnames,
ports, OS error codes, HTTP status codes, stack traces, and Django
field names. Maps network/timeout/auth/server errors to plain
English messages.

Fixed 16 locations across 10 files:
- home_screen, calendar_screen, learn_more_screen (SnackBar/Text)
- login_screen, desktop_login_screen (SnackBar)
- profile_screen, contribute_screen, search_screen (SnackBar)
- review_form, review_section (inline error text)
- gamification_provider (error field)

Also removed double-wrapped exceptions in ReviewService (rethrow
instead of throw Exception('Failed to...: $e')).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 07:15:02 +05:30

105 lines
3.1 KiB
Dart

// lib/features/reviews/services/review_service.dart
import '../../../core/api/api_client.dart';
import '../../../core/api/api_endpoints.dart';
import '../models/review_models.dart';
class ReviewService {
final ApiClient _api = ApiClient();
/// Fetch paginated reviews + stats for an event.
Future<ReviewListResponse> getReviews(int eventId, {int page = 1, int pageSize = 10}) async {
try {
final res = await _api.post(
ApiEndpoints.reviewList,
body: {'event_id': eventId, 'page': page, 'page_size': pageSize},
requiresAuth: true,
);
// Parse interactions map: { "review_id": { "helpful": bool, "flag": bool } }
final rawInteractions = res['interactions'] as Map<String, dynamic>? ?? {};
final interactionsMap = <int, Map<String, bool>>{};
rawInteractions.forEach((key, value) {
final id = int.tryParse(key);
if (id != null && value is Map) {
interactionsMap[id] = {
'helpful': value['helpful'] == true,
'flag': value['flag'] == true,
};
}
});
// Parse reviews
final rawReviews = res['reviews'] as List? ?? [];
final reviews = rawReviews.map((r) {
final review = Map<String, dynamic>.from(r as Map);
return ReviewModel.fromJson(review, interactions: interactionsMap[review['id']]);
}).toList();
// Parse stats
final stats = ReviewStatsModel.fromJson(res);
// Parse user's own review
ReviewModel? userReview;
if (res['user_review'] != null && res['user_review'] is Map) {
final ur = Map<String, dynamic>.from(res['user_review'] as Map);
userReview = ReviewModel.fromJson(ur, interactions: interactionsMap[ur['id']]);
}
return ReviewListResponse(
reviews: reviews,
stats: stats,
userReview: userReview,
total: (res['total'] as num?)?.toInt() ?? reviews.length,
page: (res['page'] as num?)?.toInt() ?? page,
pageSize: (res['page_size'] as num?)?.toInt() ?? pageSize,
);
} catch (_) {
rethrow;
}
}
/// Submit or update a review.
Future<void> submitReview(int eventId, int rating, String? comment) async {
try {
await _api.post(
ApiEndpoints.reviewSubmit,
body: {
'event_id': eventId,
'rating': rating,
if (comment != null && comment.trim().isNotEmpty) 'comment': comment.trim(),
},
requiresAuth: true,
);
} catch (_) {
rethrow;
}
}
/// Toggle helpful vote on a review. Returns new helpful count.
Future<int> markHelpful(int reviewId) async {
try {
final res = await _api.post(
ApiEndpoints.reviewHelpful,
body: {'review_id': reviewId},
requiresAuth: true,
);
return (res['helpful_count'] as num?)?.toInt() ?? 0;
} catch (_) {
rethrow;
}
}
/// Flag a review for moderation.
Future<void> flagReview(int reviewId) async {
try {
await _api.post(
ApiEndpoints.reviewFlag,
body: {'review_id': reviewId},
requiresAuth: true,
);
} catch (_) {
rethrow;
}
}
}