feat(flutter): user-facing requests view + cancel (#356)
Closes the cleanest remaining #356 gap — Discover submits Lidarr requests but had no surface for users to view or cancel their own. Admin had it; user didn't. - RequestsApi at lib/api/endpoints/requests.dart — listMine() + cancel(id). Same /api/requests endpoint the web /requests page uses; identical AdminRequest wire shape so the existing model is reused (just augmented with matched_*_id fields for the "Listen" CTA on completed rows). - MyRequestsController mirrors the web's auto-poll (#369 piece already shipped server-side / web-side): 12s refresh while any row is mid-ingest (status='approved'), stops on settle. Riverpod Timer in build() that's cancelled in onDispose. - RequestsScreen with kind/status pills, ingest progress copy, Cancel (with confirm dialog) and Listen CTAs per row state. - Route /requests under the shell. Entry point in settings between Profile and Appearance — "My requests". - Widget tests cover empty / pending / completed / rejected states + the Cancel-confirm dialog. Out of scope this slice: Discover-screen "View your requests" CTA after submitting a new request. Reasonable follow-up but doesn't block the management loop. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
import '../../models/admin_request.dart';
|
||||
|
||||
/// User-side requests API — `/api/requests`. Server scopes results to
|
||||
/// the caller; admins see only their own here, not all users'. The
|
||||
/// admin-cross-user view lives in `/api/admin/requests` (AdminRequestsApi).
|
||||
///
|
||||
/// Wire shape is identical to the admin endpoint, so the same
|
||||
/// AdminRequest model is reused.
|
||||
class RequestsApi {
|
||||
RequestsApi(this._dio);
|
||||
final Dio _dio;
|
||||
|
||||
/// GET /api/requests — caller's own requests, all statuses.
|
||||
Future<List<AdminRequest>> listMine() async {
|
||||
final r = await _dio.get<List<dynamic>>('/api/requests');
|
||||
final raw = r.data ?? const [];
|
||||
return raw
|
||||
.map((e) => AdminRequest.fromJson((e as Map).cast<String, dynamic>()))
|
||||
.toList(growable: false);
|
||||
}
|
||||
|
||||
/// DELETE /api/requests/{id} — cancel a pending request. Server
|
||||
/// returns the cancelled row body (not 204) so the caller can patch
|
||||
/// local state without a refetch.
|
||||
Future<AdminRequest> cancel(String id) async {
|
||||
final r = await _dio.delete<Map<String, dynamic>>('/api/requests/$id');
|
||||
return AdminRequest.fromJson(r.data ?? const {});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user