Compare commits

..

4 Commits

Author SHA1 Message Date
agent-company dbcfbe9138 feat: add close/reopen action to PR detail view
Add POST /pulls/{owner}/{repo}/{index}/state handler that reuses the
existing SetIssueState Gitea API call (PRs share the issues state
endpoint). The PR detail template now shows a Close PR / Reopen PR
button with HTMX for seamless state toggling without full page reload.
Also fixes the state badge to use the correct CSS class when a PR is
closed.

Closes leeworks-agents/gitea-mobile#91

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 20:05:21 +00:00
AI-Manager 732cedda3d Merge pull request 'fix: remove go.sum from Dockerfile COPY (no external deps)' (#90) from fix/dockerfile-go-sum-89 into master
Build and Push / test (push) Has been cancelled
Build and Push / build (push) Has been cancelled
2026-03-27 18:04:01 +00:00
agent-company 937da1962b fix: remove go.sum from Dockerfile COPY since project has no external dependencies
The project uses only Go stdlib with zero external dependencies, so go.sum
does not exist. The Dockerfile COPY instruction fails when go.sum is missing.

Closes leeworks-agents/gitea-mobile#89

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 18:03:03 +00:00
AI-Manager 42a61b4428 Merge pull request 'feat: make repo selector searchable on create issue form' (#88) from feature/searchable-repo-selector-87 into master
Build and Push / test (push) Has been cancelled
Build and Push / build (push) Has been cancelled
2026-03-27 16:43:35 +00:00
3 changed files with 61 additions and 2 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
# Stage 1: Build # Stage 1: Build
FROM golang:1.22-alpine AS builder FROM golang:1.22-alpine AS builder
WORKDIR /app WORKDIR /app
COPY go.mod go.sum ./ COPY go.mod ./
RUN go mod download RUN go mod download
COPY . . COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /gitea-mobile ./cmd/server RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /gitea-mobile ./cmd/server
+48
View File
@@ -55,6 +55,7 @@ func (h *Handler) RegisterRoutes(mux *http.ServeMux) {
mux.HandleFunc("GET /pulls", h.ListPulls) mux.HandleFunc("GET /pulls", h.ListPulls)
mux.HandleFunc("GET /pulls/{owner}/{repo}/{index}", h.PullDetail) mux.HandleFunc("GET /pulls/{owner}/{repo}/{index}", h.PullDetail)
mux.HandleFunc("POST /pulls/{owner}/{repo}/{index}/review", h.SubmitReview) mux.HandleFunc("POST /pulls/{owner}/{repo}/{index}/review", h.SubmitReview)
mux.HandleFunc("POST /pulls/{owner}/{repo}/{index}/state", h.SetPullState)
// Settings (handled separately for auth bypass). // Settings (handled separately for auth bypass).
settingsHandler := &SettingsHandler{ settingsHandler := &SettingsHandler{
@@ -886,6 +887,53 @@ func (h *Handler) SetIssueState(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, fmt.Sprintf("/issues/%s/%s/%d", owner, repo, index), http.StatusSeeOther) http.Redirect(w, r, fmt.Sprintf("/issues/%s/%s/%d", owner, repo, index), http.StatusSeeOther)
} }
// SetPullState handles POST /pulls/{owner}/{repo}/{index}/state.
func (h *Handler) SetPullState(w http.ResponseWriter, r *http.Request) {
token := getToken(r)
owner := r.PathValue("owner")
repo := r.PathValue("repo")
indexStr := r.PathValue("index")
index, err := strconv.ParseInt(indexStr, 10, 64)
if err != nil {
http.Error(w, "invalid pull request index", http.StatusBadRequest)
return
}
if err := r.ParseForm(); err != nil {
http.Error(w, "bad request", http.StatusBadRequest)
return
}
state := r.FormValue("state")
if state != "open" && state != "closed" {
http.Error(w, "state must be 'open' or 'closed'", http.StatusBadRequest)
return
}
if err := h.Client.SetIssueState(r.Context(), token, owner, repo, index, state); err != nil {
slog.Error("failed to set pull request state", "error", err, "owner", owner, "repo", repo, "index", index, "state", state)
http.Error(w, "failed to update pull request state", http.StatusInternalServerError)
return
}
if isHTMX(r) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
if state == "closed" {
fmt.Fprintf(w, `<span class="state-closed" id="pull-state">closed</span>
<button class="btn btn-secondary" hx-post="/pulls/%s/%s/%d/state" hx-vals='{"state":"open"}' hx-target="#state-section" hx-swap="innerHTML">Reopen PR</button>`,
template.HTMLEscapeString(owner), template.HTMLEscapeString(repo), index)
} else {
fmt.Fprintf(w, `<span class="state-open" id="pull-state">open</span>
<button class="btn btn-danger" hx-post="/pulls/%s/%s/%d/state" hx-vals='{"state":"closed"}' hx-target="#state-section" hx-swap="innerHTML">Close PR</button>`,
template.HTMLEscapeString(owner), template.HTMLEscapeString(repo), index)
}
return
}
http.Redirect(w, r, fmt.Sprintf("/pulls/%s/%s/%d", owner, repo, index), http.StatusSeeOther)
}
// AddComment handles POST /issues/{owner}/{repo}/{index}/comment. // AddComment handles POST /issues/{owner}/{repo}/{index}/comment.
func (h *Handler) AddComment(w http.ResponseWriter, r *http.Request) { func (h *Handler) AddComment(w http.ResponseWriter, r *http.Request) {
token := getToken(r) token := getToken(r)
+12 -1
View File
@@ -4,7 +4,7 @@
<div class="card"> <div class="card">
<div class="card-meta"> <div class="card-meta">
<span class="type-badge type-pull">PR</span> <span class="type-badge type-pull">PR</span>
<span class="state-open">{{.Pull.State}}</span> {{if eq .Pull.State "closed"}}<span class="state-closed">{{.Pull.State}}</span>{{else}}<span class="state-open">{{.Pull.State}}</span>{{end}}
<span>{{.Pull.RepoOwner}}/{{.Pull.RepoName}} #{{.Pull.Number}}</span> <span>{{.Pull.RepoOwner}}/{{.Pull.RepoName}} #{{.Pull.Number}}</span>
{{range .Pull.Labels}} {{range .Pull.Labels}}
<span class="label" style="color:#{{.Color}};border:1px solid #{{.Color}}">{{.Name}}</span> <span class="label" style="color:#{{.Color}};border:1px solid #{{.Color}}">{{.Name}}</span>
@@ -15,6 +15,17 @@
<span class="diff-del">-{{.Pull.Deletions}}</span> <span class="diff-del">-{{.Pull.Deletions}}</span>
{{if .Pull.Mergeable}}<span style="color:var(--accent-green);">Mergeable</span>{{end}} {{if .Pull.Mergeable}}<span style="color:var(--accent-green);">Mergeable</span>{{end}}
</div> </div>
<div class="card-meta" style="margin-top:0.5rem;">
<span id="state-section">
{{if eq .Pull.State "closed"}}
<span class="state-closed" id="pull-state">{{.Pull.State}}</span>
<button class="btn btn-secondary" hx-post="/pulls/{{.Pull.RepoOwner}}/{{.Pull.RepoName}}/{{.Pull.Number}}/state" hx-vals='{"state":"open"}' hx-target="#state-section" hx-swap="innerHTML">Reopen PR</button>
{{else}}
<span class="state-open" id="pull-state">{{.Pull.State}}</span>
<button class="btn btn-danger" hx-post="/pulls/{{.Pull.RepoOwner}}/{{.Pull.RepoName}}/{{.Pull.Number}}/state" hx-vals='{"state":"closed"}' hx-target="#state-section" hx-swap="innerHTML">Close PR</button>
{{end}}
</span>
</div>
{{if .RenderedBody}} {{if .RenderedBody}}
<div class="card-body markdown-body">{{.RenderedBody}}</div> <div class="card-body markdown-body">{{.RenderedBody}}</div>
{{else if .Pull.Body}} {{else if .Pull.Body}}