mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-23 09:33:01 +03:00
allow to delete bookmarks
This commit is contained in:
parent
fd00c76f6f
commit
9fe324996d
@ -302,26 +302,26 @@ impl App {
|
||||
self.files_database.list_by_project_id_session_id(project_id, session_id, paths)
|
||||
}
|
||||
|
||||
pub fn create_bookmark(&self, project_id: &str, timestamp_ms: &u128, note: &str) -> Result<bookmarks::Bookmark> {
|
||||
pub fn upsert_bookmark(&self, bookmark: &bookmarks::Bookmark) -> Result<()> {
|
||||
let gb_repository = gb_repository::Repository::open(
|
||||
self.local_data_dir.clone(),
|
||||
project_id.to_string(),
|
||||
bookmark.project_id.to_string(),
|
||||
self.projects_storage.clone(),
|
||||
self.users_storage.clone(),
|
||||
)
|
||||
.context("failed to open repository")?;
|
||||
|
||||
let bookmark = bookmarks::Bookmark{
|
||||
id: uuid::Uuid::new_v4().to_string(),
|
||||
project_id: project_id.to_string(),
|
||||
timestamp_ms: *timestamp_ms,
|
||||
note: note.to_string(),
|
||||
};
|
||||
|
||||
let session = gb_repository.get_or_create_current_session().context("failed to get or create current session")?;
|
||||
let writer = sessions::Writer::open(&gb_repository, &session).context("failed to open session writer")?;
|
||||
writer.write_bookmark(&bookmark).context("failed to write bookmark")?;
|
||||
Ok(bookmark)
|
||||
|
||||
self.proxy_watchers.lock().unwrap().get(&bookmark.project_id).map(|proxy_watcher| {
|
||||
if let Err(e) = proxy_watcher.send(watcher::Event::Bookmark(bookmark.clone())) {
|
||||
log::error!("failed to send bookmark event to proxy: {}", e);
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn list_bookmarks(&self, project_id: &str, range: Option<ops::Range<u128>>) -> Result<Vec<bookmarks::Bookmark>> {
|
||||
@ -337,6 +337,7 @@ impl App {
|
||||
self.deltas_database.list_by_project_id_session_id(project_id, session_id, paths)
|
||||
}
|
||||
|
||||
|
||||
pub fn git_activity(
|
||||
&self,
|
||||
project_id: &str,
|
||||
|
@ -16,15 +16,32 @@ impl Database {
|
||||
Self { database }
|
||||
}
|
||||
|
||||
pub fn insert(&self, bookmark: &Bookmark) -> Result<()> {
|
||||
pub fn get_by_id(&self, id: &str) -> Result<Option<Bookmark>> {
|
||||
self.database.transaction(|tx| {
|
||||
let mut stmt = get_by_id_stmt(tx).context("Failed to prepare get_by_id statement")?;
|
||||
let mut rows = stmt
|
||||
.query(rusqlite::named_params! { ":id": id })
|
||||
.context("Failed to execute get_by_id statement")?;
|
||||
if let Some(row) = rows.next()? {
|
||||
Ok(Some(parse_row(row)?))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn upsert(&self, bookmark: &Bookmark) -> Result<()> {
|
||||
self.database.transaction(|tx| -> Result<()> {
|
||||
let mut stmt = insert_stmt(tx).context("Failed to prepare insert statement")?;
|
||||
let timestamp_ms = bookmark.timestamp_ms.to_string();
|
||||
let created_timestamp_ms = bookmark.created_timestamp_ms.to_string();
|
||||
let updated_timestamp_ms = bookmark.updated_timestamp_ms.to_string();
|
||||
stmt.execute(rusqlite::named_params! {
|
||||
":id": &bookmark.id,
|
||||
":project_id": &bookmark.project_id,
|
||||
":timestamp_ms": ×tamp_ms,
|
||||
":created_timestamp_ms": &created_timestamp_ms,
|
||||
":updated_timestamp_ms": &updated_timestamp_ms,
|
||||
":note": &bookmark.note,
|
||||
":deleted": &bookmark.deleted,
|
||||
})
|
||||
.context("Failed to execute insert statement")?;
|
||||
Ok(())
|
||||
@ -87,8 +104,24 @@ fn insert_stmt<'conn>(
|
||||
) -> Result<rusqlite::CachedStatement<'conn>> {
|
||||
Ok(tx.prepare_cached(
|
||||
"
|
||||
INSERT INTO `bookmarks` (`id`, `project_id`, `timestamp_ms`, `note`)
|
||||
VALUES (:id, :project_id, :timestamp_ms, :note)
|
||||
INSERT INTO `bookmarks` (`id`, `project_id`, `created_timestamp_ms`, `updated_timestamp_ms`, `note`, `deleted`)
|
||||
VALUES (:id, :project_id, :created_timestamp_ms, :updated_timestamp_ms, :note, :deleted)
|
||||
ON CONFLICT(`id`) DO UPDATE SET
|
||||
`updated_timestamp_ms` = :updated_timestamp_ms,
|
||||
`note` = :note,
|
||||
`deleted` = :deleted
|
||||
",
|
||||
)?)
|
||||
}
|
||||
|
||||
fn get_by_id_stmt<'conn>(
|
||||
tx: &'conn rusqlite::Transaction,
|
||||
) -> Result<rusqlite::CachedStatement<'conn>> {
|
||||
Ok(tx.prepare_cached(
|
||||
"
|
||||
SELECT `id`, `project_id`, `created_timestamp_ms`, `updated_timestamp_ms`, `note`, `deleted`
|
||||
FROM `bookmarks`
|
||||
WHERE `id` = :id
|
||||
",
|
||||
)?)
|
||||
}
|
||||
@ -98,12 +131,12 @@ fn list_by_project_id_range_stmt<'conn>(
|
||||
) -> Result<rusqlite::CachedStatement<'conn>> {
|
||||
Ok(tx.prepare_cached(
|
||||
"
|
||||
SELECT `id`, `project_id`, `timestamp_ms`, `note`
|
||||
SELECT `id`, `project_id`, `created_timestamp_ms`, `updated_timestamp_ms`, `note`, `deleted`
|
||||
FROM `bookmarks`
|
||||
WHERE `project_id` = :project_id
|
||||
AND `timestamp_ms` >= :start
|
||||
AND `timestamp_ms` < :end
|
||||
ORDER BY `timestamp_ms` DESC
|
||||
AND `updated_timestamp_ms` >= :start
|
||||
AND `updated_timestamp_ms` < :end
|
||||
ORDER BY `created_timestamp_ms` DESC
|
||||
",
|
||||
)?)
|
||||
}
|
||||
@ -113,10 +146,10 @@ fn list_by_project_id_stmt<'conn>(
|
||||
) -> Result<rusqlite::CachedStatement<'conn>> {
|
||||
Ok(tx.prepare_cached(
|
||||
"
|
||||
SELECT `id`, `project_id`, `timestamp_ms`, `note`
|
||||
SELECT `id`, `project_id`, `created_timestamp_ms`, `updated_timestamp_ms`, `note`, `deleted`
|
||||
FROM `bookmarks`
|
||||
WHERE `project_id` = :project_id
|
||||
ORDER BY `timestamp_ms` DESC
|
||||
ORDER BY `created_timestamp_ms` DESC
|
||||
",
|
||||
)?)
|
||||
}
|
||||
@ -125,12 +158,18 @@ fn parse_row(row: &rusqlite::Row) -> Result<Bookmark> {
|
||||
Ok(Bookmark {
|
||||
id: row.get(0).context("Failed to get id")?,
|
||||
project_id: row.get(1).context("Failed to get project_id")?,
|
||||
timestamp_ms: row
|
||||
created_timestamp_ms: row
|
||||
.get::<usize, String>(2)
|
||||
.context("Failed to get timestamp_ms")?
|
||||
.parse()
|
||||
.context("Failed to parse timestamp_ms")?,
|
||||
note: row.get(3).context("Failed to get note")?,
|
||||
.context("Failed to get created_timestamp_ms")?
|
||||
.parse::<u128>()
|
||||
.context("Failed to parse created_timestamp_ms")?,
|
||||
updated_timestamp_ms: row
|
||||
.get::<usize, String>(3)
|
||||
.context("Failed to get updated_timestamp_ms")?
|
||||
.parse::<u128>()
|
||||
.context("Failed to parse updated_timestamp_ms")?,
|
||||
note: row.get(4).context("Failed to get note")?,
|
||||
deleted: row.get(5).context("Failed to get deleted")?,
|
||||
})
|
||||
}
|
||||
|
||||
@ -146,11 +185,13 @@ mod tests {
|
||||
let bookmark = Bookmark {
|
||||
id: "id".to_string(),
|
||||
project_id: "project_id".to_string(),
|
||||
timestamp_ms: 123,
|
||||
created_timestamp_ms: 123,
|
||||
updated_timestamp_ms: 123,
|
||||
note: "note".to_string(),
|
||||
deleted: false,
|
||||
};
|
||||
|
||||
database.insert(&bookmark)?;
|
||||
database.upsert(&bookmark)?;
|
||||
|
||||
let result = database.list_by_project_id_all(&bookmark.project_id)?;
|
||||
|
||||
@ -167,18 +208,22 @@ mod tests {
|
||||
let bookmark_one = Bookmark {
|
||||
id: "id".to_string(),
|
||||
project_id: "project_id".to_string(),
|
||||
timestamp_ms: 123,
|
||||
created_timestamp_ms: 123,
|
||||
updated_timestamp_ms: 123,
|
||||
note: "note".to_string(),
|
||||
deleted: false,
|
||||
};
|
||||
database.insert(&bookmark_one)?;
|
||||
database.upsert(&bookmark_one)?;
|
||||
|
||||
let bookmark_two = Bookmark {
|
||||
id: "id2".to_string(),
|
||||
project_id: "project_id".to_string(),
|
||||
timestamp_ms: 456,
|
||||
created_timestamp_ms: 456,
|
||||
updated_timestamp_ms: 456,
|
||||
note: "note".to_string(),
|
||||
deleted: false,
|
||||
};
|
||||
database.insert(&bookmark_two)?;
|
||||
database.upsert(&bookmark_two)?;
|
||||
|
||||
let result = database.list_by_project_id_range(
|
||||
&bookmark_one.project_id,
|
||||
@ -188,4 +233,34 @@ mod tests {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn update() -> Result<()> {
|
||||
let db = database::Database::memory()?;
|
||||
let database = Database::new(db);
|
||||
|
||||
assert_eq!(database.get_by_id("id")?, None);
|
||||
|
||||
let bookmark = Bookmark {
|
||||
id: "id".to_string(),
|
||||
project_id: "project_id".to_string(),
|
||||
created_timestamp_ms: 123,
|
||||
updated_timestamp_ms: 123,
|
||||
note: "note".to_string(),
|
||||
deleted: false,
|
||||
};
|
||||
|
||||
database.upsert(&bookmark)?;
|
||||
assert_eq!(database.get_by_id(&bookmark.id)?, Some(bookmark.clone()));
|
||||
|
||||
let updated = Bookmark {
|
||||
note: "updated".to_string(),
|
||||
updated_timestamp_ms: 456,
|
||||
..bookmark.clone()
|
||||
};
|
||||
database.upsert(&updated)?;
|
||||
assert_eq!(database.get_by_id(&bookmark.id.clone())?, Some(updated));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -3,13 +3,27 @@ mod reader;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Bookmark {
|
||||
pub id: String,
|
||||
pub project_id: String,
|
||||
pub timestamp_ms: u128,
|
||||
pub created_timestamp_ms: u128,
|
||||
pub updated_timestamp_ms: u128,
|
||||
pub note: String,
|
||||
pub deleted: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum Event {
|
||||
Created(Bookmark),
|
||||
Updated {
|
||||
id: String,
|
||||
note: Option<String>,
|
||||
deleted: Option<bool>,
|
||||
timestamp_ms: u128,
|
||||
},
|
||||
}
|
||||
|
||||
pub use database::Database;
|
||||
|
16
src-tauri/src/database/migrations/V4__bookmarks_update.sql
Normal file
16
src-tauri/src/database/migrations/V4__bookmarks_update.sql
Normal file
@ -0,0 +1,16 @@
|
||||
ALTER TABLE `bookmarks`
|
||||
ADD `created_timestamp_ms` text NOT NULL DEFAULT 0;
|
||||
|
||||
UPDATE
|
||||
`bookmarks`
|
||||
SET
|
||||
`created_timestamp_ms` = `timestamp_ms`;
|
||||
|
||||
ALTER TABLE `bookmarks`
|
||||
DROP COLUMN `timestamp_ms`;
|
||||
|
||||
ALTER TABLE `bookmarks`
|
||||
ADD `updated_timestamp_ms` text;
|
||||
|
||||
ALTER TABLE `bookmarks`
|
||||
ADD `deleted` boolean NOT NULL DEFAULT FALSE;
|
@ -486,10 +486,10 @@ async fn delete_all_data(handle: tauri::AppHandle) -> Result<(), Error> {
|
||||
|
||||
#[timed(duration(printer = "debug!"))]
|
||||
#[tauri::command(async)]
|
||||
async fn create_bookmark(handle: tauri::AppHandle, project_id: &str, timestamp_ms: u128, note: &str) -> Result<bookmarks::Bookmark, Error> {
|
||||
async fn upsert_bookmark(handle: tauri::AppHandle, bookmark: bookmarks::Bookmark) -> Result<(), Error> {
|
||||
let app = handle.state::<app::App>();
|
||||
let bookmark = app.create_bookmark(project_id, ×tamp_ms, note).context("failed to create bookmark")?;
|
||||
Ok(bookmark)
|
||||
app.upsert_bookmark(&bookmark).context("failed to upsert bookmark")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[timed(duration(printer = "debug!"))]
|
||||
@ -638,7 +638,7 @@ fn main() {
|
||||
get_logs_archive_path,
|
||||
get_project_archive_path,
|
||||
get_project_data_archive_path,
|
||||
create_bookmark,
|
||||
upsert_bookmark,
|
||||
list_bookmarks,
|
||||
])
|
||||
.build(tauri_context)
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::{path, time};
|
||||
|
||||
use crate::{deltas, sessions};
|
||||
use crate::{bookmarks, deltas, sessions};
|
||||
|
||||
pub enum Event {
|
||||
Tick(time::SystemTime),
|
||||
@ -17,6 +17,7 @@ pub enum Event {
|
||||
ProjectFileChange(path::PathBuf),
|
||||
|
||||
Session(sessions::Session),
|
||||
Bookmark(bookmarks::Bookmark),
|
||||
File((String, path::PathBuf, String)),
|
||||
Deltas((String, path::PathBuf, Vec<deltas::Delta>)),
|
||||
}
|
||||
|
@ -60,7 +60,14 @@ impl<'handler> Handler<'handler> {
|
||||
}
|
||||
|
||||
pub fn index_bookmark(&self, bookmark: &bookmarks::Bookmark) -> Result<Vec<events::Event>> {
|
||||
self.bookmarks_database.insert(&bookmark)?;
|
||||
match self.bookmarks_database.get_by_id(&bookmark.id)? {
|
||||
Some(existing) => {
|
||||
if existing.updated_timestamp_ms < bookmark.updated_timestamp_ms {
|
||||
self.bookmarks_database.upsert(&bookmark)?;
|
||||
}
|
||||
}
|
||||
None => self.bookmarks_database.upsert(&bookmark)?,
|
||||
}
|
||||
Ok(vec![])
|
||||
}
|
||||
|
||||
|
@ -139,7 +139,8 @@ impl<'handler> Handler<'handler> {
|
||||
events::Event::Fetch => self.fetch_project_handler.handle(),
|
||||
|
||||
events::Event::File((session_id, file_path, contents)) => {
|
||||
self.index_handler
|
||||
let file_events = self
|
||||
.index_handler
|
||||
.index_file(&session_id, file_path.to_str().unwrap(), &contents)
|
||||
.context("failed to index file")?;
|
||||
self.events_sender
|
||||
@ -150,19 +151,21 @@ impl<'handler> Handler<'handler> {
|
||||
&contents,
|
||||
))
|
||||
.context("failed to send file event")?;
|
||||
Ok(vec![])
|
||||
Ok(file_events)
|
||||
}
|
||||
events::Event::Session(session) => {
|
||||
self.index_handler
|
||||
let session_events = self
|
||||
.index_handler
|
||||
.index_session(&session)
|
||||
.context("failed to index session")?;
|
||||
self.events_sender
|
||||
.send(app_events::Event::session(&self.project_id, &session))
|
||||
.context("failed to send session event")?;
|
||||
Ok(vec![])
|
||||
Ok(session_events)
|
||||
}
|
||||
events::Event::Deltas((session_id, path, deltas)) => {
|
||||
self.index_handler
|
||||
let delta_events = self
|
||||
.index_handler
|
||||
.index_deltas(&session_id, path.to_str().unwrap(), &deltas)
|
||||
.context("failed to index deltas")?;
|
||||
self.events_sender
|
||||
@ -173,8 +176,12 @@ impl<'handler> Handler<'handler> {
|
||||
&path,
|
||||
))
|
||||
.context("failed to send deltas event")?;
|
||||
Ok(vec![])
|
||||
Ok(delta_events)
|
||||
}
|
||||
events::Event::Bookmark(bookmark) => self
|
||||
.index_handler
|
||||
.index_bookmark(&bookmark)
|
||||
.context("failed to index bookmark"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
12
src/lib/api/ipc/bookmarks.ts
Normal file
12
src/lib/api/ipc/bookmarks.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { invoke } from '$lib/ipc';
|
||||
|
||||
export type Bookmark = {
|
||||
id: string;
|
||||
projectId: string;
|
||||
createdTimestampMs: number;
|
||||
updatedTimestampMs: number;
|
||||
note: string;
|
||||
deleted: boolean;
|
||||
};
|
||||
|
||||
export const upsert = (params: { bookmark: Bookmark }) => invoke<void>('upsert_bookmark', params);
|
@ -11,6 +11,7 @@ export * as searchResults from './search';
|
||||
export { type SearchResult } from './search';
|
||||
export * as files from './files';
|
||||
export * as zip from './zip';
|
||||
export * as bookmarks from './bookmarks';
|
||||
|
||||
import { invoke } from '$lib/ipc';
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user