From 7fcf709efea7d37cf903cb3ba7313ab03f9d853d Mon Sep 17 00:00:00 2001 From: Gregory Schier Date: Thu, 30 Mar 2023 08:11:51 -0700 Subject: [PATCH] Remove updated_by, remember last location --- .../migrations/20230329000257_updated-by.sql | 7 - .../20230330143214_request-auth.sql | 2 + src-tauri/sqlx-data.json | 398 ++++++++---------- src-tauri/src/main.rs | 81 ++-- src-tauri/src/models.rs | 58 +-- src-web/components/App.tsx | 4 +- src-web/components/AppRouter.tsx | 21 +- src-web/components/Workspaces.tsx | 24 +- src-web/hooks/useKeyValue.ts | 10 +- src-web/hooks/useTauriListeners.ts | 2 +- src-web/lib/keyValueStore.ts | 3 +- src-web/lib/lastLocation.ts | 17 + src-web/main.tsx | 5 +- 13 files changed, 286 insertions(+), 346 deletions(-) delete mode 100644 src-tauri/migrations/20230329000257_updated-by.sql create mode 100644 src-tauri/migrations/20230330143214_request-auth.sql create mode 100644 src-web/lib/lastLocation.ts diff --git a/src-tauri/migrations/20230329000257_updated-by.sql b/src-tauri/migrations/20230329000257_updated-by.sql deleted file mode 100644 index e43403e5..00000000 --- a/src-tauri/migrations/20230329000257_updated-by.sql +++ /dev/null @@ -1,7 +0,0 @@ -ALTER TABLE http_requests ADD COLUMN updated_by TEXT NOT NULL DEFAULT ''; -ALTER TABLE http_responses ADD COLUMN updated_by TEXT NOT NULL DEFAULT ''; -ALTER TABLE workspaces ADD COLUMN updated_by TEXT NOT NULL DEFAULT ''; -ALTER TABLE key_values ADD COLUMN updated_by TEXT NOT NULL DEFAULT ''; - -ALTER TABLE http_requests ADD COLUMN authentication TEXT NOT NULL DEFAULT '{}'; -ALTER TABLE http_requests ADD COLUMN authentication_type TEXT; diff --git a/src-tauri/migrations/20230330143214_request-auth.sql b/src-tauri/migrations/20230330143214_request-auth.sql new file mode 100644 index 00000000..6e792c61 --- /dev/null +++ b/src-tauri/migrations/20230330143214_request-auth.sql @@ -0,0 +1,2 @@ +ALTER TABLE http_requests ADD COLUMN authentication TEXT NOT NULL DEFAULT '{}'; +ALTER TABLE http_requests ADD COLUMN authentication_type TEXT; diff --git a/src-tauri/sqlx-data.json b/src-tauri/sqlx-data.json index 929a3d70..bae3746b 100644 --- a/src-tauri/sqlx-data.json +++ b/src-tauri/sqlx-data.json @@ -1,5 +1,53 @@ { "db": "SQLite", + "06aaf8f4a17566f1d25da2a60f0baf4b5fc28c3cf0c001a84e25edf9eab3c7e3": { + "describe": { + "columns": [ + { + "name": "model", + "ordinal": 0, + "type_info": "Text" + }, + { + "name": "created_at", + "ordinal": 1, + "type_info": "Datetime" + }, + { + "name": "updated_at", + "ordinal": 2, + "type_info": "Datetime" + }, + { + "name": "namespace", + "ordinal": 3, + "type_info": "Text" + }, + { + "name": "key", + "ordinal": 4, + "type_info": "Text" + }, + { + "name": "value", + "ordinal": 5, + "type_info": "Text" + } + ], + "nullable": [ + false, + false, + false, + false, + false, + false + ], + "parameters": { + "Right": 2 + } + }, + "query": "\n SELECT model, created_at, updated_at, namespace, key, value\n FROM key_values\n WHERE namespace = ? AND key = ?\n " + }, "07d1a1c7b4f3d9625a766e60fd57bb779b71dae30e5bbce34885a911a5a42428": { "describe": { "columns": [], @@ -20,69 +68,15 @@ }, "query": "\n DELETE FROM http_responses\n WHERE request_id = ?\n " }, - "19e0076c3cd13b73a46619b5c0ee5bf304fe27245db3d19a648f625bf5231cb0": { + "318ed5a1126fe00719393cf4e6c788ee5a265af88b7253f61a475f78c6774ef6": { "describe": { "columns": [], "nullable": [], "parameters": { - "Right": 4 + "Right": 9 } }, - "query": "\n INSERT INTO workspaces (id, updated_by, name, description)\n VALUES (?, ?, ?, ?)\n " - }, - "2f93d7bd211af59c7cc15765a746216632fbe4f02301acf8382f1cf3f8d24c8d": { - "describe": { - "columns": [ - { - "name": "id", - "ordinal": 0, - "type_info": "Text" - }, - { - "name": "model", - "ordinal": 1, - "type_info": "Text" - }, - { - "name": "created_at", - "ordinal": 2, - "type_info": "Datetime" - }, - { - "name": "updated_at", - "ordinal": 3, - "type_info": "Datetime" - }, - { - "name": "updated_by", - "ordinal": 4, - "type_info": "Text" - }, - { - "name": "name", - "ordinal": 5, - "type_info": "Text" - }, - { - "name": "description", - "ordinal": 6, - "type_info": "Text" - } - ], - "nullable": [ - false, - false, - false, - false, - false, - false, - false - ], - "parameters": { - "Right": 1 - } - }, - "query": "\n SELECT id, model, created_at, updated_at, updated_by, name, description\n FROM workspaces WHERE id = ?\n " + "query": "\n INSERT INTO http_responses (\n id,\n request_id,\n workspace_id,\n elapsed,\n url,\n status,\n status_reason,\n body,\n headers\n )\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);\n " }, "448a1d1f1866ab42c0f81fcf8eb2930bf21dfdd43ca4831bc1a198cf45ac3732": { "describe": { @@ -94,7 +88,7 @@ }, "query": "\n DELETE FROM http_requests\n WHERE id = ?\n " }, - "51ee4652a889417dce585e4da457a629dd9e064b8866c3a916bc3bd2c933e14f": { + "6f0cb5a6d1e8dbc8cdfcc3c7e7944b2c83c22cb795b9d6b98fe067dabec9680b": { "describe": { "columns": [ { @@ -113,9 +107,9 @@ "type_info": "Text" }, { - "name": "request_id", + "name": "created_at", "ordinal": 3, - "type_info": "Text" + "type_info": "Datetime" }, { "name": "updated_at", @@ -123,47 +117,47 @@ "type_info": "Datetime" }, { - "name": "updated_by", + "name": "name", "ordinal": 5, "type_info": "Text" }, { - "name": "created_at", + "name": "url", "ordinal": 6, - "type_info": "Datetime" + "type_info": "Text" }, { - "name": "status", + "name": "method", "ordinal": 7, - "type_info": "Int64" - }, - { - "name": "status_reason", - "ordinal": 8, "type_info": "Text" }, { "name": "body", + "ordinal": 8, + "type_info": "Text" + }, + { + "name": "body_type", "ordinal": 9, "type_info": "Text" }, { - "name": "elapsed", + "name": "authentication!: Json>", "ordinal": 10, - "type_info": "Int64" + "type_info": "Text" }, { - "name": "url", + "name": "authentication_type", "ordinal": 11, "type_info": "Text" }, { - "name": "error", + "name": "sort_priority", "ordinal": 12, - "type_info": "Text" + "type_info": "Float" }, { - "name": "headers!: sqlx::types::Json>", + "name": "headers!: sqlx::types::Json>", "ordinal": 13, "type_info": "Text" } @@ -178,17 +172,17 @@ false, false, true, - false, - false, + true, false, true, + false, false ], "parameters": { "Right": 1 } }, - "query": "\n SELECT id, model, workspace_id, request_id, updated_at, updated_by,\n created_at, status, status_reason, body, elapsed, url, error,\n headers AS \"headers!: sqlx::types::Json>\"\n FROM http_responses\n WHERE request_id = ?\n ORDER BY created_at ASC\n " + "query": "\n SELECT\n id,\n model,\n workspace_id,\n created_at,\n updated_at,\n name,\n url,\n method,\n body,\n body_type,\n authentication AS \"authentication!: Json>\",\n authentication_type,\n sort_priority,\n headers AS \"headers!: sqlx::types::Json>\"\n FROM http_requests\n WHERE workspace_id = ?\n " }, "84be2b954870ab181738656ecd4d03fca2ff21012947014c79626abfce8e999b": { "describe": { @@ -200,61 +194,27 @@ }, "query": "\n DELETE FROM workspaces\n WHERE id = ?\n " }, - "8d71216aa3902af45acc36bb4671e36bea6da2d30b42cfe8b70cff00cd00f256": { + "a83698dcf9a815b881097133edb31a34ba25e7c6c114d463c495342a85371639": { "describe": { - "columns": [ - { - "name": "model", - "ordinal": 0, - "type_info": "Text" - }, - { - "name": "created_at", - "ordinal": 1, - "type_info": "Datetime" - }, - { - "name": "updated_at", - "ordinal": 2, - "type_info": "Datetime" - }, - { - "name": "updated_by", - "ordinal": 3, - "type_info": "Text" - }, - { - "name": "namespace", - "ordinal": 4, - "type_info": "Text" - }, - { - "name": "key", - "ordinal": 5, - "type_info": "Text" - }, - { - "name": "value", - "ordinal": 6, - "type_info": "Text" - } - ], - "nullable": [ - false, - false, - false, - false, - false, - false, - false - ], + "columns": [], + "nullable": [], "parameters": { - "Right": 2 + "Right": 8 } }, - "query": "\n SELECT model, created_at, updated_at, updated_by, namespace, key, value\n FROM key_values\n WHERE namespace = ? AND key = ?\n " + "query": "\n UPDATE http_responses SET (elapsed, url, status, status_reason, body, error, headers, updated_at) =\n (?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) WHERE id = ?;\n " }, - "9f09f300e04d9b77d408bea52069b7c812dcad163d144df4b7b02a9ba7969345": { + "b19c275180909a39342b13c3cdcf993781636913ae590967f5508c46a56dc961": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Right": 11 + } + }, + "query": "\n INSERT INTO http_requests (\n id,\n workspace_id,\n name,\n url,\n method,\n body,\n body_type,\n authentication,\n authentication_type,\n headers,\n sort_priority\n )\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT (id) DO UPDATE SET\n updated_at = CURRENT_TIMESTAMP,\n name = excluded.name,\n method = excluded.method,\n headers = excluded.headers,\n body = excluded.body,\n body_type = excluded.body_type,\n authentication = excluded.authentication,\n authentication_type = excluded.authentication_type,\n url = excluded.url,\n sort_priority = excluded.sort_priority\n " + }, + "caf3f21bf291dfbd36446592066e96c1f83abe96f6ea9211a3e049eb9c58a8c8": { "describe": { "columns": [ { @@ -278,23 +238,65 @@ "type_info": "Datetime" }, { - "name": "updated_by", + "name": "name", "ordinal": 4, "type_info": "Text" }, - { - "name": "name", - "ordinal": 5, - "type_info": "Text" - }, { "name": "description", - "ordinal": 6, + "ordinal": 5, "type_info": "Text" } ], "nullable": [ false, + false, + false, + false, + false, + false + ], + "parameters": { + "Right": 1 + } + }, + "query": "\n SELECT id, model, created_at, updated_at, name, description\n FROM workspaces WHERE id = ?\n " + }, + "cea4cae52f16ec78aca9a47b17117422d4f165e5a3b308c70fd1a180382475ea": { + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Text" + }, + { + "name": "model", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "created_at", + "ordinal": 2, + "type_info": "Datetime" + }, + { + "name": "updated_at", + "ordinal": 3, + "type_info": "Datetime" + }, + { + "name": "name", + "ordinal": 4, + "type_info": "Text" + }, + { + "name": "description", + "ordinal": 5, + "type_info": "Text" + } + ], + "nullable": [ false, false, false, @@ -306,19 +308,9 @@ "Right": 0 } }, - "query": "\n SELECT id, model, created_at, updated_at, updated_by, name, description\n FROM workspaces\n " + "query": "\n SELECT id, model, created_at, updated_at, name, description\n FROM workspaces\n " }, - "afe2d3a2b2198582d2a4360a0947785d435528eb77f67c420e830a997b5ad101": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Right": 9 - } - }, - "query": "\n UPDATE http_responses SET (elapsed, url, status, status_reason, body, error, headers, updated_by, updated_at) =\n (?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) WHERE id = ?;\n " - }, - "bc1b3220c104567176ff741b5648a14054485603ebb04222a7b9f60fc54f0970": { + "ced098adb79c0ee64e223b6e02371ef253920a2c342275de0fa9c181529a4adc": { "describe": { "columns": [ { @@ -386,14 +378,9 @@ "ordinal": 12, "type_info": "Float" }, - { - "name": "updated_by", - "ordinal": 13, - "type_info": "Text" - }, { "name": "headers!: sqlx::types::Json>", - "ordinal": 14, + "ordinal": 13, "type_info": "Text" } ], @@ -408,9 +395,8 @@ false, true, true, - true, - true, false, + true, false, false ], @@ -418,29 +404,9 @@ "Right": 1 } }, - "query": "\n SELECT\n id,\n model,\n workspace_id,\n created_at,\n updated_at,\n name,\n url,\n method,\n body,\n body_type,\n authentication AS \"authentication!: Json>\",\n authentication_type,\n sort_priority,\n updated_by,\n headers AS \"headers!: sqlx::types::Json>\"\n FROM http_requests\n WHERE id = ?\n " + "query": "\n SELECT\n id,\n model,\n workspace_id,\n created_at,\n updated_at,\n name,\n url,\n method,\n body,\n body_type,\n authentication AS \"authentication!: Json>\",\n authentication_type,\n sort_priority,\n headers AS \"headers!: sqlx::types::Json>\"\n FROM http_requests\n WHERE id = ?\n " }, - "ce62a799babc731dc53e43144751006c3905367c47c511b8abee832c58f8111d": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Right": 12 - } - }, - "query": "\n INSERT INTO http_requests (\n id,\n workspace_id,\n name,\n url,\n method,\n body,\n body_type,\n authentication,\n authentication_type,\n headers,\n sort_priority,\n updated_by\n )\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT (id) DO UPDATE SET\n updated_at = CURRENT_TIMESTAMP,\n name = excluded.name,\n method = excluded.method,\n headers = excluded.headers,\n body = excluded.body,\n body_type = excluded.body_type,\n authentication = excluded.authentication,\n authentication_type = excluded.authentication_type,\n url = excluded.url,\n sort_priority = excluded.sort_priority,\n updated_by = excluded.updated_by\n " - }, - "d56b00aeaca0edd9a9fea4c7fdce0229a6d6500c8294854974dd4fc30af8bda8": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Right": 10 - } - }, - "query": "\n INSERT INTO http_responses (\n id,\n request_id,\n workspace_id,\n elapsed,\n url,\n status,\n status_reason,\n body,\n headers,\n updated_by\n )\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);\n " - }, - "d68f12980ff00de36f02a82a626f99e86abf5a26ebdf74c95832d3be396206da": { + "d5ad6d5f82fe837fa9215bd4619ec18a7c95b3088d4fbf9825f2d1d28069d1ce": { "describe": { "columns": [ { @@ -468,49 +434,44 @@ "ordinal": 4, "type_info": "Datetime" }, - { - "name": "updated_by", - "ordinal": 5, - "type_info": "Text" - }, { "name": "created_at", - "ordinal": 6, + "ordinal": 5, "type_info": "Datetime" }, { "name": "status", - "ordinal": 7, + "ordinal": 6, "type_info": "Int64" }, { "name": "status_reason", - "ordinal": 8, + "ordinal": 7, "type_info": "Text" }, { "name": "body", - "ordinal": 9, + "ordinal": 8, "type_info": "Text" }, { "name": "elapsed", - "ordinal": 10, + "ordinal": 9, "type_info": "Int64" }, { "name": "url", - "ordinal": 11, + "ordinal": 10, "type_info": "Text" }, { "name": "error", - "ordinal": 12, + "ordinal": 11, "type_info": "Text" }, { "name": "headers!: sqlx::types::Json>", - "ordinal": 13, + "ordinal": 12, "type_info": "Text" } ], @@ -522,7 +483,6 @@ false, false, false, - false, true, false, false, @@ -534,7 +494,7 @@ "Right": 1 } }, - "query": "\n SELECT id, model, workspace_id, request_id, updated_at, updated_by, created_at,\n status, status_reason, body, elapsed, url, error,\n headers AS \"headers!: sqlx::types::Json>\"\n FROM http_responses\n WHERE id = ?\n " + "query": "\n SELECT id, model, workspace_id, request_id, updated_at,\n created_at, status, status_reason, body, elapsed, url, error,\n headers AS \"headers!: sqlx::types::Json>\"\n FROM http_responses\n WHERE request_id = ?\n ORDER BY created_at ASC\n " }, "d80c09497771e3641022e73ec6c6a87e73a551f88a948a5445d754922b82b50b": { "describe": { @@ -546,7 +506,7 @@ }, "query": "\n INSERT INTO key_values (namespace, key, value)\n VALUES (?, ?, ?) ON CONFLICT DO UPDATE SET\n updated_at = CURRENT_TIMESTAMP,\n value = excluded.value\n " }, - "dc749b8ed41ac55776e4f9dae36a82b3b880eb781eaa6fb53382e6db10f5a741": { + "e3ade0a69348d512e47e964bded9d7d890b92fdc1e01c6c22fa5e91f943639f2": { "describe": { "columns": [ { @@ -565,9 +525,9 @@ "type_info": "Text" }, { - "name": "created_at", + "name": "request_id", "ordinal": 3, - "type_info": "Datetime" + "type_info": "Text" }, { "name": "updated_at", @@ -575,17 +535,17 @@ "type_info": "Datetime" }, { - "name": "name", + "name": "created_at", "ordinal": 5, - "type_info": "Text" + "type_info": "Datetime" }, { - "name": "url", + "name": "status", "ordinal": 6, - "type_info": "Text" + "type_info": "Int64" }, { - "name": "method", + "name": "status_reason", "ordinal": 7, "type_info": "Text" }, @@ -595,33 +555,23 @@ "type_info": "Text" }, { - "name": "body_type", + "name": "elapsed", "ordinal": 9, - "type_info": "Text" + "type_info": "Int64" }, { - "name": "authentication!: Json>", + "name": "url", "ordinal": 10, "type_info": "Text" }, { - "name": "authentication_type", + "name": "error", "ordinal": 11, "type_info": "Text" }, { - "name": "sort_priority", + "name": "headers!: sqlx::types::Json>", "ordinal": 12, - "type_info": "Float" - }, - { - "name": "updated_by", - "ordinal": 13, - "type_info": "Text" - }, - { - "name": "headers!: sqlx::types::Json>", - "ordinal": 14, "type_info": "Text" } ], @@ -633,19 +583,27 @@ false, false, false, - false, - true, - true, - true, true, false, false, + false, + true, false ], "parameters": { "Right": 1 } }, - "query": "\n SELECT\n id,\n model,\n workspace_id,\n created_at,\n updated_at,\n name,\n url,\n method,\n body,\n body_type,\n authentication AS \"authentication!: Json>\",\n authentication_type,\n sort_priority,\n updated_by,\n headers AS \"headers!: sqlx::types::Json>\"\n FROM http_requests\n WHERE workspace_id = ?\n " + "query": "\n SELECT id, model, workspace_id, request_id, updated_at, created_at,\n status, status_reason, body, elapsed, url, error,\n headers AS \"headers!: sqlx::types::Json>\"\n FROM http_responses\n WHERE id = ?\n " + }, + "f116d8cf9aad828135bb8c3a4c8b8e6b857ae13303989e9133a33b2d1cf20e96": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Right": 3 + } + }, + "query": "\n INSERT INTO workspaces (id, name, description)\n VALUES (?, ?, ?)\n " } } \ No newline at end of file diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 7b4ce383..2455c3cb 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -22,7 +22,7 @@ use sqlx::sqlite::SqlitePoolOptions; use sqlx::types::Json; use sqlx::{Pool, Sqlite}; use tauri::regex::Regex; -use tauri::{AppHandle, Menu, MenuItem, State, Submenu, TitleBarStyle, Window, Wry}; +use tauri::{AppHandle, Menu, MenuItem, RunEvent, State, Submenu, TitleBarStyle, Window, Wry}; use tauri::{CustomMenuItem, Manager, SystemTray, SystemTrayEvent, SystemTrayMenu, WindowEvent}; use tokio::sync::Mutex; @@ -213,7 +213,7 @@ async fn actually_send_ephemeral_request( response.url = v.url().to_string(); response.body = v.text().await.expect("Failed to get body"); response.elapsed = start.elapsed().as_millis() as i64; - response = models::update_response_if_id(response, window.label(), pool) + response = models::update_response_if_id(response, pool) .await .expect("Failed to update response"); emit_all_others(&window, "updated_response", &response); @@ -235,10 +235,9 @@ async fn send_request( .await .expect("Failed to get request"); - let response = - models::create_response(&req.id, 0, "", 0, None, "", vec![], window.label(), pool) - .await - .expect("Failed to create response"); + let response = models::create_response(&req.id, 0, "", 0, None, "", vec![], pool) + .await + .expect("Failed to create response"); emit_all_others(&window, "updated_response", &response); actually_send_ephemeral_request(req, response, window, pool).await?; @@ -252,7 +251,7 @@ async fn response_err( pool: &Pool, ) -> Result { response.error = Some(error.clone()); - response = models::update_response_if_id(response, window.label(), pool) + response = models::update_response_if_id(response, pool) .await .expect("Failed to update response"); emit_all_others(&window, "updated_response", &response); @@ -295,7 +294,7 @@ async fn create_workspace( db_instance: State<'_, Mutex>>, ) -> Result { let pool = &*db_instance.lock().await; - let created_workspace = models::create_workspace(name, "", window.label(), pool) + let created_workspace = models::create_workspace(name, "", pool) .await .expect("Failed to create workspace"); @@ -326,7 +325,6 @@ async fn create_request( "", headers, sort_priority, - window.label(), pool, ) .await @@ -344,7 +342,7 @@ async fn duplicate_request( db_instance: State<'_, Mutex>>, ) -> Result { let pool = &*db_instance.lock().await; - let request = models::duplicate_request(id, window.label(), pool) + let request = models::duplicate_request(id, pool) .await .expect("Failed to duplicate request"); emit_all_others(&window, "updated_request", &request); @@ -382,7 +380,6 @@ async fn update_request( request.url.as_str(), request.headers.0, request.sort_priority, - window.label(), pool, ) .await @@ -468,21 +465,16 @@ async fn delete_all_responses( #[tauri::command] async fn workspaces( db_instance: State<'_, Mutex>>, - window: Window, ) -> Result, String> { let pool = &*db_instance.lock().await; let workspaces = models::find_workspaces(pool) .await .expect("Failed to find workspaces"); if workspaces.is_empty() { - let workspace = models::create_workspace( - "My Project", - "This is the default workspace", - window.label(), - pool, - ) - .await - .expect("Failed to create workspace"); + let workspace = + models::create_workspace("My Project", "This is the default workspace", pool) + .await + .expect("Failed to create workspace"); Ok(vec![workspace]) } else { Ok(workspaces) @@ -516,7 +508,6 @@ fn main() { tauri::Builder::default() .system_tray(system_tray) .setup(|app| { - let handle = app.handle(); let dir = match is_dev() { true => current_dir().unwrap(), false => app.path_resolver().app_data_dir().unwrap(), @@ -533,13 +524,6 @@ fn main() { .await .expect("Failed to connect to database"); - // Create the initial window - let app_id = get_or_create_client_id(&pool).await; - let win = create_window(handle, app_id); - if let Err(e) = win.show() { - println!("Failed to show window {}", e) - } - // Setup the DB handle let m = Mutex::new(pool); migrate_db(app.handle(), &m) @@ -583,8 +567,17 @@ fn main() { delete_response, delete_all_responses, ]) - .run(tauri::generate_context!()) - .expect("error while running tauri application"); + .build(tauri::generate_context!()) + .expect("error while running tauri application") + .run(|app_handle, event| match event { + RunEvent::Ready => { + create_window(app_handle); + } + + // ExitRequested { api, .. } => { + // } + _ => {} + }); } fn is_dev() -> bool { @@ -592,7 +585,7 @@ fn is_dev() -> bool { env.unwrap_or("production") != "production" } -fn create_window(handle: AppHandle, app_id: String) -> Window { +fn create_window(handle: &AppHandle) -> Window { let default_menu = Menu::os_default("Yaak".to_string().as_str()); let mut test_menu = Menu::new() .add_item( @@ -630,9 +623,9 @@ fn create_window(handle: AppHandle, app_id: String) -> Window { let submenu = Submenu::new("Test Menu", test_menu); let window_num = handle.windows().len(); - let window_id = format!("{}_{}", app_id, window_num); + let window_id = format!("wnd_{}", window_num); let menu = default_menu.add_submenu(submenu); - let win = tauri::WindowBuilder::new(&handle, window_id, tauri::WindowUrl::App("".into())) + let win = tauri::WindowBuilder::new(handle, window_id, tauri::WindowUrl::App("".into())) .menu(menu) .fullscreen(false) .resizable(true) @@ -644,6 +637,7 @@ fn create_window(handle: AppHandle, app_id: String) -> Window { .expect("failed to build window"); let win2 = win.clone(); + let handle2 = handle.clone(); win.on_menu_event(move |event| match event.menu_item_id() { "quit" => std::process::exit(0), "close" => win2.close().unwrap(), @@ -653,9 +647,7 @@ fn create_window(handle: AppHandle, app_id: String) -> Window { "toggle_sidebar" => win2.emit("toggle_sidebar", true).unwrap(), "refresh" => win2.emit("refresh", true).unwrap(), "send_request" => win2.emit("send_request", true).unwrap(), - "new_window" => { - create_window(handle.clone(), app_id.clone()); - } + "new_window" => _ = create_window(&handle2), "toggle_devtools" => { if win2.is_devtools_open() { win2.close_devtools(); @@ -676,6 +668,10 @@ fn create_window(handle: AppHandle, app_id: String) -> Window { match e { WindowEvent::Resized(..) => apply_offset(), WindowEvent::ThemeChanged(..) => apply_offset(), + WindowEvent::CloseRequested { .. } => { + println!("CLOSE REQUESTED"); + // api.prevent_close(); + } _ => {} } }); @@ -686,19 +682,6 @@ fn create_window(handle: AppHandle, app_id: String) -> Window { win } -async fn get_or_create_client_id(pool: &Pool) -> String { - match models::get_key_value("global", "client_id", pool).await { - Some(kv) => kv.value, - None => { - let id = &models::generate_id("yaak"); - models::set_key_value("global", "client_id", id, pool) - .await - .expect("Failed to set client id") - .value - } - } -} - /// Emit an event to all windows except the current one fn emit_all_others(current_window: &Window, event: &str, payload: S) { let windows = current_window.app_handle().windows(); diff --git a/src-tauri/src/models.rs b/src-tauri/src/models.rs index 37e71c3c..8f35e207 100644 --- a/src-tauri/src/models.rs +++ b/src-tauri/src/models.rs @@ -1,9 +1,10 @@ +use std::collections::HashMap; + use rand::distributions::{Alphanumeric, DistString}; use serde::{Deserialize, Serialize}; use sqlx::types::chrono::NaiveDateTime; use sqlx::types::{Json, JsonValue}; use sqlx::{Pool, Sqlite}; -use std::collections::HashMap; #[derive(sqlx::FromRow, Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -12,7 +13,6 @@ pub struct Workspace { pub model: String, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, - pub updated_by: String, pub name: String, pub description: String, } @@ -33,7 +33,6 @@ pub struct HttpRequest { pub model: String, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, - pub updated_by: String, pub sort_priority: f64, pub workspace_id: String, pub name: String, @@ -62,7 +61,6 @@ pub struct HttpResponse { pub request_id: String, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, - pub updated_by: String, pub error: Option, pub url: String, pub elapsed: i64, @@ -78,7 +76,6 @@ pub struct KeyValue { pub model: String, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, - pub updated_by: String, pub namespace: String, pub key: String, pub value: String, @@ -112,7 +109,7 @@ pub async fn get_key_value(namespace: &str, key: &str, pool: &Pool) -> O sqlx::query_as!( KeyValue, r#" - SELECT model, created_at, updated_at, updated_by, namespace, key, value + SELECT model, created_at, updated_at, namespace, key, value FROM key_values WHERE namespace = ? AND key = ? "#, @@ -128,7 +125,7 @@ pub async fn find_workspaces(pool: &Pool) -> Result, sqlx sqlx::query_as!( Workspace, r#" - SELECT id, model, created_at, updated_at, updated_by, name, description + SELECT id, model, created_at, updated_at, name, description FROM workspaces "#, ) @@ -140,7 +137,7 @@ pub async fn get_workspace(id: &str, pool: &Pool) -> Result) -> Result, ) -> Result { let id = generate_id("wk"); sqlx::query!( r#" - INSERT INTO workspaces (id, updated_by, name, description) - VALUES (?, ?, ?, ?) + INSERT INTO workspaces (id, name, description) + VALUES (?, ?, ?) "#, id, name, description, - updated_by, ) .execute(pool) .await @@ -189,11 +184,7 @@ pub async fn create_workspace( get_workspace(&id, pool).await } -pub async fn duplicate_request( - id: &str, - updated_by: &str, - pool: &Pool, -) -> Result { +pub async fn duplicate_request(id: &str, pool: &Pool) -> Result { let existing = get_request(id, pool) .await .expect("Failed to get request to duplicate"); @@ -219,7 +210,6 @@ pub async fn duplicate_request( existing.url.as_str(), existing.headers.0, existing.sort_priority, - updated_by, pool, ) .await @@ -237,7 +227,6 @@ pub async fn upsert_request( url: &str, headers: Vec, sort_priority: f64, - updated_by: &str, pool: &Pool, ) -> Result { let generated_id; @@ -263,10 +252,9 @@ pub async fn upsert_request( authentication, authentication_type, headers, - sort_priority, - updated_by + sort_priority ) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT (id) DO UPDATE SET updated_at = CURRENT_TIMESTAMP, name = excluded.name, @@ -277,8 +265,7 @@ pub async fn upsert_request( authentication = excluded.authentication, authentication_type = excluded.authentication_type, url = excluded.url, - sort_priority = excluded.sort_priority, - updated_by = excluded.updated_by + sort_priority = excluded.sort_priority "#, id, workspace_id, @@ -291,7 +278,6 @@ pub async fn upsert_request( authentication_type, headers_json, sort_priority, - updated_by, ) .execute(pool) .await @@ -320,7 +306,6 @@ pub async fn find_requests( authentication AS "authentication!: Json>", authentication_type, sort_priority, - updated_by, headers AS "headers!: sqlx::types::Json>" FROM http_requests WHERE workspace_id = ? @@ -349,7 +334,6 @@ pub async fn get_request(id: &str, pool: &Pool) -> Result>", authentication_type, sort_priority, - updated_by, headers AS "headers!: sqlx::types::Json>" FROM http_requests WHERE id = ? @@ -385,7 +369,6 @@ pub async fn create_response( status_reason: Option<&str>, body: &str, headers: Vec, - updated_by: &str, pool: &Pool, ) -> Result { let req = get_request(request_id, pool) @@ -404,10 +387,9 @@ pub async fn create_response( status, status_reason, body, - headers, - updated_by + headers ) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?); + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?); "#, id, request_id, @@ -418,7 +400,6 @@ pub async fn create_response( status_reason, body, headers_json, - updated_by, ) .execute(pool) .await @@ -429,25 +410,23 @@ pub async fn create_response( pub async fn update_response_if_id( response: HttpResponse, - updated_by: &str, pool: &Pool, ) -> Result { if response.id == "" { return Ok(response); } - return update_response(response, updated_by, pool).await; + return update_response(response, pool).await; } pub async fn update_response( response: HttpResponse, - updated_by: &str, pool: &Pool, ) -> Result { let headers_json = Json(response.headers); sqlx::query!( r#" - UPDATE http_responses SET (elapsed, url, status, status_reason, body, error, headers, updated_by, updated_at) = - (?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) WHERE id = ?; + UPDATE http_responses SET (elapsed, url, status, status_reason, body, error, headers, updated_at) = + (?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) WHERE id = ?; "#, response.elapsed, response.url, @@ -456,7 +435,6 @@ pub async fn update_response( response.body, response.error, headers_json, - updated_by, response.id, ) .execute(pool) @@ -469,7 +447,7 @@ pub async fn get_response(id: &str, pool: &Pool) -> Result>" FROM http_responses @@ -488,7 +466,7 @@ pub async fn find_responses( sqlx::query_as!( HttpResponse, r#" - SELECT id, model, workspace_id, request_id, updated_at, updated_by, + SELECT id, model, workspace_id, request_id, updated_at, created_at, status, status_reason, body, elapsed, url, error, headers AS "headers!: sqlx::types::Json>" FROM http_responses diff --git a/src-web/components/App.tsx b/src-web/components/App.tsx index 49246e2c..24beac03 100644 --- a/src-web/components/App.tsx +++ b/src-web/components/App.tsx @@ -14,6 +14,7 @@ import { keyValueQueryKey } from '../hooks/useKeyValue'; import { requestsQueryKey } from '../hooks/useRequests'; import { responsesQueryKey } from '../hooks/useResponses'; import { routePaths } from '../hooks/useRoutes'; +import { UPDATE_DEBOUNCE_MILLIS } from '../hooks/useTauriListeners'; import { workspacesQueryKey } from '../hooks/useWorkspaces'; import { DEFAULT_FONT_SIZE } from '../lib/constants'; import { debounce } from '../lib/debounce'; @@ -22,8 +23,6 @@ import type { HttpRequest, HttpResponse, KeyValue, Workspace } from '../lib/mode import { AppRouter } from './AppRouter'; import { DialogProvider } from './DialogContext'; -const UPDATE_DEBOUNCE_MILLIS = 500; - const queryClient = new QueryClient({ defaultOptions: { queries: { @@ -145,7 +144,6 @@ await listen('zoom', ({ payload: zoomDelta }: { payload: number }) => { }); export function App() { - console.log('STARTING APP'); return ( diff --git a/src-web/components/AppRouter.tsx b/src-web/components/AppRouter.tsx index 4aaf8e12..871aa071 100644 --- a/src-web/components/AppRouter.tsx +++ b/src-web/components/AppRouter.tsx @@ -1,6 +1,14 @@ -import { createBrowserRouter, Navigate, RouterProvider } from 'react-router-dom'; +import { useEffect } from 'react'; +import { + createBrowserRouter, + Navigate, + Outlet, + RouterProvider, + useLocation, +} from 'react-router-dom'; import { routePaths } from '../hooks/useRoutes'; import { useTauriListeners } from '../hooks/useTauriListeners'; +import { setLastLocation } from '../lib/lastLocation'; import RouteError from './RouteError'; import Workspace from './Workspace'; import Workspaces from './Workspaces'; @@ -9,6 +17,7 @@ const router = createBrowserRouter([ { path: '/', errorElement: , + element: , children: [ { path: '/', @@ -34,8 +43,14 @@ const router = createBrowserRouter([ ]); export function AppRouter() { - console.log('AppRouter'); useTauriListeners(); - console.log('AppRouter 2'); return ; } + +function RouterRoot() { + const { pathname } = useLocation(); + useEffect(() => { + setLastLocation(pathname).catch(console.error); + }, [pathname]); + return ; +} diff --git a/src-web/components/Workspaces.tsx b/src-web/components/Workspaces.tsx index 375fa79b..e2d2539f 100644 --- a/src-web/components/Workspaces.tsx +++ b/src-web/components/Workspaces.tsx @@ -1,18 +1,18 @@ +import { Navigate } from 'react-router-dom'; +import { useKeyValue } from '../hooks/useKeyValue'; +import { useRoutes } from '../hooks/useRoutes'; import { useWorkspaces } from '../hooks/useWorkspaces'; -import { Button } from './core/Button'; import { Heading } from './core/Heading'; -import { VStack } from './core/Stacks'; export default function Workspaces() { + const lastWorkspace = useKeyValue({ key: 'last_workspace', defaultValue: null }); + const routes = useRoutes(); const workspaces = useWorkspaces(); - return ( - - Workspaces - {workspaces.map((w) => ( - - ))} - - ); + const workspace = workspaces[0]; + + if (workspace === undefined) { + return There are no workspaces; + } + + return ; } diff --git a/src-web/hooks/useKeyValue.ts b/src-web/hooks/useKeyValue.ts index e5920a04..52ddc674 100644 --- a/src-web/hooks/useKeyValue.ts +++ b/src-web/hooks/useKeyValue.ts @@ -15,7 +15,7 @@ export function keyValueQueryKey({ } // eslint-disable-next-line @typescript-eslint/ban-types -export function useKeyValue({ +export function useKeyValue({ namespace = DEFAULT_NAMESPACE, key, defaultValue, @@ -30,12 +30,10 @@ export function useKeyValue({ queryFn: async () => getKeyValue({ namespace, key, fallback: defaultValue }), }); - const mutate = useMutation({ + const mutate = useMutation({ mutationFn: (value) => setKeyValue({ namespace, key, value }), - onMutate: (value) => { - // k/v should be as fast as possible, so optimistically update the cache - queryClient.setQueryData(keyValueQueryKey({ namespace, key }), value); - }, + // k/v should be as fast as possible, so optimistically update the cache + onMutate: (value) => queryClient.setQueryData(keyValueQueryKey({ namespace, key }), value), }); const set = useCallback( diff --git a/src-web/hooks/useTauriListeners.ts b/src-web/hooks/useTauriListeners.ts index fc2f8e07..291147f7 100644 --- a/src-web/hooks/useTauriListeners.ts +++ b/src-web/hooks/useTauriListeners.ts @@ -9,7 +9,7 @@ import { useRequestUpdateKey } from './useRequestUpdateKey'; import { useSidebarDisplay } from './useSidebarDisplay'; const unsubFns: (() => void)[] = []; -const UPDATE_DEBOUNCE_MILLIS = 500; +export const UPDATE_DEBOUNCE_MILLIS = 1000; export function useTauriListeners() { const sidebarDisplay = useSidebarDisplay(); diff --git a/src-web/lib/keyValueStore.ts b/src-web/lib/keyValueStore.ts index aa8f07fd..389f7c27 100644 --- a/src-web/lib/keyValueStore.ts +++ b/src-web/lib/keyValueStore.ts @@ -11,13 +11,12 @@ export async function setKeyValue({ namespace?: string; key: string | string[]; value: T; -}): Promise { +}): Promise { await invoke('set_key_value', { namespace, key: buildKeyValueKey(key), value: JSON.stringify(value), }); - return value; } export async function getKeyValue({ diff --git a/src-web/lib/lastLocation.ts b/src-web/lib/lastLocation.ts new file mode 100644 index 00000000..99783fae --- /dev/null +++ b/src-web/lib/lastLocation.ts @@ -0,0 +1,17 @@ +import { getKeyValue, setKeyValue } from './keyValueStore'; + +export async function getLastLocation(): Promise { + return getKeyValue({ key: 'last_location', fallback: '/' }); +} + +export async function setLastLocation(pathname: string): Promise { + return setKeyValue({ key: 'last_location', value: pathname }); +} + +export async function syncLastLocation(): Promise { + const lastPathname = await getLastLocation(); + if (lastPathname !== window.location.pathname) { + console.log(`Redirecting to last location: ${lastPathname}`); + window.location.assign(lastPathname); + } +} diff --git a/src-web/main.tsx b/src-web/main.tsx index 3277b874..57b170ed 100644 --- a/src-web/main.tsx +++ b/src-web/main.tsx @@ -1,14 +1,13 @@ -console.log('FIRST 0'); import { StrictMode } from 'react'; import ReactDOM from 'react-dom/client'; import { App } from './components/App'; import { getKeyValue } from './lib/keyValueStore'; +import { syncLastLocation } from './lib/lastLocation'; import { getPreferredAppearance, setAppearance } from './lib/theme/window'; import './main.css'; -console.log('FIRST'); setAppearance(await getKeyValue({ key: 'appearance', fallback: getPreferredAppearance() })); -console.log('SECOND'); +await syncLastLocation(); // root holds our app's root DOM Element: ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(