feat: autocomplete mobile teammate mentions (#447)
CI / lint (pull_request) Successful in 49s
CI / build-release (pull_request) Successful in 5s
CI / release-candidate (pull_request) Has been skipped

This commit is contained in:
timmy committed 2026-08-10 03:02:35 +00:00
1 parent 5a6ef62c4c
commit 0311f85d96
17 files changed
+418 -17

No files matched your search

+34
View File
@@ -1129,6 +1129,40 @@ async def issue_handoff_candidates(repository: str) -> list[dict]:
return candidates
async def mention_candidates(
repository: str, query: str, *, limit: int = 8
) -> list[dict]:
response = await _get_client().get(
f"/api/v1/repos/{repository}/assignees", headers=_auth()
)
response.raise_for_status()
payload = response.json()
if not isinstance(payload, list):
raise ValueError("Gitea assignees response was not a list")
needle = query.casefold()
matches = []
for item in payload:
if not isinstance(item, dict):
continue
login = item.get("login")
if (
not isinstance(login, str)
or not re.fullmatch(r"[A-Za-z0-9_.-]+", login)
):
continue
full_name = item.get("full_name")
name = full_name if isinstance(full_name, str) and full_name else login
login_match = needle in login.casefold()
name_match = needle in name.casefold()
if login_match or name_match:
matches.append((0 if login.casefold().startswith(needle) else 1, login.casefold(), {
"login": login,
"name": name,
}))
matches.sort(key=lambda item: (item[0], item[1]))
return [item[2] for item in matches[:limit]]
async def handoff_assigned_issue(
repository: str, number: int, recipient: str
) -> dict:
+22
View File
@@ -2232,6 +2232,28 @@ async def issue_handoff_candidates(
return JSONResponse(result)
@app.get("/api/v1/repos/{owner}/{repo}/mention-candidates")
async def repository_mention_candidates(
owner: str,
repo: str,
q: str = Query(min_length=2, max_length=39, pattern=r"^[A-Za-z0-9_.-]+$"),
) -> JSONResponse:
try:
result = await asyncio.wait_for(
gitea_proxy.mention_candidates(
f"{owner}/{repo}", q.casefold(), limit=8
),
timeout=ISSUE_ACTION_TIMEOUT_SECONDS,
)
except Exception:
return JSONResponse(
{"error": "Teammate suggestions are temporarily unavailable."},
status_code=503,
headers={"Retry-After": "1"},
)
return JSONResponse(result, headers={"Cache-Control": "no-store"})
@app.patch("/api/v1/repos/{owner}/{repo}/issues/{number}/handoff")
async def handoff_assigned_issue(
handoff: IssueHandoff,