feat: review pull requests from update triage (Closes #775)
This commit is contained in:
1 parent
584752ed24
commit
378291d3c8
8 files changed
+223
-3
No files matched your search
@@ -408,7 +408,7 @@ textarea { resize: vertical; min-height: 120px; }
|
||||
.update-sheet-actions a { border:1px solid #60a5fa; border-radius:10px; font-weight:700; }
|
||||
.update-more-actions { margin-top:14px; }
|
||||
.update-more-actions > summary { min-height:44px; display:flex; align-items:center; justify-content:center; cursor:pointer; border:1px solid #60a5fa; border-radius:10px; font-weight:700; }
|
||||
.update-decision-bar { display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:6px; margin-top:14px; padding:8px; background:rgba(11,21,38,.98); border-top:1px solid #2a496e; }
|
||||
.update-decision-bar { display:grid; grid-template-columns:repeat(auto-fit,minmax(76px,1fr)); gap:6px; margin-top:14px; padding:8px; background:rgba(11,21,38,.98); border-top:1px solid #2a496e; }
|
||||
.update-decision-bar button, .update-decision-bar summary { min-height:44px; min-width:0; padding-inline:6px; }
|
||||
.update-retry { min-height:44px; width:100%; margin-top:10px; }
|
||||
.issue-sheet { position:fixed; inset:0; z-index:56; display:none; justify-content:flex-end; background:rgba(5,12,21,.72); backdrop-filter:blur(4px); }
|
||||
|
||||
+59
-1
@@ -193,6 +193,7 @@
|
||||
let draft = null;
|
||||
let reviewFiles = [];
|
||||
let selectedReviewHead = '';
|
||||
let reviewHandoffSubmitted = false;
|
||||
let activeInlineTarget = null;
|
||||
let bulkConfirmationPending = false;
|
||||
let bulkMarkPending = false;
|
||||
@@ -943,7 +944,7 @@
|
||||
const updateDecision = createUpdateDecisionTransaction({
|
||||
controls: [
|
||||
qs('#keep-update-unread'), qs('#mark-update-read-next'),
|
||||
qs('#focus-update-reply'), qs('#toggle-update-more'),
|
||||
qs('#review-update-now'), qs('#focus-update-reply'), qs('#toggle-update-more'),
|
||||
],
|
||||
status: qs('#update-gesture-status'),
|
||||
});
|
||||
@@ -985,6 +986,7 @@
|
||||
qs('#update-ownership-action').hidden = true;
|
||||
qs('#update-ownership-start').hidden = true;
|
||||
qs('#create-update-follow-up').hidden = true;
|
||||
qs('#review-update-now').hidden = true;
|
||||
qs('#retry-update-load').hidden = true;
|
||||
setOfflineUpdateControls(false);
|
||||
qs('#keep-update-unread').focus();
|
||||
@@ -999,6 +1001,7 @@
|
||||
qs('#acknowledge-update-next').hidden = !detail.acknowledge_supported;
|
||||
qs('#mute-update-next').hidden = !detail.mute_supported;
|
||||
qs('#create-update-follow-up').hidden = !['Issue', 'Pull'].includes(detail.subject_type);
|
||||
qs('#review-update-now').hidden = !updateReviewHandoff.eligible(detail, selectedUpdate);
|
||||
if (offlineWorkMode) {
|
||||
setOfflineUpdateControls(true);
|
||||
} else {
|
||||
@@ -1056,6 +1059,36 @@
|
||||
showUpdateTriageOutcome(outcome);
|
||||
},
|
||||
});
|
||||
const updateReviewHandoff = createUpdateReviewHandoff({
|
||||
openReview: item => {
|
||||
qs('#update-sheet').classList.remove('open');
|
||||
reviewHandoffSubmitted = false;
|
||||
openReviewSheet(item, qs('#review-update-now'),
|
||||
offlineWorkMode ? offlineWorkStore.loadDetail(confirmedOwnerLogin, item) : null);
|
||||
},
|
||||
restoreUpdate: () => {
|
||||
qs('#review-sheet').classList.remove('open');
|
||||
selectedReview = null;
|
||||
qs('#update-sheet').classList.add('open');
|
||||
qs('#review-update-now').focus();
|
||||
},
|
||||
admitRead: notificationId => offlineWorkMode ?
|
||||
notificationReadOutbox.enqueueDurably(notificationId) : markNotificationRead(notificationId),
|
||||
advance: source => {
|
||||
mobileComposerViewport.close(qs('#review-sheet .review-sheet-panel'));
|
||||
qs('#review-sheet').classList.remove('open');
|
||||
selectedReview = null;
|
||||
offlineReview = false;
|
||||
progress = null;
|
||||
draft = null;
|
||||
reviewFiles = [];
|
||||
selectedReviewHead = '';
|
||||
notificationReader.acceptReadAndNext(lastMyWork, source);
|
||||
if (updateTriage.active()) updateTriage.acceptCompleted();
|
||||
reviewHandoffSubmitted = false;
|
||||
},
|
||||
});
|
||||
qs('#review-update-now').addEventListener('click', () => updateReviewHandoff.begin(selectedUpdate));
|
||||
qs('#review-kept-updates').addEventListener('click', () => {
|
||||
qs('#mobile-queue-sheet').close();
|
||||
updateTriage.reviewKept(keptUpdateIdentities);
|
||||
@@ -4034,6 +4067,10 @@
|
||||
}
|
||||
|
||||
function closeReviewSheet(navigate = true) {
|
||||
if (updateReviewHandoff.active()) {
|
||||
updateReviewHandoff.cancel();
|
||||
return;
|
||||
}
|
||||
if (navigate && createWorkRoute.parse(window.location.hash)) {
|
||||
workRoute.close();
|
||||
return;
|
||||
@@ -5767,6 +5804,17 @@
|
||||
)) return;
|
||||
const button = qs('#submit-review');
|
||||
button.disabled = true;
|
||||
if (updateReviewHandoff.active() && reviewHandoffSubmitted) {
|
||||
try {
|
||||
await updateReviewHandoff.complete();
|
||||
} catch (error) {
|
||||
qs('#review-submit-status').textContent = 'Review submitted, but the update is still unread. ' +
|
||||
error.message + ' Retry to continue.';
|
||||
button.disabled = false;
|
||||
button.focus();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (offlineReview) {
|
||||
const queuedTodayReview = reviewingActiveTodayItem();
|
||||
qs('#review-submit-status').textContent = 'Queueing review safely…';
|
||||
@@ -5784,6 +5832,11 @@
|
||||
draftFingerprint: localStorage.getItem(draft.storageKey) || '',
|
||||
progressFingerprint: localStorage.getItem(progress?.storageKey) || '',
|
||||
});
|
||||
if (updateReviewHandoff.active()) {
|
||||
reviewHandoffSubmitted = true;
|
||||
await updateReviewHandoff.complete();
|
||||
return;
|
||||
}
|
||||
if (queuedTodayReview) {
|
||||
const advanced = completeTodayItem(selectedReview, {
|
||||
successMessage: 'Review queued. Next Today item opened.',
|
||||
@@ -5812,6 +5865,11 @@
|
||||
expected_head_sha: selectedReviewHead,
|
||||
comments: snapshot.comments,
|
||||
});
|
||||
if (updateReviewHandoff.active()) {
|
||||
reviewHandoffSubmitted = true;
|
||||
await updateReviewHandoff.complete();
|
||||
return;
|
||||
}
|
||||
const canContinueToMerge = createReviewController.prepareMergeContinuation({
|
||||
storage: localStorage,
|
||||
item,
|
||||
|
||||
@@ -814,6 +814,7 @@
|
||||
<div class="update-decision-bar" aria-label="Update decisions">
|
||||
<button id="keep-update-unread" type="button" aria-label="Keep unread and open next update">Keep unread</button>
|
||||
<button id="mark-update-read-next" type="button" aria-label="Mark read and open next update">Mark read</button>
|
||||
<button id="review-update-now" type="button" hidden>Review now</button>
|
||||
<button id="focus-update-reply" type="button">Reply</button>
|
||||
<button id="toggle-update-more" type="button" aria-expanded="false">More</button>
|
||||
</div>
|
||||
@@ -1088,6 +1089,7 @@
|
||||
<script src="static/mobile-work-entry.js"></script>
|
||||
<script src="static/mobile-queue-launcher.js"></script>
|
||||
<script src="static/update-triage-session.js"></script>
|
||||
<script src="static/update-review-handoff.js"></script>
|
||||
<script src="static/update-read-position.js"></script>
|
||||
<script src="static/update-triage-launcher.js"></script>
|
||||
<script src="static/update-triage-gesture.js"></script>
|
||||
|
||||
@@ -77,6 +77,7 @@ const SHELL = [
|
||||
BASE + 'static/mobile-work-entry.js',
|
||||
BASE + 'static/mobile-queue-launcher.js',
|
||||
BASE + 'static/update-triage-session.js',
|
||||
BASE + 'static/update-review-handoff.js',
|
||||
BASE + 'static/update-read-position.js',
|
||||
BASE + 'static/update-triage-launcher.js',
|
||||
BASE + 'static/update-triage-gesture.js',
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
(function (root, factory) {
|
||||
if (typeof module === 'object' && module.exports) module.exports = factory;
|
||||
else root.createUpdateReviewHandoff = factory;
|
||||
})(typeof self !== 'undefined' ? self : this, function createUpdateReviewHandoff(options) {
|
||||
let source = null;
|
||||
let completing = null;
|
||||
|
||||
function eligible(detail, item = detail) {
|
||||
const merged = { ...(item || {}), ...(detail || {}) };
|
||||
return Boolean(
|
||||
merged.is_review && merged.update_reason === 'Review requested' &&
|
||||
merged.subject_type === 'Pull' && /^[^/]+\/[^/]+$/.test(String(merged.repository || '')) &&
|
||||
Number.isInteger(Number(merged.number)) && Number(merged.number) > 0 &&
|
||||
Number.isInteger(Number(merged.notification_id))
|
||||
);
|
||||
}
|
||||
|
||||
function reviewItem(item) {
|
||||
return {
|
||||
...item,
|
||||
kind: 'review',
|
||||
is_review: true,
|
||||
key: item.key || item.repository + '#' + item.number,
|
||||
};
|
||||
}
|
||||
|
||||
function begin(item) {
|
||||
if (!eligible(item) || source) return false;
|
||||
source = item;
|
||||
options.openReview(reviewItem(item));
|
||||
return true;
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
if (!source || completing) return false;
|
||||
const item = source;
|
||||
source = null;
|
||||
options.restoreUpdate?.(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
function complete() {
|
||||
if (!source) return Promise.resolve(false);
|
||||
if (completing) return completing;
|
||||
const item = source;
|
||||
completing = Promise.resolve(options.admitRead(item.notification_id)).then(() => {
|
||||
options.advance(item);
|
||||
source = null;
|
||||
return true;
|
||||
}).finally(() => { completing = null; });
|
||||
return completing;
|
||||
}
|
||||
|
||||
return {
|
||||
eligible,
|
||||
begin,
|
||||
cancel,
|
||||
complete,
|
||||
active: () => Boolean(source),
|
||||
source: () => source,
|
||||
};
|
||||
});
|
||||
Reference in new issue
Block a user