feat: autocomplete mobile teammate mentions (#447)
This commit is contained in:
1 parent
5a6ef62c4c
commit
0311f85d96
17 files changed
+418
-17
No files matched your search
@@ -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
@@ -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,
|
||||
|
||||
Reference in new issue
Block a user