Skip to content

Commit d15c420

Browse files
authored
feat(merge_requests): display private pending review notes (#605)
* feat(merge_requests): display private pending review notes Expose saved private notes with explicit paging and localized read-only recovery. Await fresh MR detail before draft dispatch on account changes, and preserve literal bodies and original nullable metadata. Signed-off-by: sjungwon03 <sjungwon03@gmail.com> * docs(parity): link pending review panel PR Signed-off-by: sjungwon03 <sjungwon03@gmail.com> --------- Signed-off-by: sjungwon03 <sjungwon03@gmail.com>
1 parent 56bdb82 commit d15c420

25 files changed

Lines changed: 2015 additions & 11 deletions

‎apps/labfox/lib/features/merge_requests/presentation/controllers/mr_pending_review_controller.dart‎

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,27 @@ class MrPendingReviewQuery {
1313
required this.mergeRequest,
1414
required this.mergeRequestId,
1515
this.perPage = 20,
16+
this.requireCurrentDetail = false,
1617
});
1718

1819
final MergeRequestRef mergeRequest;
1920
final int mergeRequestId;
2021
final int perPage;
2122

23+
/// UI consumers recheck authoritative detail before every draft read.
24+
final bool requireCurrentDetail;
25+
2226
@override
2327
bool operator ==(Object other) =>
2428
other is MrPendingReviewQuery &&
2529
other.mergeRequest == mergeRequest &&
2630
other.mergeRequestId == mergeRequestId &&
27-
other.perPage == perPage;
31+
other.perPage == perPage &&
32+
other.requireCurrentDetail == requireCurrentDetail;
2833

2934
@override
30-
int get hashCode => Object.hash(mergeRequest, mergeRequestId, perPage);
35+
int get hashCode =>
36+
Object.hash(mergeRequest, mergeRequestId, perPage, requireCurrentDetail);
3137
}
3238

3339
/// Loaded private rows are a partial list until the server cursor ends.
@@ -83,12 +89,34 @@ class MrPendingReviewController
8389
) {
8490
if (generation != _generation) return false;
8591
if (ref.read(currentAccountProvider) != account) return false;
92+
if (arg.requireCurrentDetail) {
93+
final detail = ref
94+
.read(mergeRequestControllerProvider(arg.mergeRequest))
95+
.unwrapPrevious();
96+
final source = ref.read(mergeRequestsRepositoryProvider);
97+
if (detail.isLoading ||
98+
detail.hasError ||
99+
!detail.hasValue ||
100+
source.isLoading ||
101+
source.hasError ||
102+
source.valueOrNull == null ||
103+
!_matchesDetail(detail.value!)) {
104+
return false;
105+
}
106+
}
86107
final session = ref.read(mrDraftNotesRepositoryProvider);
87108
return !session.isLoading &&
88109
!session.hasError &&
89110
identical(session.valueOrNull, repository);
90111
}
91112

113+
bool _matchesDetail(MergeRequest detail) =>
114+
detail.id == arg.mergeRequestId &&
115+
detail.id > 0 &&
116+
detail.iid == arg.mergeRequest.iid &&
117+
(detail.projectId == null ||
118+
detail.projectId == arg.mergeRequest.projectId);
119+
92120
@override
93121
Future<MrPendingReviewDrafts> build(MrPendingReviewQuery arg) async {
94122
_end();
@@ -105,6 +133,25 @@ class MrPendingReviewController
105133
if (account == null) {
106134
throw const GitLabAuthException('No authenticated draft note session.');
107135
}
136+
if (arg.requireCurrentDetail) {
137+
final source = ref.watch(mergeRequestsRepositoryProvider.future);
138+
final fresh = await ref.watch(
139+
mergeRequestControllerProvider(arg.mergeRequest).future,
140+
);
141+
final detailRepository = await source;
142+
if (generation != _generation) {
143+
return MrPendingReviewDrafts._(
144+
items: [],
145+
account: null,
146+
repository: null,
147+
);
148+
}
149+
if (detailRepository == null || !_matchesDetail(fresh)) {
150+
throw const GitLabServerException(
151+
'Invalid pending review detail identity.',
152+
);
153+
}
154+
}
108155
final repositorySession = ref.watch(mrDraftNotesRepositoryProvider.future);
109156
final repository = await repositorySession;
110157
if (!_current(generation, account, repository)) {

‎apps/labfox/lib/features/merge_requests/presentation/merge_request_detail_screen.dart‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import 'controllers/merge_requests_controllers.dart';
1313
import 'controllers/mr_actions_controller.dart';
1414
import 'widgets/mr_actions.dart';
1515
import 'widgets/mr_discussion_thread.dart';
16+
import 'widgets/mr_pending_review_panel.dart';
1617

1718
/// One merge request: an identity header, the description and discussion, with
1819
/// the approve / merge actions pinned to a sticky bar so they never scroll away.
@@ -96,6 +97,8 @@ class MergeRequestDetailScreen extends ConsumerWidget {
9697
style: Theme.of(context).textTheme.bodySmall,
9798
),
9899
const SizedBox(height: LabFoxSpacing.xl),
100+
MrPendingReviewPanel(mergeRequest: mrRef),
101+
const SizedBox(height: LabFoxSpacing.xl),
99102
MrDiscussionThread(projectId: projectId, iid: iid),
100103
],
101104
),

0 commit comments

Comments
 (0)