Add server-side work item search

This commit is contained in:
Georg Bauer
2026-07-31 21:03:01 +02:00
parent af392209e8
commit a3962449a2
9 changed files with 234 additions and 42 deletions

View File

@@ -4,8 +4,8 @@ use gotcha_gitea::{
use crate::{
domain::{
HistoryCommit, HomeData, IssueDetails, IssueDraft, IssueEditorData, MilestoneDetails,
MilestoneDraft, Page, PullDetails, RepositoryData, Server,
HistoryCommit, HomeData, IssueDetails, IssueDraft, IssueEditorData, IssueFilter,
MilestoneDetails, MilestoneDraft, Page, PullDetails, RepositoryData, Server,
},
presentation::compact_date,
};
@@ -46,8 +46,7 @@ pub async fn load_issues(
owner: &str,
repository: &str,
status: &str,
labels: &[String],
milestone: &str,
filter: &IssueFilter,
page: i32,
) -> Result<Page<models::Issue>, String> {
client(server)?
@@ -55,8 +54,10 @@ pub async fn load_issues(
&scope(owner, repository)?,
&IssueQuery {
state: status.into(),
labels: (!labels.is_empty()).then(|| labels.join(",")),
milestones: (!milestone.is_empty()).then(|| milestone.into()),
labels: (!filter.labels.is_empty())
.then(|| filter.labels.iter().cloned().collect::<Vec<_>>().join(",")),
milestones: (!filter.milestone.is_empty()).then(|| filter.milestone.clone()),
keyword: (!filter.search_text.is_empty()).then(|| filter.search_text.clone()),
page,
limit: PAGE_SIZE,
..Default::default()
@@ -150,12 +151,14 @@ pub async fn load_pulls(
server: &Server,
status: &str,
milestone: &str,
search_text: &str,
page: i32,
) -> Result<Page<models::Issue>, String> {
client(server)?
.search_pulls(
status,
(!milestone.is_empty()).then_some(milestone),
(!search_text.is_empty()).then_some(search_text),
page,
PAGE_SIZE,
)

View File

@@ -84,6 +84,8 @@ pub struct IssueFilter {
pub milestone: String,
#[serde(default)]
pub labels: BTreeSet<String>,
#[serde(skip)]
pub search_text: String,
}
impl IssueFilter {
@@ -96,6 +98,8 @@ impl IssueFilter {
pub struct PullFilter {
#[serde(default)]
pub milestone: String,
#[serde(skip)]
pub search_text: String,
}
impl PullFilter {
@@ -185,8 +189,44 @@ mod tests {
assert!(
PullFilter {
milestone: "v1".into(),
..Default::default()
}
.is_active("open")
);
assert!(
IssueFilter {
search_text: "needle".into(),
..Default::default()
}
.is_active("open")
);
assert!(
PullFilter {
search_text: "needle".into(),
..Default::default()
}
.is_active("open")
);
}
#[test]
fn search_text_is_session_only() {
let issue = IssueFilter {
search_text: "needle".into(),
..Default::default()
};
let pull = PullFilter {
search_text: "needle".into(),
..Default::default()
};
assert!(!serde_json::to_string(&issue).unwrap().contains("needle"));
assert!(!serde_json::to_string(&pull).unwrap().contains("needle"));
assert_eq!(
serde_json::from_str::<IssueFilter>(r#"{"milestone":"v1"}"#)
.unwrap()
.search_text,
""
);
}
}

View File

@@ -268,14 +268,12 @@ impl GotchaCore {
.unwrap_or_default(),
)
};
let labels: Vec<_> = filter.labels.into_iter().collect();
let page = load_issues(
&server,
&owner,
&repository,
&status,
&labels,
&filter.milestone,
&filter,
valid_page(page)?,
)
.await?;
@@ -333,6 +331,7 @@ impl GotchaCore {
repository: String,
milestone: String,
labels: Vec<String>,
search_text: String,
) -> Result<(), GotchaError> {
let owner = owner.trim();
let repository = repository.trim();
@@ -345,6 +344,7 @@ impl GotchaCore {
.into_iter()
.filter(|label| !label.is_empty())
.collect(),
search_text: search_text.trim().into(),
};
let mut state = self.state.lock().unwrap();
let server = state
@@ -585,8 +585,15 @@ impl GotchaCore {
Ok(filter.is_active(&state.preferences.pull_status))
}
pub fn set_pull_filters(&self, milestone: String) -> Result<(), GotchaError> {
let filter = PullFilter { milestone };
pub fn set_pull_filters(
&self,
milestone: String,
search_text: String,
) -> Result<(), GotchaError> {
let filter = PullFilter {
milestone,
search_text: search_text.trim().into(),
};
let mut state = self.state.lock().unwrap();
let server = state
.active_server
@@ -627,7 +634,14 @@ impl GotchaCore {
.unwrap_or_default(),
)
};
let page = load_pulls(&server, &status, &filter.milestone, valid_page(page)?).await?;
let page = load_pulls(
&server,
&status,
&filter.milestone,
&filter.search_text,
valid_page(page)?,
)
.await?;
Ok(PullListPage {
rows: pull_rows(&page.items),
has_more: page.has_more,

View File

@@ -114,6 +114,7 @@ pub struct IssueFilterOptions {
pub unavailable_labels: Vec<String>,
pub selected_milestone: String,
pub selected_labels: Vec<String>,
pub search_text: String,
}
#[derive(Clone, uniffi::Record)]
@@ -153,6 +154,7 @@ pub struct MilestoneEditorPage {
pub struct PullFilterOptions {
pub milestones: Vec<String>,
pub selected_milestone: String,
pub search_text: String,
}
#[derive(Clone, uniffi::Record)]
@@ -365,6 +367,7 @@ pub fn issue_filter_options(
unavailable_labels,
selected_milestone: filter.milestone.clone(),
selected_labels: filter.labels.iter().cloned().collect(),
search_text: filter.search_text.clone(),
}
}
@@ -419,6 +422,7 @@ pub fn pull_filter_options(milestones: &[String], filter: &PullFilter) -> PullFi
PullFilterOptions {
milestones,
selected_milestone: filter.milestone.clone(),
search_text: filter.search_text.clone(),
}
}
@@ -1365,6 +1369,7 @@ mod tests {
let filter = IssueFilter {
milestone: "v3".into(),
labels: ["bug".into(), "retired".into()].into(),
search_text: "login".into(),
};
let options = issue_filter_options(
&[
@@ -1395,15 +1400,18 @@ mod tests {
assert_eq!(options.milestones, ["v1", "v2", "v3"]);
assert_eq!(options.selected_labels, ["bug", "retired"]);
assert_eq!(options.selected_milestone, "v3");
assert_eq!(options.search_text, "login");
let pull_options = pull_filter_options(
&["v2".into(), "v1".into()],
&PullFilter {
milestone: "v3".into(),
search_text: "review".into(),
},
);
assert_eq!(pull_options.milestones, ["v1", "v2", "v3"]);
assert_eq!(pull_options.selected_milestone, "v3");
assert_eq!(pull_options.search_text, "review");
}
#[test]

View File

@@ -70,6 +70,7 @@ impl Client {
&self,
state: &str,
milestone: Option<&str>,
keyword: Option<&str>,
page: i32,
limit: i32,
) -> Result<Page<models::Issue>> {
@@ -84,7 +85,7 @@ impl Client {
Some(state),
None,
milestone,
None,
keyword,
None,
Some("pulls"),
None,
@@ -122,7 +123,7 @@ impl Client {
let mut pulls = Vec::new();
for page in 1.. {
let batch = self
.search_pulls(state, None, page, DEFAULT_PAGE_SIZE)
.search_pulls(state, None, None, page, DEFAULT_PAGE_SIZE)
.await?;
pulls.extend(batch.items);
if !batch.has_more {