Skip to content
Clone
1098df8
114 lines 4.1 KB Rust
use export::http::{Path, Query, Response, State};
use maud::html;
use crate::handler::html::{HtmlRepoPath, archived_class, resolve, response_html};
use crate::service::GitService;
use crate::share::access::BasicAuth;
use crate::share::validate;
#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
pub struct RefsDropdownQuery {
#[serde(default)]
pub revision: Option<String>,
}
#[utoipa::path(
get,
path = "/repos/{space_name}/{repo_name}/html/refs-dropdown",
tag = "html",
description = "渲染分支与标签切换下拉框片段。",
operation_id = "render_refs_dropdown",
params(HtmlRepoPath, RefsDropdownQuery),
responses(
(status = 200, description = "refs dropdown html fragment", content_type = "text/html"),
(status = 404, description = "repo not found")
)
)]
pub async fn render_refs_dropdown(
State(service): State<GitService>,
Path(path): Path<HtmlRepoPath>,
Query(q): Query<RefsDropdownQuery>,
auth: BasicAuth,
) -> Response {
let ctx = match resolve(service.clone(), path, auth).await {
Ok(c) => c,
Err(resp) => return resp,
};
let current_rev = q
.revision
.as_deref()
.filter(|r| validate::validate_revision(r).is_ok())
.unwrap_or(ctx.dto.default_branch.as_str());
let repo_ctx = service
.context(ctx.space_name.clone(), ctx.repo_name.clone())
.await
.ok();
let branches = match repo_ctx.as_ref() {
Some(repo_ctx) => service.list_branches(repo_ctx).await.unwrap_or_default(),
None => Vec::new(),
};
let tags = match repo_ctx.as_ref() {
Some(repo_ctx) => service.list_tags(repo_ctx).await.unwrap_or_default(),
None => Vec::new(),
};
let base = "git-refs-dropdown relative inline-flex w-full max-w-xs flex-col";
let cls = archived_class(&ctx.dto, base);
let default = ctx.dto.default_branch.clone();
let space = ctx.space_name.clone();
let repo = ctx.repo_name.clone();
let is_custom_rev = !branches.iter().any(|b| b.name == current_rev)
&& !tags.iter().any(|t| t.name == current_rev)
&& current_rev != default;
let markup = html! {
div class=(cls)
data-component="git-refs-dropdown"
data-space=(space)
data-repo=(repo)
data-default-branch=(default)
data-current-revision=(current_rev)
data-archived=(if ctx.dto.is_archived { "true" } else { "false" }) {
label class="mb-1 block text-xs font-medium text-zinc-600" { "Switch ref" }
select class="w-full rounded border border-zinc-200 bg-white px-2 py-1 text-sm text-zinc-800"
data-action="switch-ref" {
@if is_custom_rev {
option value=(current_rev) selected[true] {
"Tree: " (current_rev)
}
}
@if !branches.is_empty() {
optgroup label="Branches" {
@for b in &branches {
@let is_sel = b.name == current_rev;
@let is_def = b.name == default;
option value=(b.name) selected[is_sel] {
(b.name) (if is_def { " (default)" } else { "" })
}
}
}
} @else {
option value=(default) selected[true] {
(default) " (default)"
}
}
@if !tags.is_empty() {
optgroup label="Tags" {
@for t in &tags {
@let is_sel = t.name == current_rev;
option value=(t.name) selected[is_sel] {
(t.name)
}
}
}
}
}
p class="mt-1 text-xs text-zinc-500" {
(ctx.dto.branches_count) " branches · " (ctx.dto.tags_count) " tags"
}
}
};
response_html(markup, 30)
}