Add tauri commands for single repo and cobs

This commit is contained in:
Sebastian Martinez
2024-09-06 15:33:55 +02:00
committed by Rūdolfs Ošiņš
parent 0a059da762
commit 560891d1a6
14 changed files with 282 additions and 25 deletions
+3
View File
@@ -0,0 +1,3 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type Author = { did: string; alias?: string };
+1 -1
View File
@@ -5,7 +5,7 @@
*/
export type Config = {
/**
* Node alias.
* Node Public Key in NID format.
*/
publicKey: string;
/**
+11
View File
@@ -0,0 +1,11 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { Author } from "./Author";
export type Issue = {
id: string;
author: Author;
title: string;
state: { status: "closed"; reason: "other" | "solved" } | { status: "open" };
assignees: Array<Author>;
labels: Array<string>;
};
+26
View File
@@ -0,0 +1,26 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { Author } from "./Author";
export type Patch = {
id: string;
author: Author;
title: string;
state:
| {
status: "draft";
}
| {
status: "open";
conflicts: [string, string][];
}
| {
status: "archived";
}
| {
status: "merged";
revision: string;
commit: string;
};
assignees: Array<Author>;
labels: Array<string>;
};
+1
View File
@@ -1,3 +1,4 @@
pub mod auth;
pub mod cobs;
pub mod profile;
pub mod repos;
+108
View File
@@ -0,0 +1,108 @@
use radicle::identity::RepoId;
use radicle::issue::cache::Issues;
use radicle::patch::cache::Patches;
use crate::error::Error;
use crate::types::cobs;
use crate::AppState;
#[tauri::command]
pub fn list_issues(
ctx: tauri::State<AppState>,
rid: RepoId,
status: query::IssueStatus,
) -> Result<Vec<cobs::Issue>, Error> {
let (repo, _) = ctx.repo(rid)?;
let issues = ctx.profile.issues(&repo)?;
let mut issues: Vec<_> = issues
.list()?
.filter_map(|r| {
let (id, issue) = r.ok()?;
(status.matches(issue.state())).then_some((id, issue))
})
.collect::<Vec<_>>();
issues.sort_by(|(_, a), (_, b)| b.timestamp().cmp(&a.timestamp()));
let aliases = &ctx.profile.aliases();
let issues = issues
.into_iter()
.map(|(id, issue)| cobs::Issue::new(id, issue, aliases))
.collect::<Vec<_>>();
Ok::<_, Error>(issues)
}
#[tauri::command]
pub fn list_patches(
ctx: tauri::State<AppState>,
rid: RepoId,
status: query::PatchStatus,
) -> Result<Vec<cobs::Patch>, Error> {
let (repo, _) = ctx.repo(rid)?;
let patches = ctx.profile.patches(&repo)?;
let mut patches: Vec<_> = patches
.list()?
.filter_map(|r| {
let (id, patch) = r.ok()?;
(status.matches(patch.state())).then_some((id, patch))
})
.collect::<Vec<_>>();
patches.sort_by(|(_, a), (_, b)| b.timestamp().cmp(&a.timestamp()));
let aliases = &ctx.profile.aliases();
let patches = patches
.into_iter()
.map(|(id, patch)| cobs::Patch::new(id, patch, aliases))
.collect::<Vec<_>>();
Ok::<_, Error>(patches)
}
mod query {
use serde::{Deserialize, Serialize};
use radicle::issue;
use radicle::patch;
#[derive(Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum IssueStatus {
Closed,
#[default]
Open,
All,
}
impl IssueStatus {
pub fn matches(&self, issue: &issue::State) -> bool {
match self {
Self::Open => matches!(issue, issue::State::Open),
Self::Closed => matches!(issue, issue::State::Closed { .. }),
Self::All => true,
}
}
}
#[derive(Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum PatchStatus {
#[default]
Open,
Draft,
Archived,
Merged,
All,
}
impl PatchStatus {
pub fn matches(&self, patch: &patch::State) -> bool {
match self {
Self::Open => matches!(patch, patch::State::Open { .. }),
Self::Draft => matches!(patch, patch::State::Draft),
Self::Archived => matches!(patch, patch::State::Archived),
Self::Merged => matches!(patch, patch::State::Merged { .. }),
Self::All => true,
}
}
}
}
+12
View File
@@ -1,3 +1,4 @@
use radicle::identity::RepoId;
use radicle::storage::ReadStorage;
use crate::error::Error;
@@ -28,3 +29,14 @@ pub fn list_repos(ctx: tauri::State<AppState>) -> Result<Vec<types::repo::RepoIn
Ok::<_, Error>(infos)
}
#[tauri::command]
pub fn repo_by_id(
ctx: tauri::State<AppState>,
rid: RepoId,
) -> Result<types::repo::RepoInfo, Error> {
let (repo, doc) = ctx.repo(rid)?;
let repo_info = ctx.repo_info(&repo, doc)?;
Ok::<_, Error>(repo_info)
}
-19
View File
@@ -1,19 +0,0 @@
use serde_json::{json, Value};
use radicle::identity;
use radicle::node::AliasStore;
pub(crate) struct Author<'a>(&'a identity::Did);
impl<'a> Author<'a> {
pub fn new(did: &'a identity::Did) -> Self {
Self(did)
}
pub fn as_json(&self, aliases: &impl AliasStore) -> Value {
aliases.alias(self.0).map_or(
json!({ "id": self.0 }),
|alias| json!({ "id": self.0, "alias": alias, }),
)
}
}
+5 -3
View File
@@ -1,6 +1,5 @@
mod commands;
mod error;
mod json;
mod types;
use serde_json::json;
@@ -15,7 +14,7 @@ use radicle::patch::cache::Patches;
use radicle::storage::git::Repository;
use radicle::storage::{ReadRepository, ReadStorage};
use commands::{auth, profile, repos};
use commands::{auth, cobs, profile, repos};
use types::repo::SupportedPayloads;
struct AppState {
@@ -35,7 +34,7 @@ impl AppState {
let delegates = doc
.delegates
.into_iter()
.map(|did| json::Author::new(&did).as_json(&aliases))
.map(|did| types::cobs::Author::new(did, &aliases))
.collect::<Vec<_>>();
let db = &self.profile.database()?;
let seeding = db.count(&rid).unwrap_or_default();
@@ -107,6 +106,9 @@ pub fn run() {
.invoke_handler(tauri::generate_handler![
auth::authenticate,
repos::list_repos,
repos::repo_by_id,
cobs::list_issues,
cobs::list_patches,
profile::config,
])
.run(tauri::generate_context!())
+95
View File
@@ -0,0 +1,95 @@
use radicle::cob::Label;
use radicle::identity;
use radicle::issue;
use radicle::node::{Alias, AliasStore};
use radicle::patch;
use serde::Serialize;
use ts_rs::TS;
#[derive(Serialize, TS)]
pub(crate) struct Author {
#[ts(as = "String")]
did: identity::Did,
#[serde(skip_serializing_if = "Option::is_none")]
#[ts(as = "Option<String>")]
#[ts(optional)]
alias: Option<Alias>,
}
impl Author {
pub fn new(did: identity::Did, aliases: &impl AliasStore) -> Self {
Self {
did,
alias: aliases.alias(&did),
}
}
}
#[derive(TS, Serialize)]
#[ts(export)]
pub struct Issue {
#[ts(as = "String")]
id: String,
author: Author,
title: String,
#[ts(type = "{ status: 'closed', reason: 'other' | 'solved' } | { status: 'open' } ")]
state: issue::State,
assignees: Vec<Author>,
#[ts(as = "Vec<String>")]
labels: Vec<Label>,
}
impl Issue {
pub fn new(id: issue::IssueId, issue: issue::Issue, aliases: &impl AliasStore) -> Self {
Self {
id: id.to_string(),
author: Author::new(*issue.author().id(), aliases),
title: issue.title().to_string(),
state: *issue.state(),
assignees: issue
.assignees()
.map(|did| Author::new(*did, aliases))
.collect::<Vec<_>>(),
labels: issue.labels().cloned().collect::<Vec<_>>(),
}
}
}
#[derive(TS, Serialize)]
#[ts(export)]
pub struct Patch {
#[ts(as = "String")]
id: String,
author: Author,
title: String,
#[ts(type = r#"{
status: 'draft'
} | {
status: 'open',
conflicts: [string, string][]
} | {
status: 'archived'
} | {
status: 'merged', revision: string, commit: string
} "#)]
state: patch::State,
assignees: Vec<Author>,
#[ts(as = "Vec<String>")]
labels: Vec<Label>,
}
impl Patch {
pub fn new(id: patch::PatchId, patch: patch::Patch, aliases: &impl AliasStore) -> Self {
Self {
id: id.to_string(),
author: Author::new(*patch.author().id(), aliases),
title: patch.title().to_string(),
state: patch.state().clone(),
assignees: patch
.assignees()
.map(|did| Author::new(did, aliases))
.collect::<Vec<_>>(),
labels: patch.labels().cloned().collect::<Vec<_>>(),
}
}
}
+1 -1
View File
@@ -6,7 +6,7 @@ use radicle::node::config::DefaultSeedingPolicy;
use radicle::node::Alias;
/// Service configuration.
#[derive(Debug, Clone, TS, Serialize)]
#[derive(TS, Serialize)]
#[serde(rename_all = "camelCase")]
#[ts(export)]
pub struct Config {
+1
View File
@@ -1,2 +1,3 @@
pub mod cobs;
pub mod config;
pub mod repo;
+3 -1
View File
@@ -4,13 +4,15 @@ use ts_rs::TS;
use radicle::identity::RepoId;
use super::cobs::Author;
/// Repos info.
#[derive(Serialize, TS)]
#[ts(export)]
pub struct RepoInfo {
pub payloads: SupportedPayloads,
#[ts(type = "({ id: string } | { id: string, alias?: string })[]")]
pub delegates: Vec<Value>,
pub delegates: Vec<Author>,
pub threshold: usize,
#[ts(type = "{ type: 'public' } | { type: 'private', allow?: string[] }")]
pub visibility: radicle::identity::Visibility,
+15
View File
@@ -4,9 +4,24 @@
import Header from "@app/components/Header.svelte";
import RepoCard from "@app/components/RepoCard.svelte";
import { onMount } from "svelte";
import { invoke } from "@tauri-apps/api/core";
export let repos: RepoInfo[];
export let config: Config;
onMount(async () => {
const patches = await invoke("list_patches", {
rid: "rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5",
status: "open",
});
const issues = await invoke("list_issues", {
rid: "rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5",
status: "open",
});
console.log(patches, issues);
});
</script>
<style>