2021-10-13 11:10:29 +08:00
|
|
|
|
use crate::{
|
2021-10-15 15:52:08 +08:00
|
|
|
|
entities::trash::{RepeatedTrash, Trash, TrashType},
|
2021-10-13 23:11:45 +08:00
|
|
|
|
errors::{WorkspaceError, WorkspaceResult},
|
2021-10-13 11:10:29 +08:00
|
|
|
|
module::WorkspaceDatabase,
|
2021-10-14 14:34:22 +08:00
|
|
|
|
notify::{send_anonymous_dart_notification, WorkspaceNotification},
|
2021-10-16 16:45:52 +08:00
|
|
|
|
sql_tables::trash::TrashTableSql,
|
2021-10-13 11:10:29 +08:00
|
|
|
|
};
|
2021-10-16 16:45:52 +08:00
|
|
|
|
use crossbeam_utils::thread;
|
2021-10-13 23:11:45 +08:00
|
|
|
|
use flowy_database::SqliteConnection;
|
2021-10-15 15:52:08 +08:00
|
|
|
|
use std::sync::Arc;
|
2021-10-15 13:10:52 +08:00
|
|
|
|
use tokio::sync::{broadcast, mpsc};
|
2021-10-13 23:11:45 +08:00
|
|
|
|
|
2021-10-15 13:10:52 +08:00
|
|
|
|
#[derive(Clone)]
|
2021-10-13 23:11:45 +08:00
|
|
|
|
pub enum TrashEvent {
|
2021-10-16 16:45:52 +08:00
|
|
|
|
NewTrash(TrashType, Vec<String>, mpsc::Sender<WorkspaceResult<()>>),
|
2021-10-15 15:52:08 +08:00
|
|
|
|
Putback(TrashType, Vec<String>, mpsc::Sender<WorkspaceResult<()>>),
|
|
|
|
|
Delete(TrashType, Vec<String>, mpsc::Sender<WorkspaceResult<()>>),
|
2021-10-13 23:11:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TrashEvent {
|
2021-10-15 15:52:08 +08:00
|
|
|
|
pub fn select(self, s: TrashType) -> Option<TrashEvent> {
|
2021-10-13 23:11:45 +08:00
|
|
|
|
match &self {
|
2021-10-15 13:10:52 +08:00
|
|
|
|
TrashEvent::Putback(source, _, _) => {
|
2021-10-13 23:11:45 +08:00
|
|
|
|
if source == &s {
|
|
|
|
|
return Some(self);
|
|
|
|
|
}
|
|
|
|
|
},
|
2021-10-15 13:10:52 +08:00
|
|
|
|
TrashEvent::Delete(source, _, _) => {
|
2021-10-13 23:11:45 +08:00
|
|
|
|
if source == &s {
|
|
|
|
|
return Some(self);
|
|
|
|
|
}
|
|
|
|
|
},
|
2021-10-16 16:45:52 +08:00
|
|
|
|
TrashEvent::NewTrash(source, _, _) => {
|
|
|
|
|
if source == &s {
|
|
|
|
|
return Some(self);
|
|
|
|
|
}
|
|
|
|
|
},
|
2021-10-13 23:11:45 +08:00
|
|
|
|
}
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-10-13 11:10:29 +08:00
|
|
|
|
|
|
|
|
|
pub struct TrashCan {
|
|
|
|
|
database: Arc<dyn WorkspaceDatabase>,
|
2021-10-13 23:11:45 +08:00
|
|
|
|
notify: broadcast::Sender<TrashEvent>,
|
2021-10-13 11:10:29 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TrashCan {
|
2021-10-13 23:11:45 +08:00
|
|
|
|
pub fn new(database: Arc<dyn WorkspaceDatabase>) -> Self {
|
|
|
|
|
let (tx, _) = broadcast::channel(10);
|
|
|
|
|
|
|
|
|
|
Self { database, notify: tx }
|
|
|
|
|
}
|
|
|
|
|
pub fn read_trash(&self) -> Result<RepeatedTrash, WorkspaceError> {
|
|
|
|
|
let conn = self.database.db_connection()?;
|
|
|
|
|
let repeated_trash = TrashTableSql::read_all(&*conn)?;
|
|
|
|
|
Ok(repeated_trash)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tracing::instrument(level = "debug", skip(self), fields(putback) err)]
|
2021-10-15 13:10:52 +08:00
|
|
|
|
pub async fn putback(&self, trash_id: &str) -> WorkspaceResult<()> {
|
|
|
|
|
let (tx, mut rx) = mpsc::channel::<WorkspaceResult<()>>(1);
|
|
|
|
|
let trash_table = TrashTableSql::read(trash_id, &*self.database.db_connection()?)?;
|
|
|
|
|
tracing::Span::current().record(
|
|
|
|
|
"putback",
|
2021-10-15 15:52:08 +08:00
|
|
|
|
&format!("{:?}: {}", &trash_table.ty, trash_table.id).as_str(),
|
2021-10-15 13:10:52 +08:00
|
|
|
|
);
|
2021-10-15 15:52:08 +08:00
|
|
|
|
let _ = self
|
|
|
|
|
.notify
|
2021-10-15 17:09:50 +08:00
|
|
|
|
.send(TrashEvent::Putback(trash_table.ty.into(), vec![trash_table.id], tx));
|
2021-10-15 13:10:52 +08:00
|
|
|
|
|
|
|
|
|
let _ = rx.recv().await.unwrap()?;
|
2021-10-13 23:11:45 +08:00
|
|
|
|
let conn = self.database.db_connection()?;
|
2021-10-14 14:34:22 +08:00
|
|
|
|
let _ = conn.immediate_transaction::<_, WorkspaceError, _>(|| {
|
|
|
|
|
let _ = TrashTableSql::delete_trash(trash_id, &*conn)?;
|
|
|
|
|
let _ = self.notify_dart_trash_did_update(&conn)?;
|
|
|
|
|
Ok(())
|
|
|
|
|
})?;
|
2021-10-13 23:11:45 +08:00
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tracing::instrument(level = "debug", skip(self) err)]
|
2021-10-15 13:10:52 +08:00
|
|
|
|
pub fn restore_all(&self) -> WorkspaceResult<()> { Ok(()) }
|
|
|
|
|
|
|
|
|
|
#[tracing::instrument(level = "debug", skip(self) err)]
|
|
|
|
|
pub fn delete_all(&self) -> WorkspaceResult<()> { Ok(()) }
|
|
|
|
|
|
|
|
|
|
#[tracing::instrument(level = "debug", skip(self) err)]
|
|
|
|
|
pub async fn delete(&self, trash_id: &str) -> WorkspaceResult<()> {
|
|
|
|
|
let (tx, mut rx) = mpsc::channel::<WorkspaceResult<()>>(1);
|
|
|
|
|
let trash_table = TrashTableSql::read(trash_id, &*self.database.db_connection()?)?;
|
|
|
|
|
let _ = self
|
|
|
|
|
.notify
|
2021-10-15 15:52:08 +08:00
|
|
|
|
.send(TrashEvent::Delete(trash_table.ty.into(), vec![trash_table.id], tx));
|
2021-10-15 13:10:52 +08:00
|
|
|
|
|
|
|
|
|
let _ = rx.recv().await.unwrap()?;
|
|
|
|
|
let _ = TrashTableSql::delete_trash(trash_id, &*self.database.db_connection()?)?;
|
2021-10-13 23:11:45 +08:00
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-15 13:10:52 +08:00
|
|
|
|
// [[ transaction ]]
|
|
|
|
|
// https://www.tutlane.com/tutorial/sqlite/sqlite-transactions-begin-commit-rollback
|
|
|
|
|
// We can use these commands only when we are performing INSERT, UPDATE, and
|
|
|
|
|
// DELETE operations. It’s not possible for us to use these commands to
|
|
|
|
|
// CREATE and DROP tables operations because those are auto-commit in the
|
|
|
|
|
// database.
|
2021-10-16 16:45:52 +08:00
|
|
|
|
#[tracing::instrument(level = "debug", skip(self, trash), err)]
|
|
|
|
|
pub async fn add<T: Into<Trash>>(&self, trash: Vec<T>) -> Result<(), WorkspaceError> {
|
|
|
|
|
let (tx, mut rx) = mpsc::channel::<WorkspaceResult<()>>(1);
|
|
|
|
|
let trash = trash.into_iter().map(|t| t.into()).collect::<Vec<Trash>>();
|
|
|
|
|
let mut ids = vec![];
|
|
|
|
|
let mut trash_type = None;
|
|
|
|
|
let _ = thread::scope(|_s| {
|
|
|
|
|
let conn = self.database.db_connection()?;
|
|
|
|
|
conn.immediate_transaction::<_, WorkspaceError, _>(|| {
|
|
|
|
|
for t in trash {
|
|
|
|
|
if trash_type == None {
|
|
|
|
|
trash_type = Some(t.ty.clone());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if trash_type.as_ref().unwrap() != &t.ty {
|
|
|
|
|
return Err(WorkspaceError::internal());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ids.push(t.id.clone());
|
|
|
|
|
let _ = TrashTableSql::create_trash(t.into(), &*conn)?;
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
})?;
|
|
|
|
|
Ok::<(), WorkspaceError>(())
|
|
|
|
|
})
|
|
|
|
|
.unwrap()?;
|
|
|
|
|
|
|
|
|
|
if let Some(trash_type) = trash_type {
|
|
|
|
|
let _ = self.notify.send(TrashEvent::NewTrash(trash_type, ids, tx));
|
|
|
|
|
let _ = rx.recv().await.unwrap()?;
|
|
|
|
|
}
|
2021-10-14 14:34:22 +08:00
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-15 13:10:52 +08:00
|
|
|
|
pub fn subscribe(&self) -> broadcast::Receiver<TrashEvent> { self.notify.subscribe() }
|
|
|
|
|
|
2021-10-14 14:34:22 +08:00
|
|
|
|
fn notify_dart_trash_did_update(&self, conn: &SqliteConnection) -> WorkspaceResult<()> {
|
|
|
|
|
// Opti: only push the changeset
|
|
|
|
|
let repeated_trash = TrashTableSql::read_all(conn)?;
|
|
|
|
|
send_anonymous_dart_notification(WorkspaceNotification::TrashUpdated)
|
|
|
|
|
.payload(repeated_trash)
|
|
|
|
|
.send();
|
|
|
|
|
|
2021-10-13 23:11:45 +08:00
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2021-10-13 11:10:29 +08:00
|
|
|
|
}
|