feat(ios): extend swipe actions to milestones

This commit is contained in:
Georg Bauer
2026-08-01 20:57:16 +02:00
parent 5a48456333
commit c0b03f26ac
7 changed files with 413 additions and 96 deletions

View File

@@ -148,6 +148,32 @@ pub async fn save_milestone(
.map_err(message)
}
pub async fn set_milestone_closed(
server: &Server,
owner: &str,
repository: &str,
id: i64,
closed: bool,
) -> Result<(), String> {
client(server)?
.set_milestone_closed(&scope(owner, repository)?, id, closed)
.await
.map(|_| ())
.map_err(message)
}
pub async fn delete_milestone(
server: &Server,
owner: &str,
repository: &str,
id: i64,
) -> Result<(), String> {
client(server)?
.delete_milestone(&scope(owner, repository)?, &id.to_string())
.await
.map_err(message)
}
pub async fn load_pulls(
server: &Server,
status: &str,

View File

@@ -586,6 +586,35 @@ impl GotchaCore {
.ok_or_else(|| "The saved milestone has no ID.".into())
}
pub async fn set_milestone_closed(
&self,
owner: String,
repository: String,
id: i64,
closed: bool,
) -> Result<(), GotchaError> {
let (owner, repository) = validate_repository(&owner, &repository)?;
if id <= 0 {
return Err("Invalid milestone selection.".into());
}
set_milestone_closed(&self.server()?, owner, repository, id, closed).await?;
Ok(())
}
pub async fn delete_milestone(
&self,
owner: String,
repository: String,
id: i64,
) -> Result<(), GotchaError> {
let (owner, repository) = validate_repository(&owner, &repository)?;
if id <= 0 {
return Err("Invalid milestone selection.".into());
}
delete_milestone(&self.server()?, owner, repository, id).await?;
Ok(())
}
pub async fn pull_filters(&self) -> Result<PullFilterOptions, GotchaError> {
let server = self.server()?;
let filter = self

View File

@@ -208,18 +208,68 @@ impl Client {
.map_err(Into::into)
}
pub async fn set_milestone_closed(
&self,
repository: &RepositoryId,
id: i64,
closed: bool,
) -> Result<models::Milestone> {
if id < 1 {
return Err(Error::InvalidInput(
"milestone id must be a positive integer".into(),
));
}
self.edit_milestone(
repository,
&id.to_string(),
models::EditMilestoneOption {
state: Some(if closed { "closed" } else { "open" }.into()),
..Default::default()
},
)
.await
}
pub async fn delete_milestone(&self, repository: &RepositoryId, id: &str) -> Result<()> {
let id = id
.parse()
.map_err(|_| Error::InvalidInput("milestone id must be a positive integer".into()))?;
let milestone = self.milestone(repository, id).await?;
let title = milestone
.title
.ok_or_else(|| Error::Generated("The milestone has no title.".into()))?;
if !self
.issues(repository, &assigned_items_query(title))
.await?
.items
.is_empty()
{
return Err(Error::InvalidInput(
"Remove all assigned issues and pull requests before deleting this milestone."
.into(),
));
}
apis::issue_api::issue_delete_milestone(
&self.configuration(),
&repository.owner,
&repository.repository,
id,
&id.to_string(),
)
.await
.map_err(Error::generated)
}
}
fn assigned_items_query(title: String) -> IssueQuery {
IssueQuery {
state: "all".into(),
kind: "all".into(),
milestones: Some(title),
limit: 1,
..Default::default()
}
}
#[derive(Serialize)]
struct MilestoneRequest {
title: String,
@@ -244,4 +294,13 @@ mod tests {
assert!(value.get("due_on").unwrap().is_null());
assert_eq!(value["state"], "closed");
}
#[test]
fn assigned_item_check_includes_issues_and_pull_requests() {
let query = assigned_items_query("Release".into());
assert_eq!(query.state, "all");
assert_eq!(query.kind, "all");
assert_eq!(query.milestones.as_deref(), Some("Release"));
assert_eq!(query.limit, 1);
}
}