AppFlowy-Cloud/libs/workspace-template/src/lib.rs

147 lines
4.0 KiB
Rust

pub mod document;
mod hierarchy_builder;
use crate::hierarchy_builder::{FlattedViews, WorkspaceViewBuilder};
pub use anyhow::Result;
use async_trait::async_trait;
use collab::core::collab::MutexCollab;
use collab::core::collab_plugin::EncodedCollab;
use collab::core::origin::CollabOrigin;
use collab_entity::CollabType;
use collab_folder::{
timestamp, Folder, FolderData, RepeatedViewIdentifier, ViewIdentifier, ViewLayout, Workspace,
};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
#[async_trait]
pub trait WorkspaceTemplate {
fn layout(&self) -> ViewLayout;
async fn create(&self, object_id: String) -> Result<TemplateData>;
async fn create_workspace_view(
&self,
uid: i64,
workspace_view_builder: Arc<RwLock<WorkspaceViewBuilder>>,
) -> Result<TemplateData>;
}
pub struct TemplateData {
pub object_id: String,
pub object_type: CollabType,
pub object_data: EncodedCollab,
}
pub type WorkspaceTemplateHandlers = HashMap<ViewLayout, Arc<dyn WorkspaceTemplate + Send + Sync>>;
/// A builder for creating a workspace template.
/// workspace template is a set of views that are created when a workspace is created.
pub struct WorkspaceTemplateBuilder {
pub uid: i64,
pub workspace_id: String,
pub handlers: WorkspaceTemplateHandlers,
}
impl WorkspaceTemplateBuilder {
pub fn new(uid: i64, workspace_id: &str) -> Self {
let handlers = WorkspaceTemplateHandlers::default();
Self {
uid,
workspace_id: workspace_id.to_string(),
handlers,
}
}
pub fn with_template<T>(mut self, template: T) -> Self
where
T: WorkspaceTemplate + Send + Sync + 'static,
{
self.handlers.insert(template.layout(), Arc::new(template));
self
}
pub fn with_templates<T>(mut self, templates: Vec<T>) -> Self
where
T: WorkspaceTemplate + Send + Sync + 'static,
{
for template in templates {
self.handlers.insert(template.layout(), Arc::new(template));
}
self
}
pub async fn build(&self) -> Result<Vec<TemplateData>> {
let workspace_view_builder = Arc::new(RwLock::new(WorkspaceViewBuilder::new(
self.workspace_id.clone(),
self.uid,
)));
let mut templates = vec![];
for handler in self.handlers.values() {
if let Ok(template) = handler
.create_workspace_view(self.uid, workspace_view_builder.clone())
.await
{
templates.push(template);
}
}
let views = workspace_view_builder.write().await.build();
// Safe to unwrap because we have at least one view.
let first_view = views.first().unwrap().parent_view.clone();
let first_level_views = views
.iter()
.map(|value| ViewIdentifier {
id: value.parent_view.id.clone(),
})
.collect::<Vec<_>>();
let workspace = Workspace {
id: self.workspace_id.clone(),
name: "Workspace".to_string(),
child_views: RepeatedViewIdentifier::new(first_level_views),
created_at: timestamp(),
created_by: Some(self.uid),
last_edited_time: timestamp(),
last_edited_by: Some(self.uid),
};
let uid = self.uid;
let workspace_id = self.workspace_id.clone();
let folder_template = tokio::task::spawn_blocking(move || {
let folder_data = FolderData {
workspace,
current_view: first_view.id,
views: FlattedViews::flatten_views(views),
favorites: Default::default(),
recent: Default::default(),
trash: Default::default(),
private: Default::default(),
};
let collab = Arc::new(MutexCollab::new(
CollabOrigin::Empty,
&workspace_id,
vec![],
false,
));
let folder = Folder::create(uid, collab, None, folder_data);
let data = folder.encode_collab_v1()?;
Ok::<_, anyhow::Error>(TemplateData {
object_id: workspace_id,
object_type: CollabType::Folder,
object_data: data,
})
})
.await??;
templates.push(folder_template);
Ok(templates)
}
}
pub fn gen_view_id() -> String {
uuid::Uuid::new_v4().to_string()
}