Open requested pull reviews from mobile Search #792
|
|
@ -4364,9 +4364,9 @@
|
|||
claimButton.hidden = !(detail.claimable || detail.assigned_to_me);
|
||||
claimButton.textContent = detail.assigned_to_me ? 'Open in My Work' : 'Assign to me';
|
||||
claimButton.disabled = state.status === 'claiming';
|
||||
startButton.hidden = !(detail.kind === 'issue' && (detail.reopenable ||
|
||||
(detail.state === 'open' && (detail.claimable || detail.assigned_to_me))));
|
||||
startButton.textContent = detail.reopenable ? 'Reopen & resume' :
|
||||
startButton.hidden = !(detail.reviewable || (detail.kind === 'issue' &&
|
||||
(detail.reopenable || (detail.state === 'open' && (detail.claimable || detail.assigned_to_me)))));
|
||||
startButton.textContent = detail.reviewable ? 'Review now' : detail.reopenable ? 'Reopen & resume' :
|
||||
(detail.assigned_to_me ? 'Start in Today' : 'Assign & start');
|
||||
startButton.disabled = state.status === 'claiming' || state.status === 'reopening';
|
||||
const shareStatus = {
|
||||
|
|
@ -4380,7 +4380,7 @@
|
|||
else if (detail.claimable) status.textContent = 'This issue is open and unassigned.';
|
||||
else if (detail.assigned_to_me) status.textContent = 'This item is already in My Work.';
|
||||
else if (detail.reopenable) status.textContent = 'Closed—reopen to resume.';
|
||||
else status.textContent = 'Read-only preview.';
|
||||
else status.textContent = 'Ready.';
|
||||
}
|
||||
const searchPreview = createSearchPreview({
|
||||
fetchJson: item => fetchReviewJson(searchPreviewPath(item), { headers:{ Accept:'application/json' } }),
|
||||
|
|
@ -4657,6 +4657,11 @@
|
|||
});
|
||||
qs('#start-search-result').addEventListener('click', async () => {
|
||||
const detail = searchPreviewDetail;
|
||||
if (detail?.reviewable) {
|
||||
taskOverlayHistory.leave();
|
||||
detail.kind = 'review';
|
||||
return openRoutedWork(detail, null, { replace:true });
|
||||
}
|
||||
if (!detail || detail.kind !== 'issue') return;
|
||||
try {
|
||||
if (detail.reopenable) await searchReopenAndStart.run(detail);
|
||||
|
|
|
|||
|
|
@ -539,6 +539,19 @@ async def work_preview(repository: str, kind: str, number: int) -> dict:
|
|||
login = user.get("login") if isinstance(user, dict) else ""
|
||||
state = issue.get("state") if isinstance(issue.get("state"), str) else ""
|
||||
actual_kind = "pull" if isinstance(issue.get("pull_request"), dict) else "issue"
|
||||
reviewable = False
|
||||
if actual_kind == "pull" and state == "open" and login:
|
||||
try:
|
||||
pull = await fetch(f"repos/{repository}/pulls/{number}")
|
||||
except Exception:
|
||||
pull = None
|
||||
requested_reviewers = pull.get("requested_reviewers") if isinstance(pull, dict) else []
|
||||
if not isinstance(requested_reviewers, list):
|
||||
requested_reviewers = []
|
||||
reviewable = any(
|
||||
isinstance(reviewer, dict) and reviewer.get("login") == login
|
||||
for reviewer in requested_reviewers
|
||||
)
|
||||
return {
|
||||
"kind": actual_kind,
|
||||
"repository": repository,
|
||||
|
|
@ -556,6 +569,7 @@ async def work_preview(repository: str, kind: str, number: int) -> dict:
|
|||
"claimable": actual_kind == "issue" and state == "open" and not assignee_names,
|
||||
"reopenable": actual_kind == "issue" and state == "closed",
|
||||
"assigned_to_me": bool(login and login in assignee_names),
|
||||
"reviewable": reviewable,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -398,6 +398,23 @@ def test_assigned_pull_preview_hands_off_to_existing_my_work_sheet_without_claim
|
|||
assert "await openPreviewWorkInMyWork(claimed)" in click_handler
|
||||
|
||||
|
||||
def test_requested_pull_preview_opens_native_review_without_assignment_mutation():
|
||||
html = dashboard_bundle_text()
|
||||
css = (FRONTEND / "dashboard.css").read_text()
|
||||
|
||||
assert "startButton.hidden = !(detail.reviewable" in html
|
||||
assert "detail.reviewable ? 'Review now'" in html
|
||||
handler = html.split(
|
||||
"qs('#start-search-result').addEventListener('click'", 1
|
||||
)[1].split("qs('#close-whiteboard')", 1)[0]
|
||||
assert "detail.kind = 'review'" in handler
|
||||
assert "openRoutedWork" in handler
|
||||
assert "{ replace:true }" in handler
|
||||
assert "searchPreview.claim" not in handler
|
||||
assert "fetchReviewJson" not in handler
|
||||
assert ".search-preview-actions button, .search-preview-actions a { min-height:44px;" in css
|
||||
|
||||
|
||||
def test_search_preview_offers_assign_and_start_for_eligible_issues():
|
||||
html = dashboard_bundle_text()
|
||||
css = (FRONTEND / "dashboard.css").read_text()
|
||||
|
|
@ -413,7 +430,7 @@ def test_search_preview_offers_assign_and_start_for_eligible_issues():
|
|||
def test_closed_issue_preview_reopens_then_resumes_through_capacity_guard():
|
||||
html = dashboard_bundle_text()
|
||||
|
||||
assert "startButton.textContent = detail.reopenable ? 'Reopen & resume'" in html
|
||||
assert "detail.reopenable ? 'Reopen & resume'" in html
|
||||
assert "mutate: (detail, action) => fetchReviewJson(" in html
|
||||
handler = html.split("qs('#start-search-result').addEventListener('click'", 1)[1].split(
|
||||
"qs('#close-whiteboard')", 1
|
||||
|
|
|
|||
|
|
@ -267,6 +267,7 @@ async def test_work_preview_normalizes_details_and_only_allows_unassigned_open_i
|
|||
"claimable": True,
|
||||
"reopenable": False,
|
||||
"assigned_to_me": False,
|
||||
"reviewable": False,
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -294,6 +295,38 @@ async def test_work_preview_derives_pull_kind_and_never_offers_issue_claim():
|
|||
assert preview["claimable"] is False
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_work_preview_marks_authoritative_requested_pull_review_actionable():
|
||||
requested_paths = []
|
||||
|
||||
async def handler(request):
|
||||
requested_paths.append(request.url.path)
|
||||
if request.url.path.endswith("/user"):
|
||||
return httpx.Response(200, json={"login": "timmy"})
|
||||
if request.url.path.endswith("/pulls/9"):
|
||||
return httpx.Response(200, json={
|
||||
"state": "open",
|
||||
"requested_reviewers": [{"login": "timmy"}, None],
|
||||
})
|
||||
return httpx.Response(200, json={
|
||||
"number": 9,
|
||||
"title": "Improve search",
|
||||
"state": "open",
|
||||
"html_url": "https://forge.example/stackchain/web/pulls/9",
|
||||
"pull_request": {"merged": False},
|
||||
"assignees": [],
|
||||
})
|
||||
|
||||
gitea_proxy.start_client(transport=httpx.MockTransport(handler))
|
||||
try:
|
||||
preview = await gitea_proxy.work_preview("stackchain/web", "issue", 9)
|
||||
finally:
|
||||
await gitea_proxy.stop_client()
|
||||
|
||||
assert preview["reviewable"] is True
|
||||
assert "/api/v1/repos/stackchain/web/pulls/9" in requested_paths
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_work_preview_offers_reopen_only_for_closed_issues():
|
||||
async def handler(request):
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user