mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
fix: duplicate trash (#1818)
This commit is contained in:
parent
e77fef3a19
commit
e5703f83fb
@ -83,15 +83,14 @@ void main() {
|
|||||||
context.appBloc.add(AppEvent.deleteView(view.id));
|
context.appBloc.add(AppEvent.deleteView(view.id));
|
||||||
await blocResponseFuture();
|
await blocResponseFuture();
|
||||||
}
|
}
|
||||||
assert(trashBloc.state.objects[0].id == context.allViews[0].id);
|
expect(trashBloc.state.objects[0].id, context.allViews[0].id);
|
||||||
assert(trashBloc.state.objects[1].id == context.allViews[1].id);
|
expect(trashBloc.state.objects[1].id, context.allViews[1].id);
|
||||||
assert(trashBloc.state.objects[2].id == context.allViews[2].id);
|
expect(trashBloc.state.objects[2].id, context.allViews[2].id);
|
||||||
|
|
||||||
// delete a view permanently
|
// delete a view permanently
|
||||||
trashBloc.add(TrashEvent.delete(trashBloc.state.objects[0]));
|
trashBloc.add(TrashEvent.delete(trashBloc.state.objects[0]));
|
||||||
await blocResponseFuture();
|
await blocResponseFuture();
|
||||||
assert(trashBloc.state.objects.length == 2,
|
expect(trashBloc.state.objects.length, 2);
|
||||||
"but receive ${trashBloc.state.objects.length}");
|
|
||||||
|
|
||||||
// delete all view permanently
|
// delete all view permanently
|
||||||
trashBloc.add(const TrashEvent.deleteAll());
|
trashBloc.add(const TrashEvent.deleteAll());
|
||||||
|
@ -282,22 +282,38 @@ impl FolderPad {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_trash(&mut self, trash: Vec<TrashRevision>) -> SyncResult<Option<FolderChangeset>> {
|
pub fn create_trash(&mut self, trash: Vec<TrashRevision>) -> SyncResult<Option<FolderChangeset>> {
|
||||||
self.with_trash(|t| {
|
self.with_trash(|original_trash| {
|
||||||
let mut new_trash = trash.into_iter().map(Arc::new).collect::<Vec<Arc<TrashRevision>>>();
|
let mut new_trash = trash
|
||||||
t.append(&mut new_trash);
|
.into_iter()
|
||||||
|
.flat_map(|new_trash| {
|
||||||
Ok(Some(()))
|
if original_trash.iter().any(|old_trash| old_trash.id == new_trash.id) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(Arc::new(new_trash))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<Arc<TrashRevision>>>();
|
||||||
|
if new_trash.is_empty() {
|
||||||
|
Ok(None)
|
||||||
|
} else {
|
||||||
|
original_trash.append(&mut new_trash);
|
||||||
|
Ok(Some(()))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_trash(&self, trash_id: Option<String>) -> SyncResult<Vec<TrashRevision>> {
|
pub fn read_trash(&self, trash_id: Option<String>) -> SyncResult<Vec<TrashRevision>> {
|
||||||
match trash_id {
|
match trash_id {
|
||||||
None => Ok(self
|
None => {
|
||||||
.folder_rev
|
// Removes the duplicate items if exist
|
||||||
.trash
|
let mut trash_items = Vec::<TrashRevision>::with_capacity(self.folder_rev.trash.len());
|
||||||
.iter()
|
for trash_item in self.folder_rev.trash.iter() {
|
||||||
.map(|t| t.as_ref().clone())
|
if !trash_items.iter().any(|item| item.id == trash_item.id) {
|
||||||
.collect::<Vec<TrashRevision>>()),
|
trash_items.push(trash_item.as_ref().clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(trash_items)
|
||||||
|
}
|
||||||
Some(trash_id) => match self.folder_rev.trash.iter().find(|t| t.id == trash_id) {
|
Some(trash_id) => match self.folder_rev.trash.iter().find(|t| t.id == trash_id) {
|
||||||
Some(trash) => Ok(vec![trash.as_ref().clone()]),
|
Some(trash) => Ok(vec![trash.as_ref().clone()]),
|
||||||
None => Ok(vec![]),
|
None => Ok(vec![]),
|
||||||
|
@ -13,7 +13,9 @@ use tokio::sync::{broadcast, mpsc};
|
|||||||
pub struct TrashController {
|
pub struct TrashController {
|
||||||
persistence: Arc<FolderPersistence>,
|
persistence: Arc<FolderPersistence>,
|
||||||
notify: broadcast::Sender<TrashEvent>,
|
notify: broadcast::Sender<TrashEvent>,
|
||||||
|
#[allow(dead_code)]
|
||||||
cloud_service: Arc<dyn FolderCouldServiceV1>,
|
cloud_service: Arc<dyn FolderCouldServiceV1>,
|
||||||
|
#[allow(dead_code)]
|
||||||
user: Arc<dyn WorkspaceUser>,
|
user: Arc<dyn WorkspaceUser>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,11 +56,6 @@ impl TrashController {
|
|||||||
ty: trash.ty.into(),
|
ty: trash.ty.into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.delete_trash_on_server(RepeatedTrashIdPB {
|
|
||||||
items: vec![identifier.clone()],
|
|
||||||
delete_all: false,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
tracing::Span::current().record("putback", format!("{:?}", &identifier).as_str());
|
tracing::Span::current().record("putback", format!("{:?}", &identifier).as_str());
|
||||||
let _ = self.notify.send(TrashEvent::Putback(vec![identifier].into(), tx));
|
let _ = self.notify.send(TrashEvent::Putback(vec![identifier].into(), tx));
|
||||||
rx.recv().await.unwrap()?;
|
rx.recv().await.unwrap()?;
|
||||||
@ -82,7 +79,6 @@ impl TrashController {
|
|||||||
let _ = rx.recv().await;
|
let _ = rx.recv().await;
|
||||||
|
|
||||||
notify_trash_changed(RepeatedTrashPB { items: vec![] });
|
notify_trash_changed(RepeatedTrashPB { items: vec![] });
|
||||||
self.delete_all_trash_on_server().await?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,7 +93,6 @@ impl TrashController {
|
|||||||
self.delete_with_identifiers(all_trash_identifiers).await?;
|
self.delete_with_identifiers(all_trash_identifiers).await?;
|
||||||
|
|
||||||
notify_trash_changed(RepeatedTrashPB { items: vec![] });
|
notify_trash_changed(RepeatedTrashPB { items: vec![] });
|
||||||
self.delete_all_trash_on_server().await?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,7 +105,6 @@ impl TrashController {
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
notify_trash_changed(trash_revs);
|
notify_trash_changed(trash_revs);
|
||||||
self.delete_trash_on_server(trash_identifiers)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -169,8 +163,6 @@ impl TrashController {
|
|||||||
self.persistence
|
self.persistence
|
||||||
.begin_transaction(|transaction| {
|
.begin_transaction(|transaction| {
|
||||||
transaction.create_trash(trash_revs.clone())?;
|
transaction.create_trash(trash_revs.clone())?;
|
||||||
let _ = self.create_trash_on_server(trash_revs);
|
|
||||||
|
|
||||||
notify_trash_changed(transaction.read_trash(None)?);
|
notify_trash_changed(transaction.read_trash(None)?);
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
@ -194,7 +186,6 @@ impl TrashController {
|
|||||||
.map(|trash_rev| trash_rev.into())
|
.map(|trash_rev| trash_rev.into())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
self.read_trash_on_server()?;
|
|
||||||
Ok(RepeatedTrashPB { items })
|
Ok(RepeatedTrashPB { items })
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,74 +202,6 @@ impl TrashController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TrashController {
|
|
||||||
#[tracing::instrument(level = "trace", skip(self, trash), err)]
|
|
||||||
fn create_trash_on_server<T: Into<RepeatedTrashIdPB>>(&self, trash: T) -> FlowyResult<()> {
|
|
||||||
let token = self.user.token()?;
|
|
||||||
let trash_identifiers = trash.into();
|
|
||||||
let server = self.cloud_service.clone();
|
|
||||||
// TODO: retry?
|
|
||||||
let _ = tokio::spawn(async move {
|
|
||||||
match server.create_trash(&token, trash_identifiers).await {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(e) => log::error!("Create trash failed: {:?}", e),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(level = "trace", skip(self, trash), err)]
|
|
||||||
fn delete_trash_on_server<T: Into<RepeatedTrashIdPB>>(&self, trash: T) -> FlowyResult<()> {
|
|
||||||
let token = self.user.token()?;
|
|
||||||
let trash_identifiers = trash.into();
|
|
||||||
let server = self.cloud_service.clone();
|
|
||||||
let _ = tokio::spawn(async move {
|
|
||||||
match server.delete_trash(&token, trash_identifiers).await {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(e) => log::error!("Delete trash failed: {:?}", e),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(level = "trace", skip(self), err)]
|
|
||||||
fn read_trash_on_server(&self) -> FlowyResult<()> {
|
|
||||||
let token = self.user.token()?;
|
|
||||||
let server = self.cloud_service.clone();
|
|
||||||
let persistence = self.persistence.clone();
|
|
||||||
|
|
||||||
tokio::spawn(async move {
|
|
||||||
match server.read_trash(&token).await {
|
|
||||||
Ok(trash_rev) => {
|
|
||||||
tracing::debug!("Remote trash count: {}", trash_rev.len());
|
|
||||||
let result = persistence
|
|
||||||
.begin_transaction(|transaction| {
|
|
||||||
transaction.create_trash(trash_rev.clone())?;
|
|
||||||
transaction.read_trash(None)
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
|
|
||||||
match result {
|
|
||||||
Ok(trash_revs) => {
|
|
||||||
notify_trash_changed(trash_revs);
|
|
||||||
}
|
|
||||||
Err(e) => log::error!("Save trash failed: {:?}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => log::error!("Read trash failed: {:?}", e),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(level = "trace", skip(self), err)]
|
|
||||||
async fn delete_all_trash_on_server(&self) -> FlowyResult<()> {
|
|
||||||
let token = self.user.token()?;
|
|
||||||
let server = self.cloud_service.clone();
|
|
||||||
server.delete_trash(&token, RepeatedTrashIdPB::all()).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(repeated_trash), fields(n_trash))]
|
#[tracing::instrument(level = "debug", skip(repeated_trash), fields(n_trash))]
|
||||||
fn notify_trash_changed<T: Into<RepeatedTrashPB>>(repeated_trash: T) {
|
fn notify_trash_changed<T: Into<RepeatedTrashPB>>(repeated_trash: T) {
|
||||||
let repeated_trash = repeated_trash.into();
|
let repeated_trash = repeated_trash.into();
|
||||||
|
@ -2,7 +2,7 @@ use serde::de::Visitor;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_repr::*;
|
use serde_repr::*;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
#[derive(Default, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
#[derive(Default, Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct TrashRevision {
|
pub struct TrashRevision {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ pub struct TrashRevision {
|
|||||||
pub ty: TrashTypeRevision,
|
pub ty: TrashTypeRevision,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Eq, PartialEq, Debug, Clone, Serialize_repr)]
|
#[derive(Eq, PartialEq, Debug, Clone, Hash, Serialize_repr)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum TrashTypeRevision {
|
pub enum TrashTypeRevision {
|
||||||
Unknown = 0,
|
Unknown = 0,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user