feat: commit review feedback fixes from mobile (Closes #1376)
CI / lint (pull_request) Successful in 3m3s
CI / build-release (pull_request) Successful in 6s
CI / browser-journey (pull_request) Successful in 5m18s
CI / release-candidate (pull_request) Has been skipped

This commit is contained in:
timmy committed 2026-08-25 03:08:53 +00:00
1 parent b541c707fc
commit 502fcb30f5
12 files changed
+521 -69

No files matched your search

+8
View File
@@ -1310,6 +1310,14 @@ textarea { resize: vertical; min-height: 120px; }
.pull-feedback-pass-heading h4 { margin:2px 0 8px; }
.pull-feedback-pass p { white-space:pre-wrap; }
.pull-feedback-pass textarea { width:100%; min-height:88px; box-sizing:border-box; }
#make-pull-feedback-fix { width:100%; margin-top:8px; border-color:#4ade80; }
.pull-feedback-file-editor { display:grid; min-width:0; gap:8px; margin-top:10px; padding:10px; border:1px solid #4ade80; border-radius:10px; overflow-x:hidden; }
.pull-feedback-file-editor[hidden] { display:none; }
.pull-feedback-file-editor h5 { margin:0; overflow-wrap:anywhere; }
#pull-feedback-file-content { min-height:36dvh; resize:vertical; font:13px/1.45 ui-monospace, SFMono-Regular, Consolas, monospace; tab-size:2; white-space:pre; overflow:auto; }
#pull-feedback-commit-message { box-sizing:border-box; width:100%; min-width:0; }
.pull-feedback-file-actions { display:grid; grid-template-columns:1fr 1fr; gap:8px; min-width:0; }
@media (max-width:359px) { .pull-feedback-file-actions { grid-template-columns:1fr; } }
.pull-feedback-dispositions { display:grid; grid-template-columns:1fr; gap:6px; margin:10px 0; padding:8px; }
.pull-feedback-dispositions button[aria-pressed="true"] { border-color:#60a5fa; background:#173b63; }
.pull-feedback-navigation { display:grid; grid-template-columns:1fr 1fr; gap:8px; margin-top:8px; }
+12
View File
@@ -1741,6 +1741,18 @@
<p class="small" id="pull-feedback-location"></p>
<p id="pull-feedback-body"></p>
<div class="small" id="pull-feedback-context" aria-live="polite"></div>
<button id="make-pull-feedback-fix" type="button" hidden>Make code change</button>
<section class="pull-feedback-file-editor" id="pull-feedback-file-editor" hidden>
<h5>Edit <span id="pull-feedback-file-path"></span></h5>
<label for="pull-feedback-file-content">File content</label>
<textarea id="pull-feedback-file-content" spellcheck="false" maxlength="131072"></textarea>
<label for="pull-feedback-commit-message">Commit message</label>
<input id="pull-feedback-commit-message" maxlength="120" placeholder="fix: address review feedback">
<div class="pull-feedback-file-actions">
<button id="cancel-pull-feedback-fix" type="button" hidden>Cancel change</button>
<button id="commit-pull-feedback-fix" type="button" hidden>Commit to pull branch</button>
</div>
</section>
<fieldset class="pull-feedback-dispositions">
<legend>Disposition</legend>
<button type="button" data-feedback-disposition="addressed">Addressed</button>
+94
View File
@@ -432,6 +432,8 @@ function bindFeedbackControls(doc, controller, getSelected, getDetail, getLogin)
qs('#finish-pull-feedback').disabled = active.state.posted === true ||
items.some(item => !active.state.responses[String(item.id)]?.disposition);
qs('#request-feedback-review').hidden = active.state.posted !== true;
qs('#make-pull-feedback-fix').hidden = active.detail?.capabilities?.authored !== true ||
active.detail?.state !== 'open' || active.detail?.merged === true || !comment.path;
const found = focusPullFile(doc, comment.path);
qs('#pull-feedback-context').textContent = found ?
'Matching change opened below.' : 'Matching change is unavailable in this preview.';
@@ -475,10 +477,77 @@ function bindFeedbackControls(doc, controller, getSelected, getDetail, getLogin)
active = { item, detail, reviewer, login,
state:controller.loadFeedbackPass(item, detail, reviewer, login) };
panel.hidden = false;
qs('#pull-feedback-file-editor').hidden = true;
qs('#cancel-pull-feedback-fix').hidden = true;
qs('#commit-pull-feedback-fix').hidden = true;
active.file = null;
qs('#pull-feedback-status').textContent = active.state.posted ?
'Response already posted. You can request an updated review.' : 'Progress saves on this device.';
render();
});
qs('#make-pull-feedback-fix').addEventListener('click', async () => {
const comment = current();
if (!active || !comment?.path || active.detail?.capabilities?.authored !== true) return;
const button = qs('#make-pull-feedback-fix');
button.disabled = true;
qs('#pull-feedback-status').textContent = 'Loading the current pull file…';
try {
const file = await controller.loadFeedbackFile(active.item, comment.path, active.detail.head_sha);
if (!active || file.head_sha !== active.detail.head_sha || file.path !== comment.path) return;
active.file = file;
qs('#pull-feedback-file-path').textContent = file.path;
qs('#pull-feedback-file-content').value = file.content;
qs('#pull-feedback-commit-message').value = 'fix: address review feedback';
qs('#pull-feedback-file-editor').hidden = false;
qs('#cancel-pull-feedback-fix').hidden = false;
qs('#commit-pull-feedback-fix').hidden = false;
qs('#pull-feedback-status').textContent = 'Edit the bounded file, then commit it to this pull branch.';
qs('#pull-feedback-file-content').focus();
} catch (error) {
qs('#pull-feedback-status').textContent = error.message + ' Retry without losing your feedback progress.';
} finally {
button.disabled = false;
}
});
qs('#cancel-pull-feedback-fix').addEventListener('click', () => {
qs('#pull-feedback-file-editor').hidden = true;
qs('#cancel-pull-feedback-fix').hidden = true;
qs('#commit-pull-feedback-fix').hidden = true;
active.file = null;
qs('#make-pull-feedback-fix').focus();
});
qs('#commit-pull-feedback-fix').addEventListener('click', async () => {
if (!active?.file) return;
const content = qs('#pull-feedback-file-content').value;
const message = qs('#pull-feedback-commit-message').value.trim();
if (!message || content === active.file.content) {
qs('#pull-feedback-status').textContent = 'Change the file and enter a commit message before committing.';
return;
}
if (!globalThis.confirm('Commit this change to ' + active.item.key + '?')) return;
const button = qs('#commit-pull-feedback-fix');
button.disabled = true;
qs('#pull-feedback-status').textContent = 'Committing and verifying the new pull head…';
try {
const result = await controller.commitFeedbackFix(active.item, {
path:active.file.path, content, message,
expectedHeadSha:active.file.head_sha, expectedBlobSha:active.file.blob_sha,
});
active.detail.head_sha = result.head_sha;
active.state.headSha = result.head_sha;
active.file = null;
save();
qs('#pull-feedback-file-editor').hidden = true;
qs('#cancel-pull-feedback-fix').hidden = true;
qs('#commit-pull-feedback-fix').hidden = true;
qs('#pull-feedback-status').textContent = 'Code change committed. Mark the comment Addressed when satisfied.';
render();
} catch (error) {
qs('#pull-feedback-status').textContent = error.message + ' Draft kept; reload if the branch changed, or retry.';
button.disabled = false;
button.focus();
}
});
panel.querySelector('.pull-feedback-dispositions').addEventListener('click', event => {
const disposition = event.target.dataset?.feedbackDisposition;
const comment = current();
@@ -701,6 +770,7 @@ function createPullSheet({ fetchJson, storage, onState, createConversationPager
let lifecycleMutation = null;
let editRequest = null;
let feedbackRequest = null;
let feedbackFixRequest = null;
const reviewRequests = new Map();
const reviewCache = new Map();
const feedbackRequests = new Map();
@@ -764,6 +834,30 @@ function createPullSheet({ fetchJson, storage, onState, createConversationPager
feedbackRequests.set(key, request);
return request;
},
loadFeedbackFile(item, path, expectedHeadSha) {
return fetchJson(pathFor(item) + '/feedback-file?path=' + encodeURIComponent(path) +
'&expected_head_sha=' + encodeURIComponent(expectedHeadSha), {
headers:{ Accept:'application/json' },
});
},
commitFeedbackFix(item, draft) {
if (feedbackFixRequest) return feedbackFixRequest;
feedbackFixRequest = fetchJson(pathFor(item) + '/feedback-file', {
method:'PATCH',
headers:{ Accept:'application/json', 'Content-Type':'application/json' },
body:JSON.stringify({
path:draft.path, content:draft.content, message:draft.message,
expected_head_sha:draft.expectedHeadSha,
expected_blob_sha:draft.expectedBlobSha,
}),
}).then(result => {
if (!result?.head_sha || result.head_sha === draft.expectedHeadSha || result.path !== draft.path) {
throw new Error('The feedback fix was not confirmed.');
}
return result;
}).finally(() => { feedbackFixRequest = null; });
return feedbackFixRequest;
},
loadChecks(item) {
if (checkRequest) return checkRequest;
checkRequest = fetchJson(pathFor(item) + '/checks', {