use anyhow::Result; use askama::Template; use derive_builder::Builder; use phono_backends::client::WorkspaceClient; use phono_models::{client::AppDbClient, workspace::Workspace}; use sqlx::postgres::types::Oid; use uuid::Uuid; use crate::{ navigator::Navigator, workspaces::{RelationPortalSet, fetch_all_accessible_portals}, }; #[derive(Builder, Clone, Debug, Template)] #[template(path = "workspace_nav.html")] pub(crate) struct WorkspaceNav { workspace: Workspace, relations: Vec, #[builder(default, setter(strip_option))] current: Option, navigator: Navigator, } impl WorkspaceNav { pub fn builder() -> WorkspaceNavBuilder { WorkspaceNavBuilder::default() } } #[derive(Clone, Debug)] pub struct RelationItem { pub name: String, pub oid: Oid, pub portals: Vec, } #[derive(Clone, Debug)] pub struct PortalItem { pub name: String, pub id: Uuid, } #[derive(Clone, Debug, PartialEq)] pub enum NavLocation { Rel(Oid, Option), } #[derive(Clone, Debug, PartialEq)] pub enum RelLocation { Portal(Uuid), Sharing, } impl WorkspaceNavBuilder { /// Helper function to populate relations and lenses automatically. /// [`WorkspaceNavBuilder::workspace()`] must be called first, or else this /// method will return an error. /// /// WARNING: This assumes that `workspace_client` is authenticated with /// [`RoleAssignment::User`] for the current user. pub async fn populate_rels( &mut self, app_db: &mut AppDbClient, workspace_client: &mut WorkspaceClient, ) -> Result<&mut Self> { let workspace_id = self .workspace .clone() .ok_or(WorkspaceNavBuilderError::UninitializedField("workspace"))? .id; Ok(self.relations( fetch_all_accessible_portals(workspace_id, app_db, workspace_client) .await? .into_iter() .map(|RelationPortalSet { rel, portals }| RelationItem { name: rel.relname, oid: rel.oid, portals: portals .into_iter() .map(|portal| PortalItem { id: portal.id, name: portal.name, }) .collect(), }) .collect(), )) } }