Radish alpha
r
rad:z4D5UCArafTzTQpDZNQRuqswh3ury
Radicle desktop app
Radicle
Git
Add tauri commands for single repo and cobs
Merged did:key:z6MkkfM3...sVz5 opened 1 year ago

repo_by_id: For getting the RepoInfo of a single repo

Requires { rid: <RID> } as an argument to the invoke call

list_patches: For getting a list of patches

Requires { rid: <RID>, status: "open" | "draft" | .. } as an argument to the invoke call

list_issues: For getting a list of issues

Requires { rid: <RID>, status: "open" | "closed" | .. } as an argument to the invoke call

14 files changed +282 -25 0a059da7 560891d1
added src-tauri/bindings/Author.ts
@@ -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 };
modified src-tauri/bindings/Config.ts
@@ -5,7 +5,7 @@
 */
export type Config = {
  /**
-
   * Node alias.
+
   * Node Public Key in NID format.
   */
  publicKey: string;
  /**
added src-tauri/bindings/Issue.ts
@@ -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>;
+
};
added src-tauri/bindings/Patch.ts
@@ -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>;
+
};
modified src-tauri/src/commands.rs
@@ -1,3 +1,4 @@
pub mod auth;
+
pub mod cobs;
pub mod profile;
pub mod repos;
added src-tauri/src/commands/cobs.rs
@@ -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,
+
            }
+
        }
+
    }
+
}
modified src-tauri/src/commands/repos.rs
@@ -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)
+
}
deleted src-tauri/src/json.rs
@@ -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, }),
-
        )
-
    }
-
}
modified src-tauri/src/lib.rs
@@ -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();
@@ -102,6 +101,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!())
added src-tauri/src/types/cobs.rs
@@ -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<_>>(),
+
        }
+
    }
+
}
modified src-tauri/src/types/config.rs
@@ -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 {
modified src-tauri/src/types/mod.rs
@@ -1,2 +1,3 @@
+
pub mod cobs;
pub mod config;
pub mod repo;
modified src-tauri/src/types/repo.rs
@@ -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,
modified src/views/Home.svelte
@@ -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>