mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-01-11 20:00:29 +01:00
Flatten migrations, kvs lib, fix tabs
This commit is contained in:
37
index.html
37
index.html
@@ -1,17 +1,28 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Yaak App</title>
|
||||
<!-- <script src="http://localhost:8097"></script>-->
|
||||
</head>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Yaak App</title>
|
||||
<!-- <script src="http://localhost:8097"></script>-->
|
||||
<style>
|
||||
body {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<div id="cm-portal" class="cm-portal" style="pointer-events: auto"></div>
|
||||
<div id="radix-portal" class="cm-portal"></div>
|
||||
<script type="module" src="/src-web/main.tsx"></script>
|
||||
</body>
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background-color: black;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<div id="cm-portal" class="cm-portal" style="pointer-events: auto"></div>
|
||||
<div id="radix-portal" class="cm-portal"></div>
|
||||
<script type="module" src="/src-web/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,39 +1,65 @@
|
||||
CREATE TABLE key_values
|
||||
(
|
||||
model TEXT DEFAULT 'key_value' NOT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
deleted_at DATETIME,
|
||||
namespace TEXT NOT NULL,
|
||||
key TEXT NOT NULL,
|
||||
value TEXT NOT NULL,
|
||||
PRIMARY KEY (namespace, key)
|
||||
);
|
||||
|
||||
CREATE TABLE workspaces
|
||||
(
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
id TEXT NOT NULL
|
||||
PRIMARY KEY,
|
||||
model TEXT DEFAULT 'workspace' NOT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
deleted_at DATETIME,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT NOT NULL
|
||||
name TEXT NOT NULL,
|
||||
description TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE http_requests
|
||||
(
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
workspace_id TEXT NOT NULL REFERENCES workspaces (id) ON DELETE CASCADE,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
id TEXT NOT NULL
|
||||
PRIMARY KEY,
|
||||
model TEXT DEFAULT 'http_request' NOT NULL,
|
||||
workspace_id TEXT NOT NULL
|
||||
REFERENCES workspaces
|
||||
ON DELETE CASCADE,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
deleted_at DATETIME,
|
||||
name TEXT NOT NULL,
|
||||
url TEXT NOT NULL,
|
||||
method TEXT NOT NULL,
|
||||
headers TEXT NOT NULL,
|
||||
body TEXT
|
||||
name TEXT NOT NULL,
|
||||
url TEXT NOT NULL,
|
||||
method TEXT NOT NULL,
|
||||
headers TEXT NOT NULL,
|
||||
body TEXT,
|
||||
body_type TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE http_responses
|
||||
(
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
request_id TEXT NOT NULL REFERENCES http_requests (id) ON DELETE CASCADE,
|
||||
workspace_id TEXT NOT NULL REFERENCES workspaces (id) ON DELETE CASCADE,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
id TEXT NOT NULL
|
||||
PRIMARY KEY,
|
||||
model TEXT DEFAULT 'http_response' NOT NULL,
|
||||
request_id TEXT NOT NULL
|
||||
REFERENCES http_requests
|
||||
ON DELETE CASCADE,
|
||||
workspace_id TEXT NOT NULL
|
||||
REFERENCES workspaces
|
||||
ON DELETE CASCADE,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
deleted_at DATETIME,
|
||||
elapsed INTEGER NOT NULL,
|
||||
status INTEGER NOT NULL,
|
||||
elapsed INTEGER NOT NULL,
|
||||
status INTEGER NOT NULL,
|
||||
status_reason TEXT,
|
||||
url TEXT NOT NULL,
|
||||
body TEXT NOT NULL,
|
||||
headers TEXT NOT NULL
|
||||
url TEXT NOT NULL,
|
||||
body TEXT NOT NULL,
|
||||
headers TEXT NOT NULL,
|
||||
error TEXT
|
||||
);
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
ALTER TABLE http_responses ADD COLUMN error TEXT NULL;
|
||||
@@ -1 +0,0 @@
|
||||
ALTER TABLE http_requests ADD COLUMN body_type TEXT NULL;
|
||||
@@ -1,12 +0,0 @@
|
||||
CREATE TABLE key_values
|
||||
(
|
||||
model TEXT NOT NULL DEFAULT 'key_value',
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at DATETIME,
|
||||
namespace TEXT NOT NULL,
|
||||
key TEXT NOT NULL,
|
||||
value TEXT NOT NULL,
|
||||
|
||||
PRIMARY KEY (namespace, key)
|
||||
);
|
||||
@@ -1,3 +0,0 @@
|
||||
ALTER TABLE http_responses ADD COLUMN model TEXT DEFAULT 'http_response';
|
||||
ALTER TABLE http_requests ADD COLUMN model TEXT DEFAULT 'http_request';
|
||||
ALTER TABLE workspaces ADD COLUMN model TEXT DEFAULT 'workspace';
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db": "SQLite",
|
||||
"032c7a3861630c499f3e9785687be303aa95c023548e170572ad36eba90ecf84": {
|
||||
"06aaf8f4a17566f1d25da2a60f0baf4b5fc28c3cf0c001a84e25edf9eab3c7e3": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -18,24 +18,19 @@
|
||||
"ordinal": 2,
|
||||
"type_info": "Datetime"
|
||||
},
|
||||
{
|
||||
"name": "deleted_at",
|
||||
"ordinal": 3,
|
||||
"type_info": "Datetime"
|
||||
},
|
||||
{
|
||||
"name": "namespace",
|
||||
"ordinal": 4,
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "key",
|
||||
"ordinal": 5,
|
||||
"ordinal": 4,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "value",
|
||||
"ordinal": 6,
|
||||
"ordinal": 5,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
@@ -43,7 +38,6 @@
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
@@ -52,7 +46,7 @@
|
||||
"Right": 2
|
||||
}
|
||||
},
|
||||
"query": "\n SELECT model, created_at, updated_at, deleted_at, namespace, key, value\n FROM key_values\n WHERE namespace = ? AND key = ?\n "
|
||||
"query": "\n SELECT model, created_at, updated_at, namespace, key, value\n FROM key_values\n WHERE namespace = ? AND key = ?\n "
|
||||
},
|
||||
"07d1a1c7b4f3d9625a766e60fd57bb779b71dae30e5bbce34885a911a5a42428": {
|
||||
"describe": {
|
||||
@@ -84,7 +78,7 @@
|
||||
},
|
||||
"query": "\n DELETE FROM http_requests\n WHERE id = ?\n "
|
||||
},
|
||||
"8423faeb5ce376acffe4c24804e0332c334edf929d9cea0086ee00c108e833fb": {
|
||||
"68b7b17a25d415ce90b33aef16418d668f7ff6275b383bf21c16cafb38cca342": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -103,9 +97,9 @@
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "request_id",
|
||||
"name": "created_at",
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
"type_info": "Datetime"
|
||||
},
|
||||
{
|
||||
"name": "updated_at",
|
||||
@@ -113,48 +107,33 @@
|
||||
"type_info": "Datetime"
|
||||
},
|
||||
{
|
||||
"name": "deleted_at",
|
||||
"name": "name",
|
||||
"ordinal": 5,
|
||||
"type_info": "Datetime"
|
||||
"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": "headers!: sqlx::types::Json<Vec<HttpRequestHeader>>",
|
||||
"ordinal": 10,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "url",
|
||||
"ordinal": 11,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "error",
|
||||
"ordinal": 12,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "headers!: sqlx::types::Json<Vec<HttpResponseHeader>>",
|
||||
"ordinal": 13,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
@@ -164,13 +143,10 @@
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false
|
||||
],
|
||||
@@ -178,7 +154,7 @@
|
||||
"Right": 1
|
||||
}
|
||||
},
|
||||
"query": "\n SELECT id, model, workspace_id, request_id, updated_at, deleted_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 "
|
||||
"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 headers AS \"headers!: sqlx::types::Json<Vec<HttpRequestHeader>>\"\n FROM http_requests\n WHERE workspace_id = ?\n "
|
||||
},
|
||||
"913f3c3a46b1834c4cd8367aed9d5a9659a1d775d8771e9f5bf9a5aa41197356": {
|
||||
"describe": {
|
||||
@@ -190,60 +166,6 @@
|
||||
},
|
||||
"query": "\n INSERT INTO http_requests (\n id,\n workspace_id,\n name,\n url,\n method,\n body,\n body_type,\n headers\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 url = excluded.url\n "
|
||||
},
|
||||
"9fcbf8317858427b2e75b29ce64deeed1cc77ef22a35463ab65eee89eb25fa02": {
|
||||
"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": "deleted_at",
|
||||
"ordinal": 4,
|
||||
"type_info": "Datetime"
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"ordinal": 5,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"ordinal": 6,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
}
|
||||
},
|
||||
"query": "\n SELECT id, model, created_at, updated_at, deleted_at, name, description\n FROM workspaces\n WHERE id = ?\n "
|
||||
},
|
||||
"a83698dcf9a815b881097133edb31a34ba25e7c6c114d463c495342a85371639": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
@@ -254,7 +176,7 @@
|
||||
},
|
||||
"query": "\n UPDATE http_responses SET (elapsed, url, status, status_reason, body, error, headers, updated_at) =\n (?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) WHERE id = ?;\n "
|
||||
},
|
||||
"bca6dd1eec0cdf4de57323f7b88e8578ebe44b6c9606e38cbaacc3e0094b434d": {
|
||||
"caf3f21bf291dfbd36446592066e96c1f83abe96f6ea9211a3e049eb9c58a8c8": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -267,54 +189,24 @@
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "workspace_id",
|
||||
"ordinal": 2,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "created_at",
|
||||
"ordinal": 3,
|
||||
"ordinal": 2,
|
||||
"type_info": "Datetime"
|
||||
},
|
||||
{
|
||||
"name": "updated_at",
|
||||
"ordinal": 4,
|
||||
"type_info": "Datetime"
|
||||
},
|
||||
{
|
||||
"name": "deleted_at",
|
||||
"ordinal": 5,
|
||||
"ordinal": 3,
|
||||
"type_info": "Datetime"
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"ordinal": 6,
|
||||
"ordinal": 4,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "url",
|
||||
"ordinal": 7,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "method",
|
||||
"ordinal": 8,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "body",
|
||||
"ordinal": 9,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "body_type",
|
||||
"ordinal": 10,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "headers!: sqlx::types::Json<Vec<HttpRequestHeader>>",
|
||||
"ordinal": 11,
|
||||
"name": "description",
|
||||
"ordinal": 5,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
@@ -324,31 +216,63 @@
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
}
|
||||
},
|
||||
"query": "\n SELECT\n id,\n model,\n workspace_id,\n created_at,\n updated_at,\n deleted_at,\n name,\n url,\n method,\n body,\n body_type,\n headers AS \"headers!: sqlx::types::Json<Vec<HttpRequestHeader>>\"\n FROM http_requests\n WHERE id = ?\n "
|
||||
"query": "\n SELECT id, model, created_at, updated_at, name, description\n FROM workspaces WHERE id = ?\n "
|
||||
},
|
||||
"d80c09497771e3641022e73ec6c6a87e73a551f88a948a5445d754922b82b50b": {
|
||||
"cea4cae52f16ec78aca9a47b17117422d4f165e5a3b308c70fd1a180382475ea": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"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
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 3
|
||||
"Right": 0
|
||||
}
|
||||
},
|
||||
"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 SELECT id, model, created_at, updated_at, name, description\n FROM workspaces\n "
|
||||
},
|
||||
"d9b614f9f0de223474464e0a30e663f6f4467f85d6f6abaacd8b76d9289ba38f": {
|
||||
"d5ad6d5f82fe837fa9215bd4619ec18a7c95b3088d4fbf9825f2d1d28069d1ce": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -377,48 +301,43 @@
|
||||
"type_info": "Datetime"
|
||||
},
|
||||
{
|
||||
"name": "deleted_at",
|
||||
"name": "created_at",
|
||||
"ordinal": 5,
|
||||
"type_info": "Datetime"
|
||||
},
|
||||
{
|
||||
"name": "created_at",
|
||||
"ordinal": 6,
|
||||
"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<Vec<HttpResponseHeader>>",
|
||||
"ordinal": 13,
|
||||
"ordinal": 12,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
@@ -428,7 +347,6 @@
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
@@ -442,73 +360,19 @@
|
||||
"Right": 1
|
||||
}
|
||||
},
|
||||
"query": "\n SELECT id, model, workspace_id, request_id, updated_at, deleted_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 "
|
||||
"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 "
|
||||
},
|
||||
"e767522f92c8c49cd2e563e58737a05092daf9b1dc763bacc82a5c14d696d78e": {
|
||||
"d80c09497771e3641022e73ec6c6a87e73a551f88a948a5445d754922b82b50b": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Right": 9
|
||||
"Right": 3
|
||||
}
|
||||
},
|
||||
"query": "\n INSERT INTO http_responses (id, request_id, workspace_id, elapsed, url, status, status_reason, body, headers)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);\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 "
|
||||
},
|
||||
"eb7b10c632c871122d9e50cacba1f52604c366d888f014ac7f0a2642dbd27839": {
|
||||
"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": "deleted_at",
|
||||
"ordinal": 4,
|
||||
"type_info": "Datetime"
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"ordinal": 5,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"ordinal": 6,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 0
|
||||
}
|
||||
},
|
||||
"query": "\n SELECT id, model, created_at, updated_at, deleted_at, name, description\n FROM workspaces\n "
|
||||
},
|
||||
"eda13351c05e67885a71a1b9fc7f2d0641d94ca5f4be824d4a0edad5f1ec657e": {
|
||||
"d9ea350bc21ac2f51f6dcb9713328ec330f0e12105da70bf5a6eff9601e32a85": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -536,39 +400,34 @@
|
||||
"ordinal": 4,
|
||||
"type_info": "Datetime"
|
||||
},
|
||||
{
|
||||
"name": "deleted_at",
|
||||
"ordinal": 5,
|
||||
"type_info": "Datetime"
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"ordinal": 6,
|
||||
"ordinal": 5,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "url",
|
||||
"ordinal": 7,
|
||||
"ordinal": 6,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "method",
|
||||
"ordinal": 8,
|
||||
"ordinal": 7,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "body",
|
||||
"ordinal": 9,
|
||||
"ordinal": 8,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "body_type",
|
||||
"ordinal": 10,
|
||||
"ordinal": 9,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "headers!: sqlx::types::Json<Vec<HttpRequestHeader>>",
|
||||
"ordinal": 11,
|
||||
"ordinal": 10,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
@@ -578,7 +437,6 @@
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
@@ -590,7 +448,107 @@
|
||||
"Right": 1
|
||||
}
|
||||
},
|
||||
"query": "\n SELECT\n id,\n model,\n workspace_id,\n created_at,\n updated_at,\n deleted_at,\n name,\n url,\n method,\n body,\n body_type,\n headers AS \"headers!: sqlx::types::Json<Vec<HttpRequestHeader>>\"\n FROM http_requests\n WHERE workspace_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 headers AS \"headers!: sqlx::types::Json<Vec<HttpRequestHeader>>\"\n FROM http_requests\n WHERE id = ?\n "
|
||||
},
|
||||
"e3ade0a69348d512e47e964bded9d7d890b92fdc1e01c6c22fa5e91f943639f2": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "model",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "workspace_id",
|
||||
"ordinal": 2,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "request_id",
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "updated_at",
|
||||
"ordinal": 4,
|
||||
"type_info": "Datetime"
|
||||
},
|
||||
{
|
||||
"name": "created_at",
|
||||
"ordinal": 5,
|
||||
"type_info": "Datetime"
|
||||
},
|
||||
{
|
||||
"name": "status",
|
||||
"ordinal": 6,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "status_reason",
|
||||
"ordinal": 7,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "body",
|
||||
"ordinal": 8,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "elapsed",
|
||||
"ordinal": 9,
|
||||
"type_info": "Int64"
|
||||
},
|
||||
{
|
||||
"name": "url",
|
||||
"ordinal": 10,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "error",
|
||||
"ordinal": 11,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "headers!: sqlx::types::Json<Vec<HttpResponseHeader>>",
|
||||
"ordinal": 12,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
}
|
||||
},
|
||||
"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 "
|
||||
},
|
||||
"e767522f92c8c49cd2e563e58737a05092daf9b1dc763bacc82a5c14d696d78e": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Right": 9
|
||||
}
|
||||
},
|
||||
"query": "\n INSERT INTO http_responses (id, request_id, workspace_id, elapsed, url, status, status_reason, body, headers)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);\n "
|
||||
},
|
||||
"f116d8cf9aad828135bb8c3a4c8b8e6b857ae13303989e9133a33b2d1cf20e96": {
|
||||
"describe": {
|
||||
|
||||
@@ -354,7 +354,7 @@ async fn workspaces(
|
||||
.await
|
||||
.expect("Failed to find workspaces");
|
||||
if workspaces.is_empty() {
|
||||
let workspace = models::create_workspace("Default", "This is the default workspace", pool)
|
||||
let workspace = models::create_workspace("My Project", "This is the default workspace", pool)
|
||||
.await
|
||||
.expect("Failed to create workspace");
|
||||
Ok(vec![workspace])
|
||||
|
||||
@@ -11,7 +11,6 @@ pub struct Workspace {
|
||||
pub model: String,
|
||||
pub created_at: NaiveDateTime,
|
||||
pub updated_at: NaiveDateTime,
|
||||
pub deleted_at: Option<NaiveDateTime>,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
}
|
||||
@@ -30,7 +29,6 @@ pub struct HttpRequest {
|
||||
pub model: String,
|
||||
pub created_at: NaiveDateTime,
|
||||
pub updated_at: NaiveDateTime,
|
||||
pub deleted_at: Option<NaiveDateTime>,
|
||||
pub workspace_id: String,
|
||||
pub name: String,
|
||||
pub url: String,
|
||||
@@ -56,7 +54,6 @@ pub struct HttpResponse {
|
||||
pub request_id: String,
|
||||
pub created_at: NaiveDateTime,
|
||||
pub updated_at: NaiveDateTime,
|
||||
pub deleted_at: Option<NaiveDateTime>,
|
||||
pub error: Option<String>,
|
||||
pub url: String,
|
||||
pub elapsed: i64,
|
||||
@@ -72,7 +69,6 @@ pub struct KeyValue {
|
||||
pub model: String,
|
||||
pub created_at: NaiveDateTime,
|
||||
pub updated_at: NaiveDateTime,
|
||||
pub deleted_at: Option<NaiveDateTime>,
|
||||
pub namespace: String,
|
||||
pub key: String,
|
||||
pub value: String,
|
||||
@@ -106,7 +102,7 @@ pub async fn get_key_value(namespace: &str, key: &str, pool: &Pool<Sqlite>) -> O
|
||||
sqlx::query_as!(
|
||||
KeyValue,
|
||||
r#"
|
||||
SELECT model, created_at, updated_at, deleted_at, namespace, key, value
|
||||
SELECT model, created_at, updated_at, namespace, key, value
|
||||
FROM key_values
|
||||
WHERE namespace = ? AND key = ?
|
||||
"#,
|
||||
@@ -122,7 +118,7 @@ pub async fn find_workspaces(pool: &Pool<Sqlite>) -> Result<Vec<Workspace>, sqlx
|
||||
sqlx::query_as!(
|
||||
Workspace,
|
||||
r#"
|
||||
SELECT id, model, created_at, updated_at, deleted_at, name, description
|
||||
SELECT id, model, created_at, updated_at, name, description
|
||||
FROM workspaces
|
||||
"#,
|
||||
)
|
||||
@@ -134,9 +130,8 @@ pub async fn get_workspace(id: &str, pool: &Pool<Sqlite>) -> Result<Workspace, s
|
||||
sqlx::query_as!(
|
||||
Workspace,
|
||||
r#"
|
||||
SELECT id, model, created_at, updated_at, deleted_at, name, description
|
||||
FROM workspaces
|
||||
WHERE id = ?
|
||||
SELECT id, model, created_at, updated_at, name, description
|
||||
FROM workspaces WHERE id = ?
|
||||
"#,
|
||||
id,
|
||||
)
|
||||
@@ -236,7 +231,6 @@ pub async fn find_requests(
|
||||
workspace_id,
|
||||
created_at,
|
||||
updated_at,
|
||||
deleted_at,
|
||||
name,
|
||||
url,
|
||||
method,
|
||||
@@ -262,7 +256,6 @@ pub async fn get_request(id: &str, pool: &Pool<Sqlite>) -> Result<HttpRequest, s
|
||||
workspace_id,
|
||||
created_at,
|
||||
updated_at,
|
||||
deleted_at,
|
||||
name,
|
||||
url,
|
||||
method,
|
||||
@@ -361,7 +354,7 @@ pub async fn get_response(id: &str, pool: &Pool<Sqlite>) -> Result<HttpResponse,
|
||||
sqlx::query_as_unchecked!(
|
||||
HttpResponse,
|
||||
r#"
|
||||
SELECT id, model, workspace_id, request_id, updated_at, deleted_at, created_at,
|
||||
SELECT id, model, workspace_id, request_id, updated_at, created_at,
|
||||
status, status_reason, body, elapsed, url, error,
|
||||
headers AS "headers!: sqlx::types::Json<Vec<HttpResponseHeader>>"
|
||||
FROM http_responses
|
||||
@@ -380,7 +373,7 @@ pub async fn find_responses(
|
||||
sqlx::query_as!(
|
||||
HttpResponse,
|
||||
r#"
|
||||
SELECT id, model, workspace_id, request_id, updated_at, deleted_at,
|
||||
SELECT id, model, workspace_id, request_id, updated_at,
|
||||
created_at, status, status_reason, body, elapsed, url, error,
|
||||
headers AS "headers!: sqlx::types::Json<Vec<HttpResponseHeader>>"
|
||||
FROM http_responses
|
||||
|
||||
@@ -5,10 +5,11 @@ import { listen } from '@tauri-apps/api/event';
|
||||
import { MotionConfig } from 'framer-motion';
|
||||
import { HelmetProvider } from 'react-helmet-async';
|
||||
import { matchPath } from 'react-router-dom';
|
||||
import { keyValueQueryKey } from '../hooks/useKeyValues';
|
||||
import { keyValueQueryKey } from '../hooks/useKeyValue';
|
||||
import { requestsQueryKey } from '../hooks/useRequests';
|
||||
import { responsesQueryKey } from '../hooks/useResponses';
|
||||
import { DEFAULT_FONT_SIZE } from '../lib/constants';
|
||||
import { extractKeyValue } from '../lib/keyValueStore';
|
||||
import type { HttpRequest, HttpResponse, KeyValue } from '../lib/models';
|
||||
import { convertDates } from '../lib/models';
|
||||
import { AppRouter, WORKSPACE_REQUEST_PATH } from './AppRouter';
|
||||
@@ -16,7 +17,7 @@ import { AppRouter, WORKSPACE_REQUEST_PATH } from './AppRouter';
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
await listen('updated_key_value', ({ payload: keyValue }: { payload: KeyValue }) => {
|
||||
queryClient.setQueryData(keyValueQueryKey(keyValue), keyValue);
|
||||
queryClient.setQueryData(keyValueQueryKey(keyValue), extractKeyValue(keyValue));
|
||||
});
|
||||
|
||||
await listen('updated_request', ({ payload: request }: { payload: HttpRequest }) => {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import classnames from 'classnames';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { useActiveRequest } from '../hooks/useActiveRequest';
|
||||
import { useIsResponseLoading } from '../hooks/useIsResponseLoading';
|
||||
import { useKeyValues } from '../hooks/useKeyValues';
|
||||
import { useKeyValue } from '../hooks/useKeyValue';
|
||||
import { useSendRequest } from '../hooks/useSendRequest';
|
||||
import { useUpdateRequest } from '../hooks/useUpdateRequest';
|
||||
import { tryFormatJson } from '../lib/formatters';
|
||||
@@ -21,7 +22,7 @@ export function RequestPane({ fullHeight, className }: Props) {
|
||||
const updateRequest = useUpdateRequest(activeRequest);
|
||||
const sendRequest = useSendRequest(activeRequest);
|
||||
const responseLoading = useIsResponseLoading();
|
||||
const activeTab = useKeyValues({
|
||||
const activeTab = useKeyValue<string>({
|
||||
key: ['active_request_body_tab', activeRequest?.id ?? 'n/a'],
|
||||
initialValue: 'body',
|
||||
});
|
||||
@@ -61,7 +62,6 @@ export function RequestPane({ fullHeight, className }: Props) {
|
||||
{ value: 'auth', label: 'Auth' },
|
||||
]}
|
||||
className="mt-2"
|
||||
defaultValue="body"
|
||||
label="Request body"
|
||||
>
|
||||
<TabContent value="headers">
|
||||
|
||||
@@ -3,10 +3,11 @@ import React, { useRef, useState } from 'react';
|
||||
import { useActiveRequest } from '../hooks/useActiveRequest';
|
||||
import { useCreateRequest } from '../hooks/useCreateRequest';
|
||||
import { useDeleteRequest } from '../hooks/useDeleteRequest';
|
||||
import { useKeyValues } from '../hooks/useKeyValues';
|
||||
import { useKeyValue } from '../hooks/useKeyValue';
|
||||
import { useRequests } from '../hooks/useRequests';
|
||||
import { useTheme } from '../hooks/useTheme';
|
||||
import { useUpdateRequest } from '../hooks/useUpdateRequest';
|
||||
import { clamp } from '../lib/clamp';
|
||||
import type { HttpRequest } from '../lib/models';
|
||||
import { Button } from './core/Button';
|
||||
import { Dropdown, DropdownMenuTrigger } from './core/Dropdown';
|
||||
@@ -19,11 +20,12 @@ interface Props {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const MIN_WIDTH = 130;
|
||||
const MIN_WIDTH = 110;
|
||||
const MAX_WIDTH = 500;
|
||||
|
||||
export function Sidebar({ className }: Props) {
|
||||
const [isDragging, setIsDragging] = useState<boolean>(false);
|
||||
const width = useKeyValues<number>({ key: 'sidebar_width', initialValue: 200 });
|
||||
const width = useKeyValue<number>({ key: 'sidebar_width', initialValue: 200 });
|
||||
const requests = useRequests();
|
||||
const activeRequest = useActiveRequest();
|
||||
const createRequest = useCreateRequest({ navigateAfter: true });
|
||||
@@ -43,7 +45,7 @@ export function Sidebar({ className }: Props) {
|
||||
const startWidth = width.value;
|
||||
moveState.current = {
|
||||
move: (e: MouseEvent) => {
|
||||
const newWidth = Math.max(MIN_WIDTH, startWidth + (e.clientX - mouseStartX));
|
||||
const newWidth = clamp(startWidth + (e.clientX - mouseStartX), MIN_WIDTH, MAX_WIDTH);
|
||||
width.set(newWidth);
|
||||
},
|
||||
up: () => {
|
||||
@@ -73,8 +75,8 @@ export function Sidebar({ className }: Props) {
|
||||
>
|
||||
<div
|
||||
className={classnames(
|
||||
'transition-colors w-[1px] group-hover:bg-white/10 h-full pointer-events-none',
|
||||
isDragging && '!bg-white/20',
|
||||
'transition-colors w-[1px] group-hover:bg-gray-300 h-full pointer-events-none',
|
||||
isDragging && '!bg-blue-500/70',
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -10,7 +10,6 @@ import { HStack } from '../Stacks';
|
||||
import './Tabs.css';
|
||||
|
||||
interface Props {
|
||||
defaultValue?: string;
|
||||
label: string;
|
||||
onChangeValue: (value: string) => void;
|
||||
value: string;
|
||||
@@ -31,7 +30,6 @@ interface Props {
|
||||
export function Tabs({
|
||||
value,
|
||||
onChangeValue,
|
||||
defaultValue,
|
||||
label,
|
||||
children,
|
||||
tabs,
|
||||
@@ -40,7 +38,7 @@ export function Tabs({
|
||||
}: Props) {
|
||||
return (
|
||||
<T.Root
|
||||
defaultValue={defaultValue}
|
||||
value={value}
|
||||
onValueChange={onChangeValue}
|
||||
className={classnames(className, 'h-full grid grid-rows-[auto_minmax(0,1fr)] grid-cols-1')}
|
||||
>
|
||||
|
||||
@@ -4,5 +4,5 @@ export function useIsResponseLoading(): boolean {
|
||||
const responses = useResponses();
|
||||
const response = responses[responses.length - 1];
|
||||
if (!response) return false;
|
||||
return !(response.body || response.error);
|
||||
return !(response.body || response.status || response.error);
|
||||
}
|
||||
|
||||
39
src-web/hooks/useKeyValue.ts
Normal file
39
src-web/hooks/useKeyValue.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||
import { buildKeyValueKey, getKeyValue, setKeyValue } from '../lib/keyValueStore';
|
||||
|
||||
const DEFAULT_NAMESPACE = 'app';
|
||||
|
||||
export function keyValueQueryKey({
|
||||
namespace = DEFAULT_NAMESPACE,
|
||||
key,
|
||||
}: {
|
||||
namespace?: string;
|
||||
key: string | string[];
|
||||
}) {
|
||||
return ['key_value', { namespace, key: buildKeyValueKey(key) }];
|
||||
}
|
||||
|
||||
export function useKeyValue<T extends string | number | boolean>({
|
||||
namespace = DEFAULT_NAMESPACE,
|
||||
key,
|
||||
initialValue,
|
||||
}: {
|
||||
namespace?: string;
|
||||
key: string | string[];
|
||||
initialValue: T;
|
||||
}) {
|
||||
const query = useQuery<T>({
|
||||
initialData: initialValue,
|
||||
queryKey: keyValueQueryKey({ namespace, key }),
|
||||
queryFn: async () => getKeyValue({ namespace, key, fallback: initialValue }),
|
||||
});
|
||||
|
||||
const mutate = useMutation<T, unknown, T>({
|
||||
mutationFn: (value) => setKeyValue<T>({ namespace, key, value }),
|
||||
});
|
||||
|
||||
return {
|
||||
value: query.data,
|
||||
set: (value: T) => mutate.mutate(value),
|
||||
};
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||
import { invoke } from '@tauri-apps/api';
|
||||
import type { KeyValue } from '../lib/models';
|
||||
|
||||
const DEFAULT_NAMESPACE = 'app';
|
||||
|
||||
export function keyValueQueryKey({
|
||||
namespace = DEFAULT_NAMESPACE,
|
||||
key,
|
||||
}: {
|
||||
namespace?: string;
|
||||
key: string | string[];
|
||||
}) {
|
||||
return ['key_value', { namespace, key: buildKey(key) }];
|
||||
}
|
||||
|
||||
export function useKeyValues<T extends string | number | boolean>({
|
||||
namespace = DEFAULT_NAMESPACE,
|
||||
key,
|
||||
initialValue,
|
||||
}: {
|
||||
namespace?: string;
|
||||
key: string | string[];
|
||||
initialValue: T;
|
||||
}) {
|
||||
const query = useQuery<KeyValue | null>({
|
||||
initialData: null,
|
||||
queryKey: keyValueQueryKey({ namespace, key }),
|
||||
queryFn: async () => invoke('get_key_value', { namespace, key: buildKey(key) }),
|
||||
});
|
||||
|
||||
const mutate = useMutation<KeyValue, unknown, T>({
|
||||
mutationFn: (value) => {
|
||||
return invoke('set_key_value', {
|
||||
namespace,
|
||||
key: buildKey(key),
|
||||
value: JSON.stringify(value),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
let value: T;
|
||||
try {
|
||||
value = JSON.parse(query.data?.value ?? JSON.stringify(initialValue));
|
||||
} catch (e) {
|
||||
value = initialValue;
|
||||
}
|
||||
return {
|
||||
value,
|
||||
set: (value: T) => mutate.mutate(value),
|
||||
};
|
||||
}
|
||||
|
||||
function buildKey(key: string | string[]): string {
|
||||
if (typeof key === 'string') return key;
|
||||
return key.join('::');
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useKeyValues } from './useKeyValues';
|
||||
import { useKeyValue } from './useKeyValue';
|
||||
|
||||
export function useResponseViewMode(requestId?: string): [string, () => void] {
|
||||
const v = useKeyValues({
|
||||
const v = useKeyValue<string>({
|
||||
namespace: 'app',
|
||||
key: ['response_view_mode', requestId ?? 'n/a'],
|
||||
initialValue: 'pretty',
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { app } from '@tauri-apps/api';
|
||||
import { useEffect } from 'react';
|
||||
import type { Appearance } from '../lib/theme/window';
|
||||
import {
|
||||
@@ -5,10 +6,13 @@ import {
|
||||
setAppearance,
|
||||
subscribeToPreferredAppearanceChange,
|
||||
} from '../lib/theme/window';
|
||||
import { useKeyValues } from './useKeyValues';
|
||||
import { useKeyValue } from './useKeyValue';
|
||||
|
||||
export function useTheme() {
|
||||
const appearanceKv = useKeyValues({ key: 'appearance', initialValue: getAppearance() });
|
||||
const appearanceKv = useKeyValue<Appearance>({
|
||||
key: 'appearance',
|
||||
initialValue: getAppearance(),
|
||||
});
|
||||
|
||||
const themeChange = (appearance: Appearance) => {
|
||||
appearanceKv.set(appearance);
|
||||
@@ -22,7 +26,7 @@ export function useTheme() {
|
||||
useEffect(() => subscribeToPreferredAppearanceChange(themeChange), []);
|
||||
|
||||
// Sync appearance when k/v changes
|
||||
useEffect(() => setAppearance(appearanceKv.value as Appearance), [appearanceKv.value]);
|
||||
useEffect(() => setAppearance(appearanceKv.value), [appearanceKv.value]);
|
||||
|
||||
return {
|
||||
appearance: appearanceKv.value,
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useQuery } from '@tanstack/react-query';
|
||||
export function useWorkspaces() {
|
||||
return (
|
||||
useQuery(['workspaces'], async () => {
|
||||
console.log('INVOKING WORKSPACES');
|
||||
const workspaces = (await invoke('workspaces')) as Workspace[];
|
||||
return workspaces.map(convertDates);
|
||||
}).data ?? []
|
||||
|
||||
3
src-web/lib/clamp.ts
Normal file
3
src-web/lib/clamp.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export function clamp(value: number, min: number, max: number): number {
|
||||
return Math.min(Math.max(value, min), max);
|
||||
}
|
||||
62
src-web/lib/keyValueStore.ts
Normal file
62
src-web/lib/keyValueStore.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { invoke } from '@tauri-apps/api';
|
||||
import type { KeyValue } from './models';
|
||||
|
||||
const DEFAULT_NAMESPACE = 'app';
|
||||
|
||||
type KeyValueValue = string | number | boolean;
|
||||
|
||||
export async function setKeyValue<T>({
|
||||
namespace = DEFAULT_NAMESPACE,
|
||||
key,
|
||||
value,
|
||||
}: {
|
||||
namespace?: string;
|
||||
key: string | string[];
|
||||
value: T;
|
||||
}): Promise<T> {
|
||||
await invoke('set_key_value', {
|
||||
namespace,
|
||||
key: buildKeyValueKey(key),
|
||||
value: JSON.stringify(value),
|
||||
});
|
||||
return value;
|
||||
}
|
||||
|
||||
export async function getKeyValue<T extends KeyValueValue>({
|
||||
namespace = DEFAULT_NAMESPACE,
|
||||
key,
|
||||
fallback,
|
||||
}: {
|
||||
namespace?: string;
|
||||
key: string | string[];
|
||||
fallback: T;
|
||||
}) {
|
||||
const kv = (await invoke('get_key_value', {
|
||||
namespace,
|
||||
key: buildKeyValueKey(key),
|
||||
})) as KeyValue | null;
|
||||
return extractKeyValueOrFallback(kv, fallback);
|
||||
}
|
||||
|
||||
export function extractKeyValue<T extends KeyValueValue>(kv: KeyValue | null): T | undefined {
|
||||
if (kv === null) return undefined;
|
||||
try {
|
||||
return JSON.parse(kv.value) as T;
|
||||
} catch (err) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function extractKeyValueOrFallback<T extends KeyValueValue>(
|
||||
kv: KeyValue | null,
|
||||
fallback: T,
|
||||
): T {
|
||||
const v = extractKeyValue<T>(kv);
|
||||
if (v === undefined) return fallback;
|
||||
return v;
|
||||
}
|
||||
|
||||
export function buildKeyValueKey(key: string | string[]): string {
|
||||
if (typeof key === 'string') return key;
|
||||
return key.join('::');
|
||||
}
|
||||
@@ -3,7 +3,6 @@ export interface BaseModel {
|
||||
readonly workspaceId: string;
|
||||
readonly createdAt: Date;
|
||||
readonly updatedAt: Date;
|
||||
readonly deletedAt: Date | null;
|
||||
}
|
||||
|
||||
export interface Workspace extends BaseModel {
|
||||
@@ -46,14 +45,11 @@ export interface HttpResponse extends BaseModel {
|
||||
readonly headers: HttpHeader[];
|
||||
}
|
||||
|
||||
export function convertDates<T extends Pick<BaseModel, 'createdAt' | 'updatedAt' | 'deletedAt'>>(
|
||||
m: T,
|
||||
): T {
|
||||
export function convertDates<T extends Pick<BaseModel, 'createdAt' | 'updatedAt'>>(m: T): T {
|
||||
return {
|
||||
...m,
|
||||
createdAt: convertDate(m.createdAt),
|
||||
updatedAt: convertDate(m.updatedAt),
|
||||
deletedAt: m.deletedAt ? convertDate(m.deletedAt) : null,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
import { invoke } from '@tauri-apps/api';
|
||||
import { StrictMode } from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import { App } from './components/App';
|
||||
import type { KeyValue } from './lib/models';
|
||||
import type { Appearance } from './lib/theme/window';
|
||||
import { getKeyValue } from './lib/keyValueStore';
|
||||
import { getPreferredAppearance, setAppearance } from './lib/theme/window';
|
||||
import './main.css';
|
||||
|
||||
const appearance: KeyValue = await invoke('get_key_value', {
|
||||
namespace: 'app',
|
||||
key: 'appearance',
|
||||
});
|
||||
setAppearance((appearance?.value ?? getPreferredAppearance()) as Appearance);
|
||||
setAppearance(await getKeyValue({ key: 'appearance', fallback: getPreferredAppearance() }));
|
||||
|
||||
// root holds our app's root DOM Element:
|
||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||
|
||||
Reference in New Issue
Block a user