feat: api for moving page (#1060)

This commit is contained in:
Khor Shu Heng 2024-12-11 17:16:59 +08:00 committed by GitHub
parent 254bc23a6e
commit 0a10aff86f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 162 additions and 12 deletions

View File

@ -1,5 +1,6 @@
use client_api_entity::workspace_dto::{
CreatePageParams, CreateSpaceParams, Page, PageCollab, Space, UpdatePageParams, UpdateSpaceParams,
CreatePageParams, CreateSpaceParams, MovePageParams, Page, PageCollab, Space, UpdatePageParams,
UpdateSpaceParams,
};
use reqwest::Method;
use serde_json::json;
@ -24,6 +25,25 @@ impl Client {
AppResponse::<Page>::from_response(resp).await?.into_data()
}
pub async fn move_workspace_page_view(
&self,
workspace_id: Uuid,
view_id: &str,
params: &MovePageParams,
) -> Result<(), AppResponseError> {
let url = format!(
"{}/api/workspace/{}/page-view/{}/move",
self.base_url, workspace_id, view_id
);
let resp = self
.http_client_with_auth(Method::POST, &url)
.await?
.json(params)
.send()
.await?;
AppResponse::<()>::from_response(resp).await?.into_error()
}
pub async fn move_workspace_page_view_to_trash(
&self,
workspace_id: Uuid,

View File

@ -179,6 +179,12 @@ pub struct UpdatePageParams {
pub extra: Option<Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MovePageParams {
pub new_parent_view_id: String,
pub prev_view_id: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PageCollabData {
pub encoded_collab: Vec<u8>,

View File

@ -52,7 +52,7 @@ use crate::biz::workspace::ops::{
get_reactions_on_published_view, remove_comment_on_published_view, remove_reaction_on_comment,
};
use crate::biz::workspace::page_view::{
create_page, create_space, get_page_view_collab, move_page_to_trash,
create_page, create_space, get_page_view_collab, move_page, move_page_to_trash,
restore_all_pages_from_trash, restore_page_from_trash, update_page, update_page_collab_data,
update_space,
};
@ -143,6 +143,10 @@ pub fn workspace_scope() -> Scope {
.route(web::get().to(get_page_view_handler))
.route(web::patch().to(update_page_view_handler)),
)
.service(
web::resource("/{workspace_id}/page-view/{view_id}/move")
.route(web::post().to(move_page_handler)),
)
.service(
web::resource("/{workspace_id}/page-view/{view_id}/move-to-trash")
.route(web::post().to(move_page_to_trash_handler)),
@ -996,6 +1000,27 @@ async fn post_page_view_handler(
Ok(Json(AppResponse::Ok().with_data(page)))
}
async fn move_page_handler(
user_uuid: UserUuid,
path: web::Path<(Uuid, String)>,
payload: Json<MovePageParams>,
state: Data<AppState>,
) -> Result<Json<AppResponse<()>>> {
let uid = state.user_cache.get_user_uid(&user_uuid).await?;
let (workspace_uuid, view_id) = path.into_inner();
move_page(
&state.pg_pool,
&state.collab_access_control_storage,
uid,
workspace_uuid,
&view_id,
&payload.new_parent_view_id,
payload.prev_view_id.clone(),
)
.await?;
Ok(Json(AppResponse::Ok()))
}
async fn move_page_to_trash_handler(
user_uuid: UserUuid,
path: web::Path<(Uuid, String)>,

View File

@ -60,7 +60,7 @@ struct WorkspaceDatabaseUpdate {
struct FolderUpdate {
pub updated_encoded_collab: Vec<u8>,
pub encoded_updates: Vec<u8>,
pub encoded_update: Vec<u8>,
}
#[allow(clippy::too_many_arguments)]
@ -436,7 +436,7 @@ async fn add_new_space_to_folder(
};
Ok(FolderUpdate {
updated_encoded_collab: folder_to_encoded_collab(folder)?,
encoded_updates: encoded_update,
encoded_update,
})
}
@ -470,7 +470,7 @@ async fn update_space_properties(
};
Ok(FolderUpdate {
updated_encoded_collab: folder_to_encoded_collab(folder)?,
encoded_updates: encoded_update,
encoded_update,
})
}
@ -513,7 +513,7 @@ async fn add_new_view_to_folder(
};
Ok(FolderUpdate {
updated_encoded_collab: folder_to_encoded_collab(folder)?,
encoded_updates: encoded_update,
encoded_update,
})
}
@ -538,7 +538,26 @@ async fn update_view_properties(
};
Ok(FolderUpdate {
updated_encoded_collab: folder_to_encoded_collab(folder)?,
encoded_updates: encoded_update,
encoded_update,
})
}
async fn move_view(
view_id: &str,
new_parent_view_id: &str,
prev_view_id: Option<String>,
folder: &mut Folder,
) -> Result<FolderUpdate, AppError> {
let encoded_update = {
let mut txn = folder.collab.transact_mut();
folder
.body
.move_nested_view(&mut txn, view_id, new_parent_view_id, prev_view_id);
txn.encode_update_v1()
};
Ok(FolderUpdate {
updated_encoded_collab: folder_to_encoded_collab(folder)?,
encoded_update,
})
}
@ -566,7 +585,7 @@ async fn move_view_to_trash(view_id: &str, folder: &mut Folder) -> Result<Folder
Ok(FolderUpdate {
updated_encoded_collab: folder_to_encoded_collab(folder)?,
encoded_updates: encoded_update,
encoded_update,
})
}
@ -585,7 +604,7 @@ async fn move_view_out_from_trash(
Ok(FolderUpdate {
updated_encoded_collab: folder_to_encoded_collab(folder)?,
encoded_updates: encoded_update,
encoded_update,
})
}
@ -604,7 +623,7 @@ async fn move_all_views_out_from_trash(folder: &mut Folder) -> Result<FolderUpda
Ok(FolderUpdate {
updated_encoded_collab: folder_to_encoded_collab(folder)?,
encoded_updates: encoded_update,
encoded_update,
})
}
@ -696,7 +715,7 @@ async fn insert_and_broadcast_workspace_folder_update(
broadcast_update(
collab_storage,
&workspace_id.to_string(),
folder_update.encoded_updates.clone(),
folder_update.encoded_update.clone(),
)
.await?;
Ok(())
@ -915,6 +934,32 @@ async fn create_database_page(
})
}
pub async fn move_page(
pg_pool: &PgPool,
collab_storage: &CollabAccessControlStorage,
uid: i64,
workspace_id: Uuid,
view_id: &str,
new_parent_view_id: &str,
prev_view_id: Option<String>,
) -> Result<(), AppError> {
let collab_origin = GetCollabOrigin::User { uid };
let mut folder =
get_latest_collab_folder(collab_storage, collab_origin, &workspace_id.to_string()).await?;
let folder_update = move_view(view_id, new_parent_view_id, prev_view_id, &mut folder).await?;
let mut transaction = pg_pool.begin().await?;
insert_and_broadcast_workspace_folder_update(
uid,
workspace_id,
folder_update,
collab_storage,
&mut transaction,
)
.await?;
transaction.commit().await?;
Ok(())
}
pub async fn move_page_to_trash(
pg_pool: &PgPool,
collab_storage: &CollabAccessControlStorage,

View File

@ -9,7 +9,7 @@ use collab_entity::CollabType;
use collab_folder::{CollabOrigin, Folder};
use serde_json::{json, Value};
use shared_entity::dto::workspace_dto::{
CreatePageParams, CreateSpaceParams, IconType, SpacePermission, UpdatePageParams,
CreatePageParams, CreateSpaceParams, IconType, MovePageParams, SpacePermission, UpdatePageParams,
UpdateSpaceParams, ViewIcon, ViewLayout,
};
use tokio::time::sleep;
@ -208,6 +208,60 @@ async fn create_new_document_page() {
.unwrap();
}
#[tokio::test]
async fn move_page_to_another_space() {
let registered_user = generate_unique_registered_user().await;
let mut app_client = TestClient::user_with_new_device(registered_user.clone()).await;
let web_client = TestClient::user_with_new_device(registered_user.clone()).await;
let workspace_id = app_client.workspace_id().await;
let workspace_uuid = Uuid::parse_str(&workspace_id).unwrap();
app_client.open_workspace_collab(&workspace_id).await;
app_client
.wait_object_sync_complete(&workspace_id)
.await
.unwrap();
let folder_view = web_client
.api_client
.get_workspace_folder(&workspace_id, Some(2), None)
.await
.unwrap();
let general_space = folder_view
.children
.iter()
.find(|v| v.name == "General")
.unwrap()
.clone();
let todo_view_id = general_space
.children
.iter()
.find(|v| v.name == "To-dos")
.map(|v| v.view_id.clone())
.unwrap();
let shared_space = &folder_view
.children
.iter()
.find(|v| v.name == "Shared")
.unwrap()
.clone();
web_client
.api_client
.move_workspace_page_view(
workspace_uuid,
&todo_view_id,
&MovePageParams {
new_parent_view_id: shared_space.view_id.clone(),
prev_view_id: None,
},
)
.await
.unwrap();
let folder = get_latest_folder(&app_client, &workspace_id).await;
let first_children_id = folder.get_view(&shared_space.view_id).unwrap().children[0]
.id
.clone();
assert_eq!(first_children_id, todo_view_id);
}
#[tokio::test]
async fn move_page_to_trash() {
let registered_user = generate_unique_registered_user().await;