Remove updated_by, remember last location

This commit is contained in:
Gregory Schier
2023-03-30 08:11:51 -07:00
parent 904d20b9b8
commit 7fcf709efe
13 changed files with 286 additions and 346 deletions

View File

@@ -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;

View File

@@ -0,0 +1,2 @@
ALTER TABLE http_requests ADD COLUMN authentication TEXT NOT NULL DEFAULT '{}';
ALTER TABLE http_requests ADD COLUMN authentication_type TEXT;

View File

@@ -1,5 +1,53 @@
{ {
"db": "SQLite", "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": { "07d1a1c7b4f3d9625a766e60fd57bb779b71dae30e5bbce34885a911a5a42428": {
"describe": { "describe": {
"columns": [], "columns": [],
@@ -20,69 +68,15 @@
}, },
"query": "\n DELETE FROM http_responses\n WHERE request_id = ?\n " "query": "\n DELETE FROM http_responses\n WHERE request_id = ?\n "
}, },
"19e0076c3cd13b73a46619b5c0ee5bf304fe27245db3d19a648f625bf5231cb0": { "318ed5a1126fe00719393cf4e6c788ee5a265af88b7253f61a475f78c6774ef6": {
"describe": { "describe": {
"columns": [], "columns": [],
"nullable": [], "nullable": [],
"parameters": { "parameters": {
"Right": 4 "Right": 9
} }
}, },
"query": "\n INSERT INTO workspaces (id, updated_by, name, description)\n VALUES (?, ?, ?, ?)\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 "
},
"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 "
}, },
"448a1d1f1866ab42c0f81fcf8eb2930bf21dfdd43ca4831bc1a198cf45ac3732": { "448a1d1f1866ab42c0f81fcf8eb2930bf21dfdd43ca4831bc1a198cf45ac3732": {
"describe": { "describe": {
@@ -94,7 +88,7 @@
}, },
"query": "\n DELETE FROM http_requests\n WHERE id = ?\n " "query": "\n DELETE FROM http_requests\n WHERE id = ?\n "
}, },
"51ee4652a889417dce585e4da457a629dd9e064b8866c3a916bc3bd2c933e14f": { "6f0cb5a6d1e8dbc8cdfcc3c7e7944b2c83c22cb795b9d6b98fe067dabec9680b": {
"describe": { "describe": {
"columns": [ "columns": [
{ {
@@ -113,9 +107,9 @@
"type_info": "Text" "type_info": "Text"
}, },
{ {
"name": "request_id", "name": "created_at",
"ordinal": 3, "ordinal": 3,
"type_info": "Text" "type_info": "Datetime"
}, },
{ {
"name": "updated_at", "name": "updated_at",
@@ -123,47 +117,47 @@
"type_info": "Datetime" "type_info": "Datetime"
}, },
{ {
"name": "updated_by", "name": "name",
"ordinal": 5, "ordinal": 5,
"type_info": "Text" "type_info": "Text"
}, },
{ {
"name": "created_at", "name": "url",
"ordinal": 6, "ordinal": 6,
"type_info": "Datetime" "type_info": "Text"
}, },
{ {
"name": "status", "name": "method",
"ordinal": 7, "ordinal": 7,
"type_info": "Int64"
},
{
"name": "status_reason",
"ordinal": 8,
"type_info": "Text" "type_info": "Text"
}, },
{ {
"name": "body", "name": "body",
"ordinal": 8,
"type_info": "Text"
},
{
"name": "body_type",
"ordinal": 9, "ordinal": 9,
"type_info": "Text" "type_info": "Text"
}, },
{ {
"name": "elapsed", "name": "authentication!: Json<HashMap<String, JsonValue>>",
"ordinal": 10, "ordinal": 10,
"type_info": "Int64" "type_info": "Text"
}, },
{ {
"name": "url", "name": "authentication_type",
"ordinal": 11, "ordinal": 11,
"type_info": "Text" "type_info": "Text"
}, },
{ {
"name": "error", "name": "sort_priority",
"ordinal": 12, "ordinal": 12,
"type_info": "Text" "type_info": "Float"
}, },
{ {
"name": "headers!: sqlx::types::Json<Vec<HttpResponseHeader>>", "name": "headers!: sqlx::types::Json<Vec<HttpRequestHeader>>",
"ordinal": 13, "ordinal": 13,
"type_info": "Text" "type_info": "Text"
} }
@@ -178,17 +172,17 @@
false, false,
false, false,
true, true,
false, true,
false,
false, false,
true, true,
false,
false false
], ],
"parameters": { "parameters": {
"Right": 1 "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<Vec<HttpResponseHeader>>\"\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<HashMap<String, JsonValue>>\",\n authentication_type,\n sort_priority,\n headers AS \"headers!: sqlx::types::Json<Vec<HttpRequestHeader>>\"\n FROM http_requests\n WHERE workspace_id = ?\n "
}, },
"84be2b954870ab181738656ecd4d03fca2ff21012947014c79626abfce8e999b": { "84be2b954870ab181738656ecd4d03fca2ff21012947014c79626abfce8e999b": {
"describe": { "describe": {
@@ -200,61 +194,27 @@
}, },
"query": "\n DELETE FROM workspaces\n WHERE id = ?\n " "query": "\n DELETE FROM workspaces\n WHERE id = ?\n "
}, },
"8d71216aa3902af45acc36bb4671e36bea6da2d30b42cfe8b70cff00cd00f256": { "a83698dcf9a815b881097133edb31a34ba25e7c6c114d463c495342a85371639": {
"describe": { "describe": {
"columns": [ "columns": [],
{ "nullable": [],
"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
],
"parameters": { "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": { "describe": {
"columns": [ "columns": [
{ {
@@ -278,23 +238,65 @@
"type_info": "Datetime" "type_info": "Datetime"
}, },
{ {
"name": "updated_by", "name": "name",
"ordinal": 4, "ordinal": 4,
"type_info": "Text" "type_info": "Text"
}, },
{
"name": "name",
"ordinal": 5,
"type_info": "Text"
},
{ {
"name": "description", "name": "description",
"ordinal": 6, "ordinal": 5,
"type_info": "Text" "type_info": "Text"
} }
], ],
"nullable": [ "nullable": [
false, 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, false,
false, false,
@@ -306,19 +308,9 @@
"Right": 0 "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": { "ced098adb79c0ee64e223b6e02371ef253920a2c342275de0fa9c181529a4adc": {
"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": {
"describe": { "describe": {
"columns": [ "columns": [
{ {
@@ -386,14 +378,9 @@
"ordinal": 12, "ordinal": 12,
"type_info": "Float" "type_info": "Float"
}, },
{
"name": "updated_by",
"ordinal": 13,
"type_info": "Text"
},
{ {
"name": "headers!: sqlx::types::Json<Vec<HttpRequestHeader>>", "name": "headers!: sqlx::types::Json<Vec<HttpRequestHeader>>",
"ordinal": 14, "ordinal": 13,
"type_info": "Text" "type_info": "Text"
} }
], ],
@@ -408,9 +395,8 @@
false, false,
true, true,
true, true,
true,
true,
false, false,
true,
false, false,
false false
], ],
@@ -418,29 +404,9 @@
"Right": 1 "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<HashMap<String, JsonValue>>\",\n authentication_type,\n sort_priority,\n updated_by,\n headers AS \"headers!: sqlx::types::Json<Vec<HttpRequestHeader>>\"\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<HashMap<String, JsonValue>>\",\n authentication_type,\n sort_priority,\n headers AS \"headers!: sqlx::types::Json<Vec<HttpRequestHeader>>\"\n FROM http_requests\n WHERE id = ?\n "
}, },
"ce62a799babc731dc53e43144751006c3905367c47c511b8abee832c58f8111d": { "d5ad6d5f82fe837fa9215bd4619ec18a7c95b3088d4fbf9825f2d1d28069d1ce": {
"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": {
"describe": { "describe": {
"columns": [ "columns": [
{ {
@@ -468,49 +434,44 @@
"ordinal": 4, "ordinal": 4,
"type_info": "Datetime" "type_info": "Datetime"
}, },
{
"name": "updated_by",
"ordinal": 5,
"type_info": "Text"
},
{ {
"name": "created_at", "name": "created_at",
"ordinal": 6, "ordinal": 5,
"type_info": "Datetime" "type_info": "Datetime"
}, },
{ {
"name": "status", "name": "status",
"ordinal": 7, "ordinal": 6,
"type_info": "Int64" "type_info": "Int64"
}, },
{ {
"name": "status_reason", "name": "status_reason",
"ordinal": 8, "ordinal": 7,
"type_info": "Text" "type_info": "Text"
}, },
{ {
"name": "body", "name": "body",
"ordinal": 9, "ordinal": 8,
"type_info": "Text" "type_info": "Text"
}, },
{ {
"name": "elapsed", "name": "elapsed",
"ordinal": 10, "ordinal": 9,
"type_info": "Int64" "type_info": "Int64"
}, },
{ {
"name": "url", "name": "url",
"ordinal": 11, "ordinal": 10,
"type_info": "Text" "type_info": "Text"
}, },
{ {
"name": "error", "name": "error",
"ordinal": 12, "ordinal": 11,
"type_info": "Text" "type_info": "Text"
}, },
{ {
"name": "headers!: sqlx::types::Json<Vec<HttpResponseHeader>>", "name": "headers!: sqlx::types::Json<Vec<HttpResponseHeader>>",
"ordinal": 13, "ordinal": 12,
"type_info": "Text" "type_info": "Text"
} }
], ],
@@ -522,7 +483,6 @@
false, false,
false, false,
false, false,
false,
true, true,
false, false,
false, false,
@@ -534,7 +494,7 @@
"Right": 1 "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<Vec<HttpResponseHeader>>\"\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<Vec<HttpResponseHeader>>\"\n FROM http_responses\n WHERE request_id = ?\n ORDER BY created_at ASC\n "
}, },
"d80c09497771e3641022e73ec6c6a87e73a551f88a948a5445d754922b82b50b": { "d80c09497771e3641022e73ec6c6a87e73a551f88a948a5445d754922b82b50b": {
"describe": { "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 " "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": { "describe": {
"columns": [ "columns": [
{ {
@@ -565,9 +525,9 @@
"type_info": "Text" "type_info": "Text"
}, },
{ {
"name": "created_at", "name": "request_id",
"ordinal": 3, "ordinal": 3,
"type_info": "Datetime" "type_info": "Text"
}, },
{ {
"name": "updated_at", "name": "updated_at",
@@ -575,17 +535,17 @@
"type_info": "Datetime" "type_info": "Datetime"
}, },
{ {
"name": "name", "name": "created_at",
"ordinal": 5, "ordinal": 5,
"type_info": "Text" "type_info": "Datetime"
}, },
{ {
"name": "url", "name": "status",
"ordinal": 6, "ordinal": 6,
"type_info": "Text" "type_info": "Int64"
}, },
{ {
"name": "method", "name": "status_reason",
"ordinal": 7, "ordinal": 7,
"type_info": "Text" "type_info": "Text"
}, },
@@ -595,33 +555,23 @@
"type_info": "Text" "type_info": "Text"
}, },
{ {
"name": "body_type", "name": "elapsed",
"ordinal": 9, "ordinal": 9,
"type_info": "Text" "type_info": "Int64"
}, },
{ {
"name": "authentication!: Json<HashMap<String, JsonValue>>", "name": "url",
"ordinal": 10, "ordinal": 10,
"type_info": "Text" "type_info": "Text"
}, },
{ {
"name": "authentication_type", "name": "error",
"ordinal": 11, "ordinal": 11,
"type_info": "Text" "type_info": "Text"
}, },
{ {
"name": "sort_priority", "name": "headers!: sqlx::types::Json<Vec<HttpResponseHeader>>",
"ordinal": 12, "ordinal": 12,
"type_info": "Float"
},
{
"name": "updated_by",
"ordinal": 13,
"type_info": "Text"
},
{
"name": "headers!: sqlx::types::Json<Vec<HttpRequestHeader>>",
"ordinal": 14,
"type_info": "Text" "type_info": "Text"
} }
], ],
@@ -633,19 +583,27 @@
false, false,
false, false,
false, false,
false,
true,
true,
true,
true, true,
false, false,
false, false,
false,
true,
false false
], ],
"parameters": { "parameters": {
"Right": 1 "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<HashMap<String, JsonValue>>\",\n authentication_type,\n sort_priority,\n updated_by,\n headers AS \"headers!: sqlx::types::Json<Vec<HttpRequestHeader>>\"\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<Vec<HttpResponseHeader>>\"\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 "
} }
} }

View File

@@ -22,7 +22,7 @@ use sqlx::sqlite::SqlitePoolOptions;
use sqlx::types::Json; use sqlx::types::Json;
use sqlx::{Pool, Sqlite}; use sqlx::{Pool, Sqlite};
use tauri::regex::Regex; 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 tauri::{CustomMenuItem, Manager, SystemTray, SystemTrayEvent, SystemTrayMenu, WindowEvent};
use tokio::sync::Mutex; use tokio::sync::Mutex;
@@ -213,7 +213,7 @@ async fn actually_send_ephemeral_request(
response.url = v.url().to_string(); response.url = v.url().to_string();
response.body = v.text().await.expect("Failed to get body"); response.body = v.text().await.expect("Failed to get body");
response.elapsed = start.elapsed().as_millis() as i64; 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 .await
.expect("Failed to update response"); .expect("Failed to update response");
emit_all_others(&window, "updated_response", &response); emit_all_others(&window, "updated_response", &response);
@@ -235,10 +235,9 @@ async fn send_request(
.await .await
.expect("Failed to get request"); .expect("Failed to get request");
let response = let response = models::create_response(&req.id, 0, "", 0, None, "", vec![], pool)
models::create_response(&req.id, 0, "", 0, None, "", vec![], window.label(), pool) .await
.await .expect("Failed to create response");
.expect("Failed to create response");
emit_all_others(&window, "updated_response", &response); emit_all_others(&window, "updated_response", &response);
actually_send_ephemeral_request(req, response, window, pool).await?; actually_send_ephemeral_request(req, response, window, pool).await?;
@@ -252,7 +251,7 @@ async fn response_err(
pool: &Pool<Sqlite>, pool: &Pool<Sqlite>,
) -> Result<models::HttpResponse, String> { ) -> Result<models::HttpResponse, String> {
response.error = Some(error.clone()); response.error = Some(error.clone());
response = models::update_response_if_id(response, window.label(), pool) response = models::update_response_if_id(response, pool)
.await .await
.expect("Failed to update response"); .expect("Failed to update response");
emit_all_others(&window, "updated_response", &response); emit_all_others(&window, "updated_response", &response);
@@ -295,7 +294,7 @@ async fn create_workspace(
db_instance: State<'_, Mutex<Pool<Sqlite>>>, db_instance: State<'_, Mutex<Pool<Sqlite>>>,
) -> Result<String, String> { ) -> Result<String, String> {
let pool = &*db_instance.lock().await; 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 .await
.expect("Failed to create workspace"); .expect("Failed to create workspace");
@@ -326,7 +325,6 @@ async fn create_request(
"", "",
headers, headers,
sort_priority, sort_priority,
window.label(),
pool, pool,
) )
.await .await
@@ -344,7 +342,7 @@ async fn duplicate_request(
db_instance: State<'_, Mutex<Pool<Sqlite>>>, db_instance: State<'_, Mutex<Pool<Sqlite>>>,
) -> Result<String, String> { ) -> Result<String, String> {
let pool = &*db_instance.lock().await; let pool = &*db_instance.lock().await;
let request = models::duplicate_request(id, window.label(), pool) let request = models::duplicate_request(id, pool)
.await .await
.expect("Failed to duplicate request"); .expect("Failed to duplicate request");
emit_all_others(&window, "updated_request", &request); emit_all_others(&window, "updated_request", &request);
@@ -382,7 +380,6 @@ async fn update_request(
request.url.as_str(), request.url.as_str(),
request.headers.0, request.headers.0,
request.sort_priority, request.sort_priority,
window.label(),
pool, pool,
) )
.await .await
@@ -468,21 +465,16 @@ async fn delete_all_responses(
#[tauri::command] #[tauri::command]
async fn workspaces( async fn workspaces(
db_instance: State<'_, Mutex<Pool<Sqlite>>>, db_instance: State<'_, Mutex<Pool<Sqlite>>>,
window: Window<Wry>,
) -> Result<Vec<models::Workspace>, String> { ) -> Result<Vec<models::Workspace>, String> {
let pool = &*db_instance.lock().await; let pool = &*db_instance.lock().await;
let workspaces = models::find_workspaces(pool) let workspaces = models::find_workspaces(pool)
.await .await
.expect("Failed to find workspaces"); .expect("Failed to find workspaces");
if workspaces.is_empty() { if workspaces.is_empty() {
let workspace = models::create_workspace( let workspace =
"My Project", models::create_workspace("My Project", "This is the default workspace", pool)
"This is the default workspace", .await
window.label(), .expect("Failed to create workspace");
pool,
)
.await
.expect("Failed to create workspace");
Ok(vec![workspace]) Ok(vec![workspace])
} else { } else {
Ok(workspaces) Ok(workspaces)
@@ -516,7 +508,6 @@ fn main() {
tauri::Builder::default() tauri::Builder::default()
.system_tray(system_tray) .system_tray(system_tray)
.setup(|app| { .setup(|app| {
let handle = app.handle();
let dir = match is_dev() { let dir = match is_dev() {
true => current_dir().unwrap(), true => current_dir().unwrap(),
false => app.path_resolver().app_data_dir().unwrap(), false => app.path_resolver().app_data_dir().unwrap(),
@@ -533,13 +524,6 @@ fn main() {
.await .await
.expect("Failed to connect to database"); .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 // Setup the DB handle
let m = Mutex::new(pool); let m = Mutex::new(pool);
migrate_db(app.handle(), &m) migrate_db(app.handle(), &m)
@@ -583,8 +567,17 @@ fn main() {
delete_response, delete_response,
delete_all_responses, delete_all_responses,
]) ])
.run(tauri::generate_context!()) .build(tauri::generate_context!())
.expect("error while running tauri application"); .expect("error while running tauri application")
.run(|app_handle, event| match event {
RunEvent::Ready => {
create_window(app_handle);
}
// ExitRequested { api, .. } => {
// }
_ => {}
});
} }
fn is_dev() -> bool { fn is_dev() -> bool {
@@ -592,7 +585,7 @@ fn is_dev() -> bool {
env.unwrap_or("production") != "production" env.unwrap_or("production") != "production"
} }
fn create_window(handle: AppHandle<Wry>, app_id: String) -> Window<Wry> { fn create_window(handle: &AppHandle<Wry>) -> Window<Wry> {
let default_menu = Menu::os_default("Yaak".to_string().as_str()); let default_menu = Menu::os_default("Yaak".to_string().as_str());
let mut test_menu = Menu::new() let mut test_menu = Menu::new()
.add_item( .add_item(
@@ -630,9 +623,9 @@ fn create_window(handle: AppHandle<Wry>, app_id: String) -> Window<Wry> {
let submenu = Submenu::new("Test Menu", test_menu); let submenu = Submenu::new("Test Menu", test_menu);
let window_num = handle.windows().len(); 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 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) .menu(menu)
.fullscreen(false) .fullscreen(false)
.resizable(true) .resizable(true)
@@ -644,6 +637,7 @@ fn create_window(handle: AppHandle<Wry>, app_id: String) -> Window<Wry> {
.expect("failed to build window"); .expect("failed to build window");
let win2 = win.clone(); let win2 = win.clone();
let handle2 = handle.clone();
win.on_menu_event(move |event| match event.menu_item_id() { win.on_menu_event(move |event| match event.menu_item_id() {
"quit" => std::process::exit(0), "quit" => std::process::exit(0),
"close" => win2.close().unwrap(), "close" => win2.close().unwrap(),
@@ -653,9 +647,7 @@ fn create_window(handle: AppHandle<Wry>, app_id: String) -> Window<Wry> {
"toggle_sidebar" => win2.emit("toggle_sidebar", true).unwrap(), "toggle_sidebar" => win2.emit("toggle_sidebar", true).unwrap(),
"refresh" => win2.emit("refresh", true).unwrap(), "refresh" => win2.emit("refresh", true).unwrap(),
"send_request" => win2.emit("send_request", true).unwrap(), "send_request" => win2.emit("send_request", true).unwrap(),
"new_window" => { "new_window" => _ = create_window(&handle2),
create_window(handle.clone(), app_id.clone());
}
"toggle_devtools" => { "toggle_devtools" => {
if win2.is_devtools_open() { if win2.is_devtools_open() {
win2.close_devtools(); win2.close_devtools();
@@ -676,6 +668,10 @@ fn create_window(handle: AppHandle<Wry>, app_id: String) -> Window<Wry> {
match e { match e {
WindowEvent::Resized(..) => apply_offset(), WindowEvent::Resized(..) => apply_offset(),
WindowEvent::ThemeChanged(..) => apply_offset(), WindowEvent::ThemeChanged(..) => apply_offset(),
WindowEvent::CloseRequested { .. } => {
println!("CLOSE REQUESTED");
// api.prevent_close();
}
_ => {} _ => {}
} }
}); });
@@ -686,19 +682,6 @@ fn create_window(handle: AppHandle<Wry>, app_id: String) -> Window<Wry> {
win win
} }
async fn get_or_create_client_id(pool: &Pool<Sqlite>) -> 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 /// Emit an event to all windows except the current one
fn emit_all_others<S: Serialize + Clone>(current_window: &Window<Wry>, event: &str, payload: S) { fn emit_all_others<S: Serialize + Clone>(current_window: &Window<Wry>, event: &str, payload: S) {
let windows = current_window.app_handle().windows(); let windows = current_window.app_handle().windows();

View File

@@ -1,9 +1,10 @@
use std::collections::HashMap;
use rand::distributions::{Alphanumeric, DistString}; use rand::distributions::{Alphanumeric, DistString};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sqlx::types::chrono::NaiveDateTime; use sqlx::types::chrono::NaiveDateTime;
use sqlx::types::{Json, JsonValue}; use sqlx::types::{Json, JsonValue};
use sqlx::{Pool, Sqlite}; use sqlx::{Pool, Sqlite};
use std::collections::HashMap;
#[derive(sqlx::FromRow, Debug, Clone, Serialize, Deserialize)] #[derive(sqlx::FromRow, Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
@@ -12,7 +13,6 @@ pub struct Workspace {
pub model: String, pub model: String,
pub created_at: NaiveDateTime, pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime, pub updated_at: NaiveDateTime,
pub updated_by: String,
pub name: String, pub name: String,
pub description: String, pub description: String,
} }
@@ -33,7 +33,6 @@ pub struct HttpRequest {
pub model: String, pub model: String,
pub created_at: NaiveDateTime, pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime, pub updated_at: NaiveDateTime,
pub updated_by: String,
pub sort_priority: f64, pub sort_priority: f64,
pub workspace_id: String, pub workspace_id: String,
pub name: String, pub name: String,
@@ -62,7 +61,6 @@ pub struct HttpResponse {
pub request_id: String, pub request_id: String,
pub created_at: NaiveDateTime, pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime, pub updated_at: NaiveDateTime,
pub updated_by: String,
pub error: Option<String>, pub error: Option<String>,
pub url: String, pub url: String,
pub elapsed: i64, pub elapsed: i64,
@@ -78,7 +76,6 @@ pub struct KeyValue {
pub model: String, pub model: String,
pub created_at: NaiveDateTime, pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime, pub updated_at: NaiveDateTime,
pub updated_by: String,
pub namespace: String, pub namespace: String,
pub key: String, pub key: String,
pub value: String, pub value: String,
@@ -112,7 +109,7 @@ pub async fn get_key_value(namespace: &str, key: &str, pool: &Pool<Sqlite>) -> O
sqlx::query_as!( sqlx::query_as!(
KeyValue, KeyValue,
r#" r#"
SELECT model, created_at, updated_at, updated_by, namespace, key, value SELECT model, created_at, updated_at, namespace, key, value
FROM key_values FROM key_values
WHERE namespace = ? AND key = ? WHERE namespace = ? AND key = ?
"#, "#,
@@ -128,7 +125,7 @@ pub async fn find_workspaces(pool: &Pool<Sqlite>) -> Result<Vec<Workspace>, sqlx
sqlx::query_as!( sqlx::query_as!(
Workspace, Workspace,
r#" r#"
SELECT id, model, created_at, updated_at, updated_by, name, description SELECT id, model, created_at, updated_at, name, description
FROM workspaces FROM workspaces
"#, "#,
) )
@@ -140,7 +137,7 @@ pub async fn get_workspace(id: &str, pool: &Pool<Sqlite>) -> Result<Workspace, s
sqlx::query_as!( sqlx::query_as!(
Workspace, Workspace,
r#" r#"
SELECT id, model, created_at, updated_at, updated_by, name, description SELECT id, model, created_at, updated_at, name, description
FROM workspaces WHERE id = ? FROM workspaces WHERE id = ?
"#, "#,
id, id,
@@ -168,19 +165,17 @@ pub async fn delete_workspace(id: &str, pool: &Pool<Sqlite>) -> Result<Workspace
pub async fn create_workspace( pub async fn create_workspace(
name: &str, name: &str,
description: &str, description: &str,
updated_by: &str,
pool: &Pool<Sqlite>, pool: &Pool<Sqlite>,
) -> Result<Workspace, sqlx::Error> { ) -> Result<Workspace, sqlx::Error> {
let id = generate_id("wk"); let id = generate_id("wk");
sqlx::query!( sqlx::query!(
r#" r#"
INSERT INTO workspaces (id, updated_by, name, description) INSERT INTO workspaces (id, name, description)
VALUES (?, ?, ?, ?) VALUES (?, ?, ?)
"#, "#,
id, id,
name, name,
description, description,
updated_by,
) )
.execute(pool) .execute(pool)
.await .await
@@ -189,11 +184,7 @@ pub async fn create_workspace(
get_workspace(&id, pool).await get_workspace(&id, pool).await
} }
pub async fn duplicate_request( pub async fn duplicate_request(id: &str, pool: &Pool<Sqlite>) -> Result<HttpRequest, sqlx::Error> {
id: &str,
updated_by: &str,
pool: &Pool<Sqlite>,
) -> Result<HttpRequest, sqlx::Error> {
let existing = get_request(id, pool) let existing = get_request(id, pool)
.await .await
.expect("Failed to get request to duplicate"); .expect("Failed to get request to duplicate");
@@ -219,7 +210,6 @@ pub async fn duplicate_request(
existing.url.as_str(), existing.url.as_str(),
existing.headers.0, existing.headers.0,
existing.sort_priority, existing.sort_priority,
updated_by,
pool, pool,
) )
.await .await
@@ -237,7 +227,6 @@ pub async fn upsert_request(
url: &str, url: &str,
headers: Vec<HttpRequestHeader>, headers: Vec<HttpRequestHeader>,
sort_priority: f64, sort_priority: f64,
updated_by: &str,
pool: &Pool<Sqlite>, pool: &Pool<Sqlite>,
) -> Result<HttpRequest, sqlx::Error> { ) -> Result<HttpRequest, sqlx::Error> {
let generated_id; let generated_id;
@@ -263,10 +252,9 @@ pub async fn upsert_request(
authentication, authentication,
authentication_type, authentication_type,
headers, headers,
sort_priority, sort_priority
updated_by
) )
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT (id) DO UPDATE SET ON CONFLICT (id) DO UPDATE SET
updated_at = CURRENT_TIMESTAMP, updated_at = CURRENT_TIMESTAMP,
name = excluded.name, name = excluded.name,
@@ -277,8 +265,7 @@ pub async fn upsert_request(
authentication = excluded.authentication, authentication = excluded.authentication,
authentication_type = excluded.authentication_type, authentication_type = excluded.authentication_type,
url = excluded.url, url = excluded.url,
sort_priority = excluded.sort_priority, sort_priority = excluded.sort_priority
updated_by = excluded.updated_by
"#, "#,
id, id,
workspace_id, workspace_id,
@@ -291,7 +278,6 @@ pub async fn upsert_request(
authentication_type, authentication_type,
headers_json, headers_json,
sort_priority, sort_priority,
updated_by,
) )
.execute(pool) .execute(pool)
.await .await
@@ -320,7 +306,6 @@ pub async fn find_requests(
authentication AS "authentication!: Json<HashMap<String, JsonValue>>", authentication AS "authentication!: Json<HashMap<String, JsonValue>>",
authentication_type, authentication_type,
sort_priority, sort_priority,
updated_by,
headers AS "headers!: sqlx::types::Json<Vec<HttpRequestHeader>>" headers AS "headers!: sqlx::types::Json<Vec<HttpRequestHeader>>"
FROM http_requests FROM http_requests
WHERE workspace_id = ? WHERE workspace_id = ?
@@ -349,7 +334,6 @@ pub async fn get_request(id: &str, pool: &Pool<Sqlite>) -> Result<HttpRequest, s
authentication AS "authentication!: Json<HashMap<String, JsonValue>>", authentication AS "authentication!: Json<HashMap<String, JsonValue>>",
authentication_type, authentication_type,
sort_priority, sort_priority,
updated_by,
headers AS "headers!: sqlx::types::Json<Vec<HttpRequestHeader>>" headers AS "headers!: sqlx::types::Json<Vec<HttpRequestHeader>>"
FROM http_requests FROM http_requests
WHERE id = ? WHERE id = ?
@@ -385,7 +369,6 @@ pub async fn create_response(
status_reason: Option<&str>, status_reason: Option<&str>,
body: &str, body: &str,
headers: Vec<HttpResponseHeader>, headers: Vec<HttpResponseHeader>,
updated_by: &str,
pool: &Pool<Sqlite>, pool: &Pool<Sqlite>,
) -> Result<HttpResponse, sqlx::Error> { ) -> Result<HttpResponse, sqlx::Error> {
let req = get_request(request_id, pool) let req = get_request(request_id, pool)
@@ -404,10 +387,9 @@ pub async fn create_response(
status, status,
status_reason, status_reason,
body, body,
headers, headers
updated_by
) )
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?); VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);
"#, "#,
id, id,
request_id, request_id,
@@ -418,7 +400,6 @@ pub async fn create_response(
status_reason, status_reason,
body, body,
headers_json, headers_json,
updated_by,
) )
.execute(pool) .execute(pool)
.await .await
@@ -429,25 +410,23 @@ pub async fn create_response(
pub async fn update_response_if_id( pub async fn update_response_if_id(
response: HttpResponse, response: HttpResponse,
updated_by: &str,
pool: &Pool<Sqlite>, pool: &Pool<Sqlite>,
) -> Result<HttpResponse, sqlx::Error> { ) -> Result<HttpResponse, sqlx::Error> {
if response.id == "" { if response.id == "" {
return Ok(response); return Ok(response);
} }
return update_response(response, updated_by, pool).await; return update_response(response, pool).await;
} }
pub async fn update_response( pub async fn update_response(
response: HttpResponse, response: HttpResponse,
updated_by: &str,
pool: &Pool<Sqlite>, pool: &Pool<Sqlite>,
) -> Result<HttpResponse, sqlx::Error> { ) -> Result<HttpResponse, sqlx::Error> {
let headers_json = Json(response.headers); let headers_json = Json(response.headers);
sqlx::query!( sqlx::query!(
r#" r#"
UPDATE http_responses SET (elapsed, url, status, status_reason, body, error, headers, updated_by, updated_at) = UPDATE http_responses SET (elapsed, url, status, status_reason, body, error, headers, updated_at) =
(?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) WHERE id = ?; (?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) WHERE id = ?;
"#, "#,
response.elapsed, response.elapsed,
response.url, response.url,
@@ -456,7 +435,6 @@ pub async fn update_response(
response.body, response.body,
response.error, response.error,
headers_json, headers_json,
updated_by,
response.id, response.id,
) )
.execute(pool) .execute(pool)
@@ -469,7 +447,7 @@ pub async fn get_response(id: &str, pool: &Pool<Sqlite>) -> Result<HttpResponse,
sqlx::query_as_unchecked!( sqlx::query_as_unchecked!(
HttpResponse, HttpResponse,
r#" r#"
SELECT id, model, workspace_id, request_id, updated_at, updated_by, created_at, SELECT id, model, workspace_id, request_id, updated_at, created_at,
status, status_reason, body, elapsed, url, error, status, status_reason, body, elapsed, url, error,
headers AS "headers!: sqlx::types::Json<Vec<HttpResponseHeader>>" headers AS "headers!: sqlx::types::Json<Vec<HttpResponseHeader>>"
FROM http_responses FROM http_responses
@@ -488,7 +466,7 @@ pub async fn find_responses(
sqlx::query_as!( sqlx::query_as!(
HttpResponse, HttpResponse,
r#" 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, created_at, status, status_reason, body, elapsed, url, error,
headers AS "headers!: sqlx::types::Json<Vec<HttpResponseHeader>>" headers AS "headers!: sqlx::types::Json<Vec<HttpResponseHeader>>"
FROM http_responses FROM http_responses

View File

@@ -14,6 +14,7 @@ import { keyValueQueryKey } from '../hooks/useKeyValue';
import { requestsQueryKey } from '../hooks/useRequests'; import { requestsQueryKey } from '../hooks/useRequests';
import { responsesQueryKey } from '../hooks/useResponses'; import { responsesQueryKey } from '../hooks/useResponses';
import { routePaths } from '../hooks/useRoutes'; import { routePaths } from '../hooks/useRoutes';
import { UPDATE_DEBOUNCE_MILLIS } from '../hooks/useTauriListeners';
import { workspacesQueryKey } from '../hooks/useWorkspaces'; import { workspacesQueryKey } from '../hooks/useWorkspaces';
import { DEFAULT_FONT_SIZE } from '../lib/constants'; import { DEFAULT_FONT_SIZE } from '../lib/constants';
import { debounce } from '../lib/debounce'; import { debounce } from '../lib/debounce';
@@ -22,8 +23,6 @@ import type { HttpRequest, HttpResponse, KeyValue, Workspace } from '../lib/mode
import { AppRouter } from './AppRouter'; import { AppRouter } from './AppRouter';
import { DialogProvider } from './DialogContext'; import { DialogProvider } from './DialogContext';
const UPDATE_DEBOUNCE_MILLIS = 500;
const queryClient = new QueryClient({ const queryClient = new QueryClient({
defaultOptions: { defaultOptions: {
queries: { queries: {
@@ -145,7 +144,6 @@ await listen('zoom', ({ payload: zoomDelta }: { payload: number }) => {
}); });
export function App() { export function App() {
console.log('STARTING APP');
return ( return (
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<MotionConfig transition={{ duration: 0.1 }}> <MotionConfig transition={{ duration: 0.1 }}>

View File

@@ -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 { routePaths } from '../hooks/useRoutes';
import { useTauriListeners } from '../hooks/useTauriListeners'; import { useTauriListeners } from '../hooks/useTauriListeners';
import { setLastLocation } from '../lib/lastLocation';
import RouteError from './RouteError'; import RouteError from './RouteError';
import Workspace from './Workspace'; import Workspace from './Workspace';
import Workspaces from './Workspaces'; import Workspaces from './Workspaces';
@@ -9,6 +17,7 @@ const router = createBrowserRouter([
{ {
path: '/', path: '/',
errorElement: <RouteError />, errorElement: <RouteError />,
element: <RouterRoot />,
children: [ children: [
{ {
path: '/', path: '/',
@@ -34,8 +43,14 @@ const router = createBrowserRouter([
]); ]);
export function AppRouter() { export function AppRouter() {
console.log('AppRouter');
useTauriListeners(); useTauriListeners();
console.log('AppRouter 2');
return <RouterProvider router={router} />; return <RouterProvider router={router} />;
} }
function RouterRoot() {
const { pathname } = useLocation();
useEffect(() => {
setLastLocation(pathname).catch(console.error);
}, [pathname]);
return <Outlet />;
}

View File

@@ -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 { useWorkspaces } from '../hooks/useWorkspaces';
import { Button } from './core/Button';
import { Heading } from './core/Heading'; import { Heading } from './core/Heading';
import { VStack } from './core/Stacks';
export default function Workspaces() { export default function Workspaces() {
const lastWorkspace = useKeyValue<string | null>({ key: 'last_workspace', defaultValue: null });
const routes = useRoutes();
const workspaces = useWorkspaces(); const workspaces = useWorkspaces();
return ( const workspace = workspaces[0];
<VStack as="ul" className="p-12" space={1}>
<Heading>Workspaces</Heading> if (workspace === undefined) {
{workspaces.map((w) => ( return <Heading>There are no workspaces</Heading>;
<Button key={w.id} color="gray" to={`/workspaces/${w.id}`}> }
{w.name}
</Button> return <Navigate to={routes.paths.workspace({ workspaceId: workspace.id })} />;
))}
</VStack>
);
} }

View File

@@ -15,7 +15,7 @@ export function keyValueQueryKey({
} }
// eslint-disable-next-line @typescript-eslint/ban-types // eslint-disable-next-line @typescript-eslint/ban-types
export function useKeyValue<T extends Object>({ export function useKeyValue<T extends Object | null>({
namespace = DEFAULT_NAMESPACE, namespace = DEFAULT_NAMESPACE,
key, key,
defaultValue, defaultValue,
@@ -30,12 +30,10 @@ export function useKeyValue<T extends Object>({
queryFn: async () => getKeyValue({ namespace, key, fallback: defaultValue }), queryFn: async () => getKeyValue({ namespace, key, fallback: defaultValue }),
}); });
const mutate = useMutation<T, unknown, T>({ const mutate = useMutation<void, unknown, T>({
mutationFn: (value) => setKeyValue<T>({ namespace, key, value }), mutationFn: (value) => setKeyValue<T>({ namespace, key, value }),
onMutate: (value) => { // k/v should be as fast as possible, so optimistically update the cache
// k/v should be as fast as possible, so optimistically update the cache onMutate: (value) => queryClient.setQueryData(keyValueQueryKey({ namespace, key }), value),
queryClient.setQueryData(keyValueQueryKey({ namespace, key }), value);
},
}); });
const set = useCallback( const set = useCallback(

View File

@@ -9,7 +9,7 @@ import { useRequestUpdateKey } from './useRequestUpdateKey';
import { useSidebarDisplay } from './useSidebarDisplay'; import { useSidebarDisplay } from './useSidebarDisplay';
const unsubFns: (() => void)[] = []; const unsubFns: (() => void)[] = [];
const UPDATE_DEBOUNCE_MILLIS = 500; export const UPDATE_DEBOUNCE_MILLIS = 1000;
export function useTauriListeners() { export function useTauriListeners() {
const sidebarDisplay = useSidebarDisplay(); const sidebarDisplay = useSidebarDisplay();

View File

@@ -11,13 +11,12 @@ export async function setKeyValue<T>({
namespace?: string; namespace?: string;
key: string | string[]; key: string | string[];
value: T; value: T;
}): Promise<T> { }): Promise<void> {
await invoke('set_key_value', { await invoke('set_key_value', {
namespace, namespace,
key: buildKeyValueKey(key), key: buildKeyValueKey(key),
value: JSON.stringify(value), value: JSON.stringify(value),
}); });
return value;
} }
export async function getKeyValue<T>({ export async function getKeyValue<T>({

View File

@@ -0,0 +1,17 @@
import { getKeyValue, setKeyValue } from './keyValueStore';
export async function getLastLocation(): Promise<string> {
return getKeyValue({ key: 'last_location', fallback: '/' });
}
export async function setLastLocation(pathname: string): Promise<void> {
return setKeyValue({ key: 'last_location', value: pathname });
}
export async function syncLastLocation(): Promise<void> {
const lastPathname = await getLastLocation();
if (lastPathname !== window.location.pathname) {
console.log(`Redirecting to last location: ${lastPathname}`);
window.location.assign(lastPathname);
}
}

View File

@@ -1,14 +1,13 @@
console.log('FIRST 0');
import { StrictMode } from 'react'; import { StrictMode } from 'react';
import ReactDOM from 'react-dom/client'; import ReactDOM from 'react-dom/client';
import { App } from './components/App'; import { App } from './components/App';
import { getKeyValue } from './lib/keyValueStore'; import { getKeyValue } from './lib/keyValueStore';
import { syncLastLocation } from './lib/lastLocation';
import { getPreferredAppearance, setAppearance } from './lib/theme/window'; import { getPreferredAppearance, setAppearance } from './lib/theme/window';
import './main.css'; import './main.css';
console.log('FIRST');
setAppearance(await getKeyValue({ key: 'appearance', fallback: getPreferredAppearance() })); setAppearance(await getKeyValue({ key: 'appearance', fallback: getPreferredAppearance() }));
console.log('SECOND'); await syncLastLocation();
// root holds our app's root DOM Element: // root holds our app's root DOM Element:
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(