mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-07-04 20:11:48 +02:00
Fix workspace creation, reveal sync dir, and don't update timestamps on sync/import
This commit is contained in:
@@ -33,6 +33,7 @@
|
|||||||
"opener:allow-open-url",
|
"opener:allow-open-url",
|
||||||
"opener:allow-open-path",
|
"opener:allow-open-path",
|
||||||
"opener:allow-default-urls",
|
"opener:allow-default-urls",
|
||||||
|
"opener:allow-reveal-item-in-dir",
|
||||||
"core:webview:allow-set-webview-zoom",
|
"core:webview:allow-set-webview-zoom",
|
||||||
"core:window:allow-close",
|
"core:window:allow-close",
|
||||||
"core:window:allow-internal-toggle-maximize",
|
"core:window:allow-internal-toggle-maximize",
|
||||||
|
|||||||
Generated
+1
-1
@@ -1 +1 @@
|
|||||||
{"main":{"identifier":"main","description":"Main permissions","local":true,"windows":["*"],"permissions":["core:event:allow-emit","core:event:allow-listen","core:event:allow-unlisten","os:allow-os-type","clipboard-manager:allow-clear","clipboard-manager:allow-write-text","clipboard-manager:allow-read-text","dialog:allow-open","dialog:allow-save","fs:allow-read-dir","fs:allow-read-file","fs:allow-read-text-file",{"identifier":"fs:scope","allow":[{"path":"$APPDATA"},{"path":"$APPDATA/**"}]},"opener:allow-open-url","opener:allow-open-path","opener:allow-default-urls","core:webview:allow-set-webview-zoom","core:window:allow-close","core:window:allow-internal-toggle-maximize","core:window:allow-is-fullscreen","core:window:allow-maximize","core:window:allow-minimize","core:window:allow-set-decorations","core:window:allow-set-title","core:window:allow-show","core:window:allow-start-dragging","core:window:allow-theme","core:window:allow-toggle-maximize","core:window:allow-unmaximize","clipboard-manager:allow-read-text","clipboard-manager:allow-write-text","yaak-license:default","yaak-sync:default"]}}
|
{"main":{"identifier":"main","description":"Main permissions","local":true,"windows":["*"],"permissions":["core:event:allow-emit","core:event:allow-listen","core:event:allow-unlisten","os:allow-os-type","clipboard-manager:allow-clear","clipboard-manager:allow-write-text","clipboard-manager:allow-read-text","dialog:allow-open","dialog:allow-save","fs:allow-read-dir","fs:allow-read-file","fs:allow-read-text-file",{"identifier":"fs:scope","allow":[{"path":"$APPDATA"},{"path":"$APPDATA/**"}]},"opener:allow-open-url","opener:allow-open-path","opener:allow-default-urls","opener:allow-reveal-item-in-dir","core:webview:allow-set-webview-zoom","core:window:allow-close","core:window:allow-internal-toggle-maximize","core:window:allow-is-fullscreen","core:window:allow-maximize","core:window:allow-minimize","core:window:allow-set-decorations","core:window:allow-set-title","core:window:allow-show","core:window:allow-start-dragging","core:window:allow-theme","core:window:allow-toggle-maximize","core:window:allow-unmaximize","clipboard-manager:allow-read-text","clipboard-manager:allow-write-text","yaak-license:default","yaak-sync:default"]}}
|
||||||
@@ -9,7 +9,7 @@ use crate::models::{
|
|||||||
WorkspaceMetaIden,
|
WorkspaceMetaIden,
|
||||||
};
|
};
|
||||||
use crate::plugin::SqliteConnection;
|
use crate::plugin::SqliteConnection;
|
||||||
use chrono::NaiveDateTime;
|
use chrono::{NaiveDateTime, Utc};
|
||||||
use log::{debug, error, info, warn};
|
use log::{debug, error, info, warn};
|
||||||
use nanoid::nanoid;
|
use nanoid::nanoid;
|
||||||
use rusqlite::OptionalExtension;
|
use rusqlite::OptionalExtension;
|
||||||
@@ -278,8 +278,8 @@ pub async fn upsert_workspace<R: Runtime>(
|
|||||||
])
|
])
|
||||||
.values_panic([
|
.values_panic([
|
||||||
id.as_str().into(),
|
id.as_str().into(),
|
||||||
CurrentTimestamp.into(),
|
timestamp_for_upsert(update_source, workspace.created_at).into(),
|
||||||
CurrentTimestamp.into(),
|
timestamp_for_upsert(update_source, workspace.updated_at).into(),
|
||||||
trimmed_name.into(),
|
trimmed_name.into(),
|
||||||
workspace.description.into(),
|
workspace.description.into(),
|
||||||
workspace.setting_follow_redirects.into(),
|
workspace.setting_follow_redirects.into(),
|
||||||
@@ -297,6 +297,7 @@ pub async fn upsert_workspace<R: Runtime>(
|
|||||||
WorkspaceIden::SettingRequestTimeout,
|
WorkspaceIden::SettingRequestTimeout,
|
||||||
WorkspaceIden::SettingValidateCertificates,
|
WorkspaceIden::SettingValidateCertificates,
|
||||||
])
|
])
|
||||||
|
.values([(WorkspaceIden::UpdatedAt, CurrentTimestamp.into())])
|
||||||
.to_owned(),
|
.to_owned(),
|
||||||
)
|
)
|
||||||
.returning_all()
|
.returning_all()
|
||||||
@@ -333,8 +334,8 @@ pub async fn upsert_workspace_meta<R: Runtime>(
|
|||||||
.values_panic([
|
.values_panic([
|
||||||
id.as_str().into(),
|
id.as_str().into(),
|
||||||
workspace_meta.workspace_id.into(),
|
workspace_meta.workspace_id.into(),
|
||||||
CurrentTimestamp.into(),
|
timestamp_for_upsert(update_source, workspace_meta.created_at).into(),
|
||||||
CurrentTimestamp.into(),
|
timestamp_for_upsert(update_source, workspace_meta.updated_at).into(),
|
||||||
workspace_meta.setting_sync_dir.into(),
|
workspace_meta.setting_sync_dir.into(),
|
||||||
])
|
])
|
||||||
.on_conflict(
|
.on_conflict(
|
||||||
@@ -500,8 +501,8 @@ pub async fn upsert_grpc_request<R: Runtime>(
|
|||||||
])
|
])
|
||||||
.values_panic([
|
.values_panic([
|
||||||
id.into(),
|
id.into(),
|
||||||
CurrentTimestamp.into(),
|
timestamp_for_upsert(update_source, request.created_at).into(),
|
||||||
CurrentTimestamp.into(),
|
timestamp_for_upsert(update_source, request.updated_at).into(),
|
||||||
trimmed_name.into(),
|
trimmed_name.into(),
|
||||||
request.description.into(),
|
request.description.into(),
|
||||||
request.workspace_id.into(),
|
request.workspace_id.into(),
|
||||||
@@ -612,8 +613,8 @@ pub async fn upsert_grpc_connection<R: Runtime>(
|
|||||||
])
|
])
|
||||||
.values_panic([
|
.values_panic([
|
||||||
id.as_str().into(),
|
id.as_str().into(),
|
||||||
CurrentTimestamp.into(),
|
timestamp_for_upsert(update_source, connection.created_at).into(),
|
||||||
CurrentTimestamp.into(),
|
timestamp_for_upsert(update_source, connection.updated_at).into(),
|
||||||
connection.workspace_id.as_str().into(),
|
connection.workspace_id.as_str().into(),
|
||||||
connection.request_id.as_str().into(),
|
connection.request_id.as_str().into(),
|
||||||
connection.service.as_str().into(),
|
connection.service.as_str().into(),
|
||||||
@@ -771,8 +772,8 @@ pub async fn upsert_grpc_event<R: Runtime>(
|
|||||||
])
|
])
|
||||||
.values_panic([
|
.values_panic([
|
||||||
id.as_str().into(),
|
id.as_str().into(),
|
||||||
CurrentTimestamp.into(),
|
timestamp_for_upsert(update_source, event.created_at).into(),
|
||||||
CurrentTimestamp.into(),
|
timestamp_for_upsert(update_source, event.updated_at).into(),
|
||||||
event.workspace_id.as_str().into(),
|
event.workspace_id.as_str().into(),
|
||||||
event.request_id.as_str().into(),
|
event.request_id.as_str().into(),
|
||||||
event.connection_id.as_str().into(),
|
event.connection_id.as_str().into(),
|
||||||
@@ -859,8 +860,8 @@ pub async fn upsert_cookie_jar<R: Runtime>(
|
|||||||
])
|
])
|
||||||
.values_panic([
|
.values_panic([
|
||||||
id.as_str().into(),
|
id.as_str().into(),
|
||||||
CurrentTimestamp.into(),
|
timestamp_for_upsert(update_source, cookie_jar.created_at).into(),
|
||||||
CurrentTimestamp.into(),
|
timestamp_for_upsert(update_source, cookie_jar.updated_at).into(),
|
||||||
cookie_jar.workspace_id.as_str().into(),
|
cookie_jar.workspace_id.as_str().into(),
|
||||||
trimmed_name.into(),
|
trimmed_name.into(),
|
||||||
serde_json::to_string(&cookie_jar.cookies)?.into(),
|
serde_json::to_string(&cookie_jar.cookies)?.into(),
|
||||||
@@ -1064,8 +1065,8 @@ pub async fn upsert_environment<R: Runtime>(
|
|||||||
])
|
])
|
||||||
.values_panic([
|
.values_panic([
|
||||||
id.as_str().into(),
|
id.as_str().into(),
|
||||||
CurrentTimestamp.into(),
|
timestamp_for_upsert(update_source, environment.created_at).into(),
|
||||||
CurrentTimestamp.into(),
|
timestamp_for_upsert(update_source, environment.updated_at).into(),
|
||||||
environment.environment_id.into(),
|
environment.environment_id.into(),
|
||||||
environment.workspace_id.into(),
|
environment.workspace_id.into(),
|
||||||
trimmed_name.into(),
|
trimmed_name.into(),
|
||||||
@@ -1174,8 +1175,8 @@ pub async fn upsert_plugin<R: Runtime>(
|
|||||||
])
|
])
|
||||||
.values_panic([
|
.values_panic([
|
||||||
id.as_str().into(),
|
id.as_str().into(),
|
||||||
CurrentTimestamp.into(),
|
timestamp_for_upsert(update_source, plugin.created_at).into(),
|
||||||
CurrentTimestamp.into(),
|
timestamp_for_upsert(update_source, plugin.updated_at).into(),
|
||||||
plugin.checked_at.into(),
|
plugin.checked_at.into(),
|
||||||
plugin.directory.into(),
|
plugin.directory.into(),
|
||||||
plugin.url.into(),
|
plugin.url.into(),
|
||||||
@@ -1276,15 +1277,15 @@ pub async fn delete_folder<R: Runtime>(
|
|||||||
|
|
||||||
pub async fn upsert_folder<R: Runtime>(
|
pub async fn upsert_folder<R: Runtime>(
|
||||||
window: &WebviewWindow<R>,
|
window: &WebviewWindow<R>,
|
||||||
r: Folder,
|
folder: Folder,
|
||||||
|
|
||||||
update_source: &UpdateSource,
|
update_source: &UpdateSource,
|
||||||
) -> Result<Folder> {
|
) -> Result<Folder> {
|
||||||
let id = match r.id.as_str() {
|
let id = match folder.id.as_str() {
|
||||||
"" => generate_model_id(ModelType::TypeFolder),
|
"" => generate_model_id(ModelType::TypeFolder),
|
||||||
_ => r.id.to_string(),
|
_ => folder.id.to_string(),
|
||||||
};
|
};
|
||||||
let trimmed_name = r.name.trim();
|
let trimmed_name = folder.name.trim();
|
||||||
|
|
||||||
let dbm = &*window.app_handle().state::<SqliteConnection>();
|
let dbm = &*window.app_handle().state::<SqliteConnection>();
|
||||||
let db = dbm.0.lock().await.get().unwrap();
|
let db = dbm.0.lock().await.get().unwrap();
|
||||||
@@ -1303,13 +1304,13 @@ pub async fn upsert_folder<R: Runtime>(
|
|||||||
])
|
])
|
||||||
.values_panic([
|
.values_panic([
|
||||||
id.as_str().into(),
|
id.as_str().into(),
|
||||||
CurrentTimestamp.into(),
|
timestamp_for_upsert(update_source, folder.created_at).into(),
|
||||||
CurrentTimestamp.into(),
|
timestamp_for_upsert(update_source, folder.updated_at).into(),
|
||||||
r.workspace_id.as_str().into(),
|
folder.workspace_id.as_str().into(),
|
||||||
r.folder_id.as_ref().map(|s| s.as_str()).into(),
|
folder.folder_id.as_ref().map(|s| s.as_str()).into(),
|
||||||
trimmed_name.into(),
|
trimmed_name.into(),
|
||||||
r.description.into(),
|
folder.description.into(),
|
||||||
r.sort_priority.into(),
|
folder.sort_priority.into(),
|
||||||
])
|
])
|
||||||
.on_conflict(
|
.on_conflict(
|
||||||
OnConflict::column(GrpcEventIden::Id)
|
OnConflict::column(GrpcEventIden::Id)
|
||||||
@@ -1419,14 +1420,14 @@ pub async fn duplicate_folder<R: Runtime>(
|
|||||||
|
|
||||||
pub async fn upsert_http_request<R: Runtime>(
|
pub async fn upsert_http_request<R: Runtime>(
|
||||||
window: &WebviewWindow<R>,
|
window: &WebviewWindow<R>,
|
||||||
r: HttpRequest,
|
request: HttpRequest,
|
||||||
update_source: &UpdateSource,
|
update_source: &UpdateSource,
|
||||||
) -> Result<HttpRequest> {
|
) -> Result<HttpRequest> {
|
||||||
let id = match r.id.as_str() {
|
let id = match request.id.as_str() {
|
||||||
"" => generate_model_id(ModelType::TypeHttpRequest),
|
"" => generate_model_id(ModelType::TypeHttpRequest),
|
||||||
_ => r.id.to_string(),
|
_ => request.id.to_string(),
|
||||||
};
|
};
|
||||||
let trimmed_name = r.name.trim();
|
let trimmed_name = request.name.trim();
|
||||||
|
|
||||||
let dbm = &*window.app_handle().state::<SqliteConnection>();
|
let dbm = &*window.app_handle().state::<SqliteConnection>();
|
||||||
let db = dbm.0.lock().await.get().unwrap();
|
let db = dbm.0.lock().await.get().unwrap();
|
||||||
@@ -1453,21 +1454,21 @@ pub async fn upsert_http_request<R: Runtime>(
|
|||||||
])
|
])
|
||||||
.values_panic([
|
.values_panic([
|
||||||
id.as_str().into(),
|
id.as_str().into(),
|
||||||
CurrentTimestamp.into(),
|
timestamp_for_upsert(update_source, request.created_at).into(),
|
||||||
CurrentTimestamp.into(),
|
timestamp_for_upsert(update_source, request.updated_at).into(),
|
||||||
r.workspace_id.into(),
|
request.workspace_id.into(),
|
||||||
r.folder_id.as_ref().map(|s| s.as_str()).into(),
|
request.folder_id.as_ref().map(|s| s.as_str()).into(),
|
||||||
trimmed_name.into(),
|
trimmed_name.into(),
|
||||||
r.description.into(),
|
request.description.into(),
|
||||||
r.url.into(),
|
request.url.into(),
|
||||||
serde_json::to_string(&r.url_parameters)?.into(),
|
serde_json::to_string(&request.url_parameters)?.into(),
|
||||||
r.method.into(),
|
request.method.into(),
|
||||||
serde_json::to_string(&r.body)?.into(),
|
serde_json::to_string(&request.body)?.into(),
|
||||||
r.body_type.as_ref().map(|s| s.as_str()).into(),
|
request.body_type.as_ref().map(|s| s.as_str()).into(),
|
||||||
serde_json::to_string(&r.authentication)?.into(),
|
serde_json::to_string(&request.authentication)?.into(),
|
||||||
r.authentication_type.as_ref().map(|s| s.as_str()).into(),
|
request.authentication_type.as_ref().map(|s| s.as_str()).into(),
|
||||||
serde_json::to_string(&r.headers)?.into(),
|
serde_json::to_string(&request.headers)?.into(),
|
||||||
r.sort_priority.into(),
|
request.sort_priority.into(),
|
||||||
])
|
])
|
||||||
.on_conflict(
|
.on_conflict(
|
||||||
OnConflict::column(GrpcEventIden::Id)
|
OnConflict::column(GrpcEventIden::Id)
|
||||||
@@ -2167,7 +2168,7 @@ pub async fn get_workspace_export_resources<R: Runtime>(
|
|||||||
let mut data = WorkspaceExport {
|
let mut data = WorkspaceExport {
|
||||||
yaak_version: mgr.package_info().version.clone().to_string(),
|
yaak_version: mgr.package_info().version.clone().to_string(),
|
||||||
yaak_schema: 2,
|
yaak_schema: 2,
|
||||||
timestamp: chrono::Utc::now().naive_utc(),
|
timestamp: Utc::now().naive_utc(),
|
||||||
resources: BatchUpsertResult {
|
resources: BatchUpsertResult {
|
||||||
workspaces: Vec::new(),
|
workspaces: Vec::new(),
|
||||||
environments: Vec::new(),
|
environments: Vec::new(),
|
||||||
@@ -2197,3 +2198,21 @@ pub async fn get_workspace_export_resources<R: Runtime>(
|
|||||||
|
|
||||||
data
|
data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate the created_at or updated_at timestamps for an upsert operation, depending on the ID
|
||||||
|
// provided.
|
||||||
|
fn timestamp_for_upsert(update_source: &UpdateSource, dt: NaiveDateTime) -> NaiveDateTime {
|
||||||
|
match update_source {
|
||||||
|
// Sync and import operations always preserve timestamps
|
||||||
|
UpdateSource::Sync | UpdateSource::Import => {
|
||||||
|
if dt.and_utc().timestamp() == 0 {
|
||||||
|
// Sometimes data won't have timestamps (partial data)
|
||||||
|
Utc::now().naive_utc()
|
||||||
|
} else {
|
||||||
|
dt
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Other sources will always update to the latest time
|
||||||
|
_ => Utc::now().naive_utc(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::sync::{
|
use crate::sync::{
|
||||||
apply_sync_ops, apply_sync_state_ops, compute_sync_ops, get_db_candidates, get_fs_candidates,
|
apply_sync_ops, apply_sync_state_ops, compute_sync_ops, get_db_candidates, get_fs_candidates,
|
||||||
SyncOp,
|
FsCandidate, SyncOp,
|
||||||
};
|
};
|
||||||
use crate::watch::{watch_directory, WatchEvent};
|
use crate::watch::{watch_directory, WatchEvent};
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
@@ -23,16 +23,18 @@ pub async fn calculate<R: Runtime>(
|
|||||||
let fs_candidates = get_fs_candidates(sync_dir)
|
let fs_candidates = get_fs_candidates(sync_dir)
|
||||||
.await?
|
.await?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
// Strip out any non-workspace candidates
|
// Only keep items in the same workspace
|
||||||
.filter(|fs| fs.model.workspace_id() == workspace_id)
|
.filter(|fs| fs.model.workspace_id() == workspace_id)
|
||||||
.collect();
|
.collect::<Vec<FsCandidate>>();
|
||||||
|
// println!("\ndb_candidates: \n{}\n", serde_json::to_string_pretty(&db_candidates)?);
|
||||||
|
// println!("\nfs_candidates: \n{}\n", serde_json::to_string_pretty(&fs_candidates)?);
|
||||||
Ok(compute_sync_ops(db_candidates, fs_candidates))
|
Ok(compute_sync_ops(db_candidates, fs_candidates))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
pub async fn calculate_fs(dir: &Path) -> Result<Vec<SyncOp>> {
|
pub async fn calculate_fs(dir: &Path) -> Result<Vec<SyncOp>> {
|
||||||
let db_candidates = Vec::new();
|
let db_candidates = Vec::new();
|
||||||
let fs_candidates = get_fs_candidates(Path::new(&dir)).await?;
|
let fs_candidates = get_fs_candidates(dir).await?;
|
||||||
Ok(compute_sync_ops(db_candidates, fs_candidates))
|
Ok(compute_sync_ops(db_candidates, fs_candidates))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,8 @@ impl Display for SyncOp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
pub(crate) enum DbCandidate {
|
pub(crate) enum DbCandidate {
|
||||||
Added(SyncModel),
|
Added(SyncModel),
|
||||||
Modified(SyncModel, SyncState),
|
Modified(SyncModel, SyncState),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { Folder, Workspace } from '@yaakapp-internal/models';
|
import type { Folder } from '@yaakapp-internal/models';
|
||||||
import { applySync, calculateSync } from '@yaakapp-internal/sync';
|
import { applySync, calculateSync } from '@yaakapp-internal/sync';
|
||||||
import { Banner } from '../components/core/Banner';
|
import { Banner } from '../components/core/Banner';
|
||||||
import { InlineCode } from '../components/core/InlineCode';
|
import { InlineCode } from '../components/core/InlineCode';
|
||||||
@@ -10,21 +10,8 @@ import { showConfirm } from '../lib/confirm';
|
|||||||
import { fallbackRequestName } from '../lib/fallbackRequestName';
|
import { fallbackRequestName } from '../lib/fallbackRequestName';
|
||||||
import { pluralizeCount } from '../lib/pluralize';
|
import { pluralizeCount } from '../lib/pluralize';
|
||||||
import { showPrompt } from '../lib/prompt';
|
import { showPrompt } from '../lib/prompt';
|
||||||
import { router } from '../lib/router';
|
|
||||||
import { invokeCmd } from '../lib/tauri';
|
import { invokeCmd } from '../lib/tauri';
|
||||||
|
|
||||||
export const createWorkspace = createFastMutation<Workspace, void, Partial<Workspace>>({
|
|
||||||
mutationKey: ['create_workspace'],
|
|
||||||
mutationFn: (patch) => invokeCmd<Workspace>('cmd_update_workspace', { workspace: patch }),
|
|
||||||
onSuccess: async (workspace) => {
|
|
||||||
await router.navigate({
|
|
||||||
to: '/workspaces/$workspaceId',
|
|
||||||
params: { workspaceId: workspace.id },
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onSettled: () => trackEvent('workspace', 'create'),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const createFolder = createFastMutation<
|
export const createFolder = createFastMutation<
|
||||||
Folder | null,
|
Folder | null,
|
||||||
void,
|
void,
|
||||||
@@ -66,8 +53,10 @@ export const syncWorkspace = createFastMutation<
|
|||||||
mutationFn: async ({ workspaceId, syncDir }) => {
|
mutationFn: async ({ workspaceId, syncDir }) => {
|
||||||
const ops = (await calculateSync(workspaceId, syncDir)) ?? [];
|
const ops = (await calculateSync(workspaceId, syncDir)) ?? [];
|
||||||
if (ops.length === 0) {
|
if (ops.length === 0) {
|
||||||
|
console.log('Nothing to sync', workspaceId, syncDir, ops);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
console.log('syncing workspace', workspaceId, syncDir, ops);
|
||||||
|
|
||||||
const dbOps = ops.filter((o) => o.type.startsWith('db'));
|
const dbOps = ops.filter((o) => o.type.startsWith('db'));
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { createFastMutation } from '../hooks/useFastMutation';
|
|||||||
import { showSimpleAlert } from '../lib/alert';
|
import { showSimpleAlert } from '../lib/alert';
|
||||||
import { router } from '../lib/router';
|
import { router } from '../lib/router';
|
||||||
|
|
||||||
export const openWorkspace = createFastMutation({
|
export const openWorkspaceFromSyncDir = createFastMutation({
|
||||||
mutationKey: [],
|
mutationKey: [],
|
||||||
mutationFn: async () => {
|
mutationFn: async () => {
|
||||||
const dir = await open({
|
const dir = await open({
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import { createFastMutation } from '../hooks/useFastMutation';
|
||||||
|
import { getRecentCookieJars } from '../hooks/useRecentCookieJars';
|
||||||
|
import { getRecentEnvironments } from '../hooks/useRecentEnvironments';
|
||||||
|
import { getRecentRequests } from '../hooks/useRecentRequests';
|
||||||
|
import { router } from '../lib/router';
|
||||||
|
import { invokeCmd } from '../lib/tauri';
|
||||||
|
|
||||||
|
export const switchWorkspace = createFastMutation({
|
||||||
|
mutationKey: ['open_workspace'],
|
||||||
|
mutationFn: async ({
|
||||||
|
workspaceId,
|
||||||
|
inNewWindow,
|
||||||
|
}: {
|
||||||
|
workspaceId: string;
|
||||||
|
inNewWindow: boolean;
|
||||||
|
}) => {
|
||||||
|
const environmentId = (await getRecentEnvironments(workspaceId))[0] ?? undefined;
|
||||||
|
const requestId = (await getRecentRequests(workspaceId))[0] ?? undefined;
|
||||||
|
const cookieJarId = (await getRecentCookieJars(workspaceId))[0] ?? undefined;
|
||||||
|
const search = {
|
||||||
|
environment_id: environmentId,
|
||||||
|
cookie_jar_id: cookieJarId,
|
||||||
|
request_id: requestId,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (inNewWindow) {
|
||||||
|
const location = router.buildLocation({
|
||||||
|
to: '/workspaces/$workspaceId',
|
||||||
|
params: { workspaceId },
|
||||||
|
search,
|
||||||
|
});
|
||||||
|
await invokeCmd<void>('cmd_new_main_window', { url: location.href });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await router.navigate({
|
||||||
|
to: '/workspaces/$workspaceId',
|
||||||
|
params: { workspaceId },
|
||||||
|
search,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import type { Workspace } from '@yaakapp-internal/models';
|
||||||
|
import { createFastMutation } from '../hooks/useFastMutation';
|
||||||
|
import { trackEvent } from '../lib/analytics';
|
||||||
|
import { invokeCmd } from '../lib/tauri';
|
||||||
|
|
||||||
|
export const upsertWorkspace = createFastMutation<
|
||||||
|
Workspace,
|
||||||
|
void,
|
||||||
|
Workspace | Partial<Omit<Workspace, 'id'>>
|
||||||
|
>({
|
||||||
|
mutationKey: ['upsert_workspace'],
|
||||||
|
mutationFn: (workspace) => invokeCmd<Workspace>('cmd_update_workspace', { workspace }),
|
||||||
|
onSuccess: async (workspace) => {
|
||||||
|
const isNew = workspace.createdAt == workspace.updatedAt;
|
||||||
|
|
||||||
|
if (isNew) trackEvent('workspace', 'create');
|
||||||
|
else trackEvent('workspace', 'update');
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -7,7 +7,7 @@ import { invokeCmd } from '../lib/tauri';
|
|||||||
export const upsertWorkspaceMeta = createFastMutation<
|
export const upsertWorkspaceMeta = createFastMutation<
|
||||||
WorkspaceMeta,
|
WorkspaceMeta,
|
||||||
unknown,
|
unknown,
|
||||||
Partial<WorkspaceMeta>
|
WorkspaceMeta | (Partial<Omit<WorkspaceMeta, 'id'>> & { workspaceId: string })
|
||||||
>({
|
>({
|
||||||
mutationKey: ['update_workspace_meta'],
|
mutationKey: ['update_workspace_meta'],
|
||||||
mutationFn: async (patch) => {
|
mutationFn: async (patch) => {
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import classNames from 'classnames';
|
|||||||
import { fuzzyFilter } from 'fuzzbunny';
|
import { fuzzyFilter } from 'fuzzbunny';
|
||||||
import type { KeyboardEvent, ReactNode } from 'react';
|
import type { KeyboardEvent, ReactNode } from 'react';
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
import { createFolder } from '../commands/commands';
|
||||||
|
import { switchWorkspace } from '../commands/switchWorkspace';
|
||||||
import { useActiveCookieJar } from '../hooks/useActiveCookieJar';
|
import { useActiveCookieJar } from '../hooks/useActiveCookieJar';
|
||||||
import { useActiveEnvironment } from '../hooks/useActiveEnvironment';
|
import { useActiveEnvironment } from '../hooks/useActiveEnvironment';
|
||||||
import { useActiveRequest } from '../hooks/useActiveRequest';
|
import { useActiveRequest } from '../hooks/useActiveRequest';
|
||||||
@@ -16,7 +18,6 @@ import type { HotkeyAction } from '../hooks/useHotKey';
|
|||||||
import { useHotKey } from '../hooks/useHotKey';
|
import { useHotKey } from '../hooks/useHotKey';
|
||||||
import { useHttpRequestActions } from '../hooks/useHttpRequestActions';
|
import { useHttpRequestActions } from '../hooks/useHttpRequestActions';
|
||||||
import { useOpenSettings } from '../hooks/useOpenSettings';
|
import { useOpenSettings } from '../hooks/useOpenSettings';
|
||||||
import { useSwitchWorkspace } from '../hooks/useSwitchWorkspace';
|
|
||||||
import { useRecentEnvironments } from '../hooks/useRecentEnvironments';
|
import { useRecentEnvironments } from '../hooks/useRecentEnvironments';
|
||||||
import { useRecentRequests } from '../hooks/useRecentRequests';
|
import { useRecentRequests } from '../hooks/useRecentRequests';
|
||||||
import { useRecentWorkspaces } from '../hooks/useRecentWorkspaces';
|
import { useRecentWorkspaces } from '../hooks/useRecentWorkspaces';
|
||||||
@@ -38,7 +39,6 @@ import { Icon } from './core/Icon';
|
|||||||
import { PlainInput } from './core/PlainInput';
|
import { PlainInput } from './core/PlainInput';
|
||||||
import { HStack } from './core/Stacks';
|
import { HStack } from './core/Stacks';
|
||||||
import { EnvironmentEditDialog } from './EnvironmentEditDialog';
|
import { EnvironmentEditDialog } from './EnvironmentEditDialog';
|
||||||
import { createFolder } from '../commands/commands';
|
|
||||||
|
|
||||||
interface CommandPaletteGroup {
|
interface CommandPaletteGroup {
|
||||||
key: string;
|
key: string;
|
||||||
@@ -71,7 +71,6 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) {
|
|||||||
const [, setSidebarHidden] = useSidebarHidden();
|
const [, setSidebarHidden] = useSidebarHidden();
|
||||||
const { baseEnvironment } = useEnvironments();
|
const { baseEnvironment } = useEnvironments();
|
||||||
const { mutate: openSettings } = useOpenSettings();
|
const { mutate: openSettings } = useOpenSettings();
|
||||||
const { mutate: switchWorkspace } = useSwitchWorkspace();
|
|
||||||
const { mutate: createHttpRequest } = useCreateHttpRequest();
|
const { mutate: createHttpRequest } = useCreateHttpRequest();
|
||||||
const { mutate: createGrpcRequest } = useCreateGrpcRequest();
|
const { mutate: createGrpcRequest } = useCreateGrpcRequest();
|
||||||
const { mutate: createEnvironment } = useCreateEnvironment();
|
const { mutate: createEnvironment } = useCreateEnvironment();
|
||||||
@@ -315,7 +314,7 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) {
|
|||||||
workspaceGroup.items.push({
|
workspaceGroup.items.push({
|
||||||
key: `switch-workspace-${w.id}`,
|
key: `switch-workspace-${w.id}`,
|
||||||
label: w.name,
|
label: w.name,
|
||||||
onSelect: () => switchWorkspace({ workspaceId: w.id, inNewWindow: false }),
|
onSelect: () => switchWorkspace.mutate({ workspaceId: w.id, inNewWindow: false }),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,7 +326,6 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) {
|
|||||||
activeEnvironment?.id,
|
activeEnvironment?.id,
|
||||||
setActiveEnvironmentId,
|
setActiveEnvironmentId,
|
||||||
sortedWorkspaces,
|
sortedWorkspaces,
|
||||||
switchWorkspace,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const allItems = useMemo(() => groups.flatMap((g) => g.items), [groups]);
|
const allItems = useMemo(() => groups.flatMap((g) => g.items), [groups]);
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ export const CookieDropdown = memo(function CookieDropdown() {
|
|||||||
key: 'delete',
|
key: 'delete',
|
||||||
label: 'Delete',
|
label: 'Delete',
|
||||||
leftSlot: <Icon icon="trash" />,
|
leftSlot: <Icon icon="trash" />,
|
||||||
variant: 'danger',
|
color: 'danger',
|
||||||
onSelect: () => deleteCookieJar.mutateAsync(),
|
onSelect: () => deleteCookieJar.mutateAsync(),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { createWorkspace } from '../commands/commands';
|
import { upsertWorkspace } from '../commands/upsertWorkspace';
|
||||||
|
import { upsertWorkspaceMeta } from '../commands/upsertWorkspaceMeta';
|
||||||
|
import { router } from '../lib/router';
|
||||||
|
import { getWorkspaceMeta } from '../lib/store';
|
||||||
import { Button } from './core/Button';
|
import { Button } from './core/Button';
|
||||||
import { PlainInput } from './core/PlainInput';
|
import { PlainInput } from './core/PlainInput';
|
||||||
import { VStack } from './core/Stacks';
|
import { VStack } from './core/Stacks';
|
||||||
@@ -26,7 +29,20 @@ export function CreateWorkspaceDialog({ hide }: Props) {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const { enabled, value } = settingSyncDir ?? {};
|
const { enabled, value } = settingSyncDir ?? {};
|
||||||
if (enabled && !value) return;
|
if (enabled && !value) return;
|
||||||
await createWorkspace.mutateAsync({ name, settingSyncDir: value });
|
const workspace = await upsertWorkspace.mutateAsync({ name });
|
||||||
|
if (workspace == null) return;
|
||||||
|
|
||||||
|
// Do getWorkspaceMeta instead of naively creating one because it might have
|
||||||
|
// been created already when the store refreshes the workspace meta after
|
||||||
|
const workspaceMeta = await getWorkspaceMeta(workspace.id);
|
||||||
|
upsertWorkspaceMeta.mutate({ ...workspaceMeta, settingSyncDir: value });
|
||||||
|
|
||||||
|
// Navigate to workspace
|
||||||
|
await router.navigate({
|
||||||
|
to: '/workspaces/$workspaceId',
|
||||||
|
params: { workspaceId: workspace.id },
|
||||||
|
});
|
||||||
|
|
||||||
hide();
|
hide();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -278,7 +278,7 @@ function SidebarButton({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'delete-environment',
|
key: 'delete-environment',
|
||||||
variant: 'danger',
|
color: 'danger',
|
||||||
label: 'Delete',
|
label: 'Delete',
|
||||||
leftSlot: <Icon icon="trash" size="sm" />,
|
leftSlot: <Icon icon="trash" size="sm" />,
|
||||||
onSelect: () => deleteEnvironment.mutate(),
|
onSelect: () => deleteEnvironment.mutate(),
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ export function GraphQLEditor({ request, onChange, baseRequest, ...extraEditorPr
|
|||||||
label: 'Clear',
|
label: 'Clear',
|
||||||
onSelect: clear,
|
onSelect: clear,
|
||||||
hidden: !schema,
|
hidden: !schema,
|
||||||
variant: 'danger',
|
color: 'danger',
|
||||||
leftSlot: <Icon icon="trash" />,
|
leftSlot: <Icon icon="trash" />,
|
||||||
},
|
},
|
||||||
{ type: 'separator', label: 'Setting' },
|
{ type: 'separator', label: 'Setting' },
|
||||||
|
|||||||
@@ -156,7 +156,9 @@ export function GrpcConnectionMessagesPane({ style, methodType, activeRequest }:
|
|||||||
) : (
|
) : (
|
||||||
<KeyValueRows>
|
<KeyValueRows>
|
||||||
{Object.entries(activeEvent.metadata).map(([key, value]) => (
|
{Object.entries(activeEvent.metadata).map(([key, value]) => (
|
||||||
<KeyValueRow key={key} label={key} value={value} />
|
<KeyValueRow key={key} label={key}>
|
||||||
|
{value}
|
||||||
|
</KeyValueRow>
|
||||||
))}
|
))}
|
||||||
</KeyValueRows>
|
</KeyValueRows>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ export function ResponseHeaders({ response }: Props) {
|
|||||||
<div className="overflow-auto h-full pb-4">
|
<div className="overflow-auto h-full pb-4">
|
||||||
<KeyValueRows>
|
<KeyValueRows>
|
||||||
{response.headers.map((h, i) => (
|
{response.headers.map((h, i) => (
|
||||||
<KeyValueRow labelColor="primary" key={i} label={h.name} value={h.value} />
|
<KeyValueRow labelColor="primary" key={i} label={h.name}>
|
||||||
|
{h.value}
|
||||||
|
</KeyValueRow>
|
||||||
))}
|
))}
|
||||||
</KeyValueRows>
|
</KeyValueRows>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -11,8 +11,12 @@ export function ResponseInfo({ response }: Props) {
|
|||||||
return (
|
return (
|
||||||
<div className="overflow-auto h-full pb-4">
|
<div className="overflow-auto h-full pb-4">
|
||||||
<KeyValueRows>
|
<KeyValueRows>
|
||||||
<KeyValueRow labelColor="info" label="Version" value={response.version} />
|
<KeyValueRow labelColor="info" label="Version">
|
||||||
<KeyValueRow labelColor="info" label="Remote Address" value={response.remoteAddr} />
|
{response.version}
|
||||||
|
</KeyValueRow>
|
||||||
|
<KeyValueRow labelColor="info" label="Remote Address">
|
||||||
|
{response.remoteAddr}
|
||||||
|
</KeyValueRow>
|
||||||
<KeyValueRow
|
<KeyValueRow
|
||||||
labelColor="info"
|
labelColor="info"
|
||||||
label={
|
label={
|
||||||
@@ -27,12 +31,13 @@ export function ResponseInfo({ response }: Props) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
value={
|
>
|
||||||
|
{
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<span className="select-text cursor-text">{response.url}</span>
|
<span className="select-text cursor-text">{response.url}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
</KeyValueRow>
|
||||||
</KeyValueRows>
|
</KeyValueRows>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
|
import { revealItemInDir } from '@tauri-apps/plugin-opener';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { upsertWorkspace } from '../../commands/upsertWorkspace';
|
||||||
import { useActiveWorkspace } from '../../hooks/useActiveWorkspace';
|
import { useActiveWorkspace } from '../../hooks/useActiveWorkspace';
|
||||||
import { useAppInfo } from '../../hooks/useAppInfo';
|
import { useAppInfo } from '../../hooks/useAppInfo';
|
||||||
import { useCheckForUpdates } from '../../hooks/useCheckForUpdates';
|
import { useCheckForUpdates } from '../../hooks/useCheckForUpdates';
|
||||||
import { useSettings } from '../../hooks/useSettings';
|
import { useSettings } from '../../hooks/useSettings';
|
||||||
import { useUpdateSettings } from '../../hooks/useUpdateSettings';
|
import { useUpdateSettings } from '../../hooks/useUpdateSettings';
|
||||||
import { useUpdateWorkspace } from '../../hooks/useUpdateWorkspace';
|
import { revealInFinderText } from '../../lib/reveal';
|
||||||
import { Checkbox } from '../core/Checkbox';
|
import { Checkbox } from '../core/Checkbox';
|
||||||
import { Heading } from '../core/Heading';
|
import { Heading } from '../core/Heading';
|
||||||
import { IconButton } from '../core/IconButton';
|
import { IconButton } from '../core/IconButton';
|
||||||
@@ -16,7 +18,6 @@ import { VStack } from '../core/Stacks';
|
|||||||
|
|
||||||
export function SettingsGeneral() {
|
export function SettingsGeneral() {
|
||||||
const workspace = useActiveWorkspace();
|
const workspace = useActiveWorkspace();
|
||||||
const updateWorkspace = useUpdateWorkspace(workspace?.id ?? null);
|
|
||||||
const settings = useSettings();
|
const settings = useSettings();
|
||||||
const updateSettings = useUpdateSettings();
|
const updateSettings = useUpdateSettings();
|
||||||
const appInfo = useAppInfo();
|
const appInfo = useAppInfo();
|
||||||
@@ -103,7 +104,9 @@ export function SettingsGeneral() {
|
|||||||
labelPosition="left"
|
labelPosition="left"
|
||||||
defaultValue={`${workspace.settingRequestTimeout}`}
|
defaultValue={`${workspace.settingRequestTimeout}`}
|
||||||
validate={(value) => parseInt(value) >= 0}
|
validate={(value) => parseInt(value) >= 0}
|
||||||
onChange={(v) => updateWorkspace.mutate({ settingRequestTimeout: parseInt(v) || 0 })}
|
onChange={(v) =>
|
||||||
|
upsertWorkspace.mutate({ ...workspace, settingRequestTimeout: parseInt(v) || 0 })
|
||||||
|
}
|
||||||
type="number"
|
type="number"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -112,7 +115,7 @@ export function SettingsGeneral() {
|
|||||||
title="Validate TLS Certificates"
|
title="Validate TLS Certificates"
|
||||||
event="validate-certs"
|
event="validate-certs"
|
||||||
onChange={(settingValidateCertificates) =>
|
onChange={(settingValidateCertificates) =>
|
||||||
updateWorkspace.mutate({ settingValidateCertificates })
|
upsertWorkspace.mutate({ ...workspace, settingValidateCertificates })
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -120,7 +123,12 @@ export function SettingsGeneral() {
|
|||||||
checked={workspace.settingFollowRedirects}
|
checked={workspace.settingFollowRedirects}
|
||||||
title="Follow Redirects"
|
title="Follow Redirects"
|
||||||
event="follow-redirects"
|
event="follow-redirects"
|
||||||
onChange={(settingFollowRedirects) => updateWorkspace.mutate({ settingFollowRedirects })}
|
onChange={(settingFollowRedirects) =>
|
||||||
|
upsertWorkspace.mutate({
|
||||||
|
...workspace,
|
||||||
|
settingFollowRedirects,
|
||||||
|
})
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</VStack>
|
</VStack>
|
||||||
|
|
||||||
@@ -128,9 +136,33 @@ export function SettingsGeneral() {
|
|||||||
|
|
||||||
<Heading size={2}>App Info</Heading>
|
<Heading size={2}>App Info</Heading>
|
||||||
<KeyValueRows>
|
<KeyValueRows>
|
||||||
<KeyValueRow label="Version" value={appInfo.version} />
|
<KeyValueRow label="Version">{appInfo.version}</KeyValueRow>
|
||||||
<KeyValueRow label="Data Directory" value={appInfo.appDataDir} />
|
<KeyValueRow
|
||||||
<KeyValueRow label="Logs Directory" value={appInfo.appLogDir} />
|
label="Data Directory"
|
||||||
|
rightSlot={
|
||||||
|
<IconButton
|
||||||
|
title={revealInFinderText}
|
||||||
|
icon="folder_open"
|
||||||
|
size="2xs"
|
||||||
|
onClick={() => revealItemInDir(appInfo.appDataDir)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{appInfo.appDataDir}
|
||||||
|
</KeyValueRow>
|
||||||
|
<KeyValueRow
|
||||||
|
label="Logs Directory"
|
||||||
|
rightSlot={
|
||||||
|
<IconButton
|
||||||
|
title={revealInFinderText}
|
||||||
|
icon="folder_open"
|
||||||
|
size="2xs"
|
||||||
|
onClick={() => revealItemInDir(appInfo.appLogDir)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{appInfo.appLogDir}
|
||||||
|
</KeyValueRow>
|
||||||
</KeyValueRows>
|
</KeyValueRows>
|
||||||
</VStack>
|
</VStack>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ export function SidebarItemContextMenu({ child, show, close }: Props) {
|
|||||||
{
|
{
|
||||||
key: 'delete-folder',
|
key: 'delete-folder',
|
||||||
label: 'Delete',
|
label: 'Delete',
|
||||||
variant: 'danger',
|
color: 'danger',
|
||||||
leftSlot: <Icon icon="trash" />,
|
leftSlot: <Icon icon="trash" />,
|
||||||
onSelect: () => deleteFolder.mutate(),
|
onSelect: () => deleteFolder.mutate(),
|
||||||
},
|
},
|
||||||
@@ -132,7 +132,7 @@ export function SidebarItemContextMenu({ child, show, close }: Props) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'delete-request',
|
key: 'delete-request',
|
||||||
variant: 'danger',
|
color: 'danger',
|
||||||
label: 'Delete',
|
label: 'Delete',
|
||||||
hotKeyAction: 'http_request.delete',
|
hotKeyAction: 'http_request.delete',
|
||||||
hotKeyLabelOnly: true,
|
hotKeyLabelOnly: true,
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
import type { Workspace } from '@yaakapp-internal/models';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useSwitchWorkspace } from '../hooks/useSwitchWorkspace';
|
import { switchWorkspace } from '../commands/switchWorkspace';
|
||||||
import { useSettings } from '../hooks/useSettings';
|
import { useSettings } from '../hooks/useSettings';
|
||||||
import { useUpdateSettings } from '../hooks/useUpdateSettings';
|
import { useUpdateSettings } from '../hooks/useUpdateSettings';
|
||||||
import type { Workspace } from '@yaakapp-internal/models';
|
|
||||||
import { Button } from './core/Button';
|
import { Button } from './core/Button';
|
||||||
import { Checkbox } from './core/Checkbox';
|
import { Checkbox } from './core/Checkbox';
|
||||||
import { Icon } from './core/Icon';
|
import { Icon } from './core/Icon';
|
||||||
@@ -15,7 +15,6 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function SwitchWorkspaceDialog({ hide, workspace }: Props) {
|
export function SwitchWorkspaceDialog({ hide, workspace }: Props) {
|
||||||
const switchWorkspace = useSwitchWorkspace();
|
|
||||||
const settings = useSettings();
|
const settings = useSettings();
|
||||||
const updateSettings = useUpdateSettings();
|
const updateSettings = useUpdateSettings();
|
||||||
const [remember, setRemember] = useState<boolean>(false);
|
const [remember, setRemember] = useState<boolean>(false);
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
|
import { revealItemInDir } from '@tauri-apps/plugin-opener';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
import {openWorkspace} from "../commands/openWorkspace";
|
import { openWorkspaceFromSyncDir } from '../commands/openWorkspaceFromSyncDir';
|
||||||
|
import { switchWorkspace } from '../commands/switchWorkspace';
|
||||||
import { useActiveWorkspace } from '../hooks/useActiveWorkspace';
|
import { useActiveWorkspace } from '../hooks/useActiveWorkspace';
|
||||||
import { useCreateWorkspace } from '../hooks/useCreateWorkspace';
|
import { useCreateWorkspace } from '../hooks/useCreateWorkspace';
|
||||||
import { useDeleteSendHistory } from '../hooks/useDeleteSendHistory';
|
import { useDeleteSendHistory } from '../hooks/useDeleteSendHistory';
|
||||||
import { useSwitchWorkspace } from '../hooks/useSwitchWorkspace';
|
import { settingsAtom } from '../hooks/useSettings';
|
||||||
import { useSettings } from '../hooks/useSettings';
|
import { useWorkspaceMeta } from '../hooks/useWorkspaceMeta';
|
||||||
import { useWorkspaces } from '../hooks/useWorkspaces';
|
import { useWorkspaces } from '../hooks/useWorkspaces';
|
||||||
import { showDialog } from '../lib/dialog';
|
import { showDialog } from '../lib/dialog';
|
||||||
|
import { jotaiStore } from '../lib/jotai';
|
||||||
|
import { revealInFinderText } from '../lib/reveal';
|
||||||
import { getWorkspace } from '../lib/store';
|
import { getWorkspace } from '../lib/store';
|
||||||
import type { ButtonProps } from './core/Button';
|
import type { ButtonProps } from './core/Button';
|
||||||
import { Button } from './core/Button';
|
import { Button } from './core/Button';
|
||||||
@@ -25,12 +29,10 @@ export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({
|
|||||||
...buttonProps
|
...buttonProps
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const workspaces = useWorkspaces();
|
const workspaces = useWorkspaces();
|
||||||
const activeWorkspace = useActiveWorkspace();
|
const workspace = useActiveWorkspace();
|
||||||
const createWorkspace = useCreateWorkspace();
|
const createWorkspace = useCreateWorkspace();
|
||||||
|
const workspaceMeta = useWorkspaceMeta();
|
||||||
const { mutate: deleteSendHistory } = useDeleteSendHistory();
|
const { mutate: deleteSendHistory } = useDeleteSendHistory();
|
||||||
const settings = useSettings();
|
|
||||||
const switchWorkspace = useSwitchWorkspace();
|
|
||||||
const openWorkspaceNewWindow = settings?.openWorkspaceNewWindow ?? null;
|
|
||||||
|
|
||||||
const orderedWorkspaces = useMemo(
|
const orderedWorkspaces = useMemo(
|
||||||
() => [...workspaces].sort((a, b) => (a.name.localeCompare(b.name) > 0 ? 1 : -1)),
|
() => [...workspaces].sort((a, b) => (a.name.localeCompare(b.name) > 0 ? 1 : -1)),
|
||||||
@@ -45,7 +47,7 @@ export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({
|
|||||||
key: w.id,
|
key: w.id,
|
||||||
label: w.name,
|
label: w.name,
|
||||||
value: w.id,
|
value: w.id,
|
||||||
leftSlot: w.id === activeWorkspace?.id ? <Icon icon="check" /> : <Icon icon="empty" />,
|
leftSlot: w.id === workspace?.id ? <Icon icon="check" /> : <Icon icon="empty" />,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const extraItems: DropdownItem[] = [
|
const extraItems: DropdownItem[] = [
|
||||||
@@ -60,14 +62,25 @@ export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({
|
|||||||
title: 'Workspace Settings',
|
title: 'Workspace Settings',
|
||||||
size: 'md',
|
size: 'md',
|
||||||
render: ({ hide }) => (
|
render: ({ hide }) => (
|
||||||
<WorkspaceSettingsDialog workspaceId={activeWorkspace?.id ?? null} hide={hide} />
|
<WorkspaceSettingsDialog workspaceId={workspace?.id ?? null} hide={hide} />
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'reveal-workspace-sync-dir',
|
||||||
|
label: revealInFinderText,
|
||||||
|
hidden: workspaceMeta == null || workspaceMeta.settingSyncDir == null,
|
||||||
|
leftSlot: <Icon icon="folder_open" />,
|
||||||
|
onSelect: async () => {
|
||||||
|
if (workspaceMeta?.settingSyncDir == null) return;
|
||||||
|
await revealItemInDir(workspaceMeta.settingSyncDir);
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'delete-responses',
|
key: 'delete-responses',
|
||||||
label: 'Clear Send History',
|
label: 'Clear Send History',
|
||||||
|
color: 'warning',
|
||||||
leftSlot: <Icon icon="history" />,
|
leftSlot: <Icon icon="history" />,
|
||||||
onSelect: deleteSendHistory,
|
onSelect: deleteSendHistory,
|
||||||
},
|
},
|
||||||
@@ -82,52 +95,50 @@ export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({
|
|||||||
key: 'open-workspace',
|
key: 'open-workspace',
|
||||||
label: 'Open Workspace',
|
label: 'Open Workspace',
|
||||||
leftSlot: <Icon icon="folder" />,
|
leftSlot: <Icon icon="folder" />,
|
||||||
onSelect: openWorkspace.mutate,
|
onSelect: openWorkspaceFromSyncDir.mutate,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return { workspaceItems, extraItems };
|
return { workspaceItems, extraItems };
|
||||||
}, [orderedWorkspaces, activeWorkspace?.id, deleteSendHistory, createWorkspace]);
|
}, [orderedWorkspaces, deleteSendHistory, createWorkspace, workspaceMeta, workspace?.id]);
|
||||||
|
|
||||||
const handleChange = useCallback(
|
const handleChangeWorkspace = useCallback(async (workspaceId: string | null) => {
|
||||||
async (workspaceId: string | null) => {
|
if (workspaceId == null) return;
|
||||||
if (workspaceId == null) return;
|
|
||||||
|
|
||||||
if (typeof openWorkspaceNewWindow === 'boolean') {
|
const settings = jotaiStore.get(settingsAtom);
|
||||||
switchWorkspace.mutate({ workspaceId, inNewWindow: openWorkspaceNewWindow });
|
if (typeof settings.openWorkspaceNewWindow === 'boolean') {
|
||||||
return;
|
switchWorkspace.mutate({ workspaceId, inNewWindow: settings.openWorkspaceNewWindow });
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const workspace = await getWorkspace(workspaceId);
|
const workspace = await getWorkspace(workspaceId);
|
||||||
if (workspace == null) return;
|
if (workspace == null) return;
|
||||||
|
|
||||||
showDialog({
|
showDialog({
|
||||||
id: 'switch-workspace',
|
id: 'switch-workspace',
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
title: 'Switch Workspace',
|
title: 'Switch Workspace',
|
||||||
render: ({ hide }) => <SwitchWorkspaceDialog workspace={workspace} hide={hide} />,
|
render: ({ hide }) => <SwitchWorkspaceDialog workspace={workspace} hide={hide} />,
|
||||||
});
|
});
|
||||||
},
|
}, []);
|
||||||
[switchWorkspace, openWorkspaceNewWindow],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RadioDropdown
|
<RadioDropdown
|
||||||
items={workspaceItems}
|
items={workspaceItems}
|
||||||
extraItems={extraItems}
|
extraItems={extraItems}
|
||||||
onChange={handleChange}
|
onChange={handleChangeWorkspace}
|
||||||
value={activeWorkspace?.id ?? null}
|
value={workspace?.id ?? null}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
className={classNames(
|
className={classNames(
|
||||||
className,
|
className,
|
||||||
'text !px-2 truncate',
|
'text !px-2 truncate',
|
||||||
activeWorkspace === null && 'italic opacity-disabled',
|
workspace === null && 'italic opacity-disabled',
|
||||||
)}
|
)}
|
||||||
{...buttonProps}
|
{...buttonProps}
|
||||||
>
|
>
|
||||||
{activeWorkspace?.name ?? 'Workspace'}
|
{workspace?.name ?? 'Workspace'}
|
||||||
</Button>
|
</Button>
|
||||||
</RadioDropdown>
|
</RadioDropdown>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
import { upsertWorkspace } from '../commands/upsertWorkspace';
|
||||||
import { upsertWorkspaceMeta } from '../commands/upsertWorkspaceMeta';
|
import { upsertWorkspaceMeta } from '../commands/upsertWorkspaceMeta';
|
||||||
import { useDeleteActiveWorkspace } from '../hooks/useDeleteActiveWorkspace';
|
import { useDeleteActiveWorkspace } from '../hooks/useDeleteActiveWorkspace';
|
||||||
import { useUpdateWorkspace } from '../hooks/useUpdateWorkspace';
|
|
||||||
import { useWorkspaceMeta } from '../hooks/useWorkspaceMeta';
|
import { useWorkspaceMeta } from '../hooks/useWorkspaceMeta';
|
||||||
import { useWorkspaces } from '../hooks/useWorkspaces';
|
import { useWorkspaces } from '../hooks/useWorkspaces';
|
||||||
import { Banner } from './core/Banner';
|
import { Banner } from './core/Banner';
|
||||||
@@ -21,7 +21,6 @@ export function WorkspaceSettingsDialog({ workspaceId, hide }: Props) {
|
|||||||
const workspaces = useWorkspaces();
|
const workspaces = useWorkspaces();
|
||||||
const workspace = workspaces.find((w) => w.id === workspaceId);
|
const workspace = workspaces.find((w) => w.id === workspaceId);
|
||||||
const workspaceMeta = useWorkspaceMeta();
|
const workspaceMeta = useWorkspaceMeta();
|
||||||
const { mutate: updateWorkspace } = useUpdateWorkspace(workspaceId ?? null);
|
|
||||||
const { mutateAsync: deleteActiveWorkspace } = useDeleteActiveWorkspace();
|
const { mutateAsync: deleteActiveWorkspace } = useDeleteActiveWorkspace();
|
||||||
|
|
||||||
if (workspace == null) {
|
if (workspace == null) {
|
||||||
@@ -44,7 +43,7 @@ export function WorkspaceSettingsDialog({ workspaceId, hide }: Props) {
|
|||||||
<Input
|
<Input
|
||||||
label="Workspace Name"
|
label="Workspace Name"
|
||||||
defaultValue={workspace.name}
|
defaultValue={workspace.name}
|
||||||
onChange={(name) => updateWorkspace({ name })}
|
onChange={(name) => upsertWorkspace.mutate({ ...workspace, name })}
|
||||||
stateKey={`name.${workspace.id}`}
|
stateKey={`name.${workspace.id}`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -54,7 +53,7 @@ export function WorkspaceSettingsDialog({ workspaceId, hide }: Props) {
|
|||||||
className="min-h-[10rem] max-h-[25rem] border border-border px-2"
|
className="min-h-[10rem] max-h-[25rem] border border-border px-2"
|
||||||
defaultValue={workspace.description}
|
defaultValue={workspace.description}
|
||||||
stateKey={`description.${workspace.id}`}
|
stateKey={`description.${workspace.id}`}
|
||||||
onChange={(description) => updateWorkspace({ description })}
|
onChange={(description) => upsertWorkspace.mutate({ ...workspace, description })}
|
||||||
heightMode="auto"
|
heightMode="auto"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -62,7 +61,7 @@ export function WorkspaceSettingsDialog({ workspaceId, hide }: Props) {
|
|||||||
<SyncToFilesystemSetting
|
<SyncToFilesystemSetting
|
||||||
value={workspaceMeta.settingSyncDir}
|
value={workspaceMeta.settingSyncDir}
|
||||||
onChange={({ value: settingSyncDir }) => {
|
onChange={({ value: settingSyncDir }) => {
|
||||||
upsertWorkspaceMeta.mutate({ settingSyncDir });
|
upsertWorkspaceMeta.mutate({ ...workspaceMeta, settingSyncDir });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Separator />
|
<Separator />
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import React, {
|
|||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import {useClickAway, useKey, useWindowSize} from 'react-use';
|
import { useClickAway, useKey, useWindowSize } from 'react-use';
|
||||||
import type { HotkeyAction } from '../../hooks/useHotKey';
|
import type { HotkeyAction } from '../../hooks/useHotKey';
|
||||||
import { useHotKey } from '../../hooks/useHotKey';
|
import { useHotKey } from '../../hooks/useHotKey';
|
||||||
import { useStateWithDeps } from '../../hooks/useStateWithDeps';
|
import { useStateWithDeps } from '../../hooks/useStateWithDeps';
|
||||||
@@ -45,7 +45,7 @@ export type DropdownItemDefault = {
|
|||||||
keepOpen?: boolean;
|
keepOpen?: boolean;
|
||||||
hotKeyAction?: HotkeyAction;
|
hotKeyAction?: HotkeyAction;
|
||||||
hotKeyLabelOnly?: boolean;
|
hotKeyLabelOnly?: boolean;
|
||||||
variant?: 'default' | 'danger' | 'notify';
|
color?: 'default' | 'danger' | 'info' | 'warning' | 'notice';
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
hidden?: boolean;
|
hidden?: boolean;
|
||||||
leftSlot?: ReactNode;
|
leftSlot?: ReactNode;
|
||||||
@@ -558,8 +558,10 @@ function MenuItem({ className, focused, onFocus, item, onSelect, ...props }: Men
|
|||||||
'h-xs', // More compact
|
'h-xs', // More compact
|
||||||
'min-w-[8rem] outline-none px-2 mx-1.5 flex whitespace-nowrap',
|
'min-w-[8rem] outline-none px-2 mx-1.5 flex whitespace-nowrap',
|
||||||
'focus:bg-surface-highlight focus:text rounded',
|
'focus:bg-surface-highlight focus:text rounded',
|
||||||
item.variant === 'danger' && '!text-danger',
|
item.color === 'danger' && '!text-danger',
|
||||||
item.variant === 'notify' && '!text-primary',
|
item.color === 'warning' && '!text-warning',
|
||||||
|
item.color === 'notice' && '!text-notice',
|
||||||
|
item.color === 'info' && '!text-info',
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -42,9 +42,10 @@ const icons = {
|
|||||||
filter: lucide.FilterIcon,
|
filter: lucide.FilterIcon,
|
||||||
flask: lucide.FlaskConicalIcon,
|
flask: lucide.FlaskConicalIcon,
|
||||||
folder: lucide.FolderIcon,
|
folder: lucide.FolderIcon,
|
||||||
folder_sync: lucide.FolderSyncIcon,
|
|
||||||
folder_input: lucide.FolderInputIcon,
|
folder_input: lucide.FolderInputIcon,
|
||||||
|
folder_open: lucide.FolderOpenIcon,
|
||||||
folder_output: lucide.FolderOutputIcon,
|
folder_output: lucide.FolderOutputIcon,
|
||||||
|
folder_sync: lucide.FolderSyncIcon,
|
||||||
git_branch: lucide.GitBranchIcon,
|
git_branch: lucide.GitBranchIcon,
|
||||||
git_commit: lucide.GitCommitIcon,
|
git_commit: lucide.GitCommitIcon,
|
||||||
git_commit_vertical: lucide.GitCommitVerticalIcon,
|
git_commit_vertical: lucide.GitCommitVerticalIcon,
|
||||||
|
|||||||
@@ -22,14 +22,18 @@ export function KeyValueRows({ children }: Props) {
|
|||||||
|
|
||||||
interface KeyValueRowProps {
|
interface KeyValueRowProps {
|
||||||
label: ReactNode;
|
label: ReactNode;
|
||||||
value: ReactNode;
|
children: ReactNode;
|
||||||
|
rightSlot?: ReactNode;
|
||||||
|
leftSlot?: ReactNode;
|
||||||
labelClassName?: string;
|
labelClassName?: string;
|
||||||
labelColor?: 'secondary' | 'primary' | 'info';
|
labelColor?: 'secondary' | 'primary' | 'info';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function KeyValueRow({
|
export function KeyValueRow({
|
||||||
label,
|
label,
|
||||||
value,
|
children,
|
||||||
|
rightSlot,
|
||||||
|
leftSlot,
|
||||||
labelColor = 'secondary',
|
labelColor = 'secondary',
|
||||||
labelClassName,
|
labelClassName,
|
||||||
}: KeyValueRowProps) {
|
}: KeyValueRowProps) {
|
||||||
@@ -47,7 +51,11 @@ export function KeyValueRow({
|
|||||||
<span className="select-text cursor-text">{label}</span>
|
<span className="select-text cursor-text">{label}</span>
|
||||||
</td>
|
</td>
|
||||||
<td className="select-none py-0.5 break-all align-top max-w-[15rem]">
|
<td className="select-none py-0.5 break-all align-top max-w-[15rem]">
|
||||||
<div className="select-text cursor-text max-h-[5rem] overflow-y-auto">{value}</div>
|
<div className="select-text cursor-text max-h-[5rem] overflow-y-auto grid grid-cols-[auto_minmax(0,1fr)_auto]">
|
||||||
|
{leftSlot ?? <span aria-hidden />}
|
||||||
|
{children}
|
||||||
|
{rightSlot ? <div className="ml-1.5">{rightSlot}</div> : <span aria-hidden />}
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -344,7 +344,7 @@ function PairEditorRow({
|
|||||||
key: 'delete',
|
key: 'delete',
|
||||||
label: 'Delete',
|
label: 'Delete',
|
||||||
onSelect: handleDelete,
|
onSelect: handleDelete,
|
||||||
variant: 'danger',
|
color: 'danger',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[handleDelete],
|
[handleDelete],
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
import { router } from '../lib/router';
|
|
||||||
import { invokeCmd } from '../lib/tauri';
|
|
||||||
import { useFastMutation } from './useFastMutation';
|
|
||||||
import { getRecentCookieJars } from './useRecentCookieJars';
|
|
||||||
import { getRecentEnvironments } from './useRecentEnvironments';
|
|
||||||
import { getRecentRequests } from './useRecentRequests';
|
|
||||||
|
|
||||||
export function useSwitchWorkspace() {
|
|
||||||
return useFastMutation({
|
|
||||||
mutationKey: ['open_workspace'],
|
|
||||||
mutationFn: async ({
|
|
||||||
workspaceId,
|
|
||||||
inNewWindow,
|
|
||||||
}: {
|
|
||||||
workspaceId: string;
|
|
||||||
inNewWindow: boolean;
|
|
||||||
}) => {
|
|
||||||
const environmentId = (await getRecentEnvironments(workspaceId))[0] ?? undefined;
|
|
||||||
const requestId = (await getRecentRequests(workspaceId))[0] ?? undefined;
|
|
||||||
const cookieJarId = (await getRecentCookieJars(workspaceId))[0] ?? undefined;
|
|
||||||
const search = {
|
|
||||||
environment_id: environmentId,
|
|
||||||
cookie_jar_id: cookieJarId,
|
|
||||||
request_id: requestId,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (inNewWindow) {
|
|
||||||
const location = router.buildLocation({
|
|
||||||
to: '/workspaces/$workspaceId',
|
|
||||||
params: { workspaceId },
|
|
||||||
search,
|
|
||||||
});
|
|
||||||
await invokeCmd('cmd_new_main_window', { url: location.href });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await router.navigate({
|
|
||||||
to: '/workspaces/$workspaceId',
|
|
||||||
params: { workspaceId },
|
|
||||||
search,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { WorkspaceMeta } from '@yaakapp-internal/models';
|
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { jotaiStore } from '../lib/jotai';
|
import { jotaiStore } from '../lib/jotai';
|
||||||
|
import { getWorkspaceMeta } from '../lib/store';
|
||||||
import { invokeCmd } from '../lib/tauri';
|
import { invokeCmd } from '../lib/tauri';
|
||||||
import { activeWorkspaceIdAtom, getActiveWorkspaceId } from './useActiveWorkspace';
|
import { activeWorkspaceIdAtom, getActiveWorkspaceId } from './useActiveWorkspace';
|
||||||
import { cookieJarsAtom } from './useCookieJars';
|
import { cookieJarsAtom } from './useCookieJars';
|
||||||
@@ -26,10 +26,9 @@ async function sync() {
|
|||||||
jotaiStore.set(keyValuesAtom, await invokeCmd('cmd_list_key_values'));
|
jotaiStore.set(keyValuesAtom, await invokeCmd('cmd_list_key_values'));
|
||||||
|
|
||||||
const workspaceId = getActiveWorkspaceId();
|
const workspaceId = getActiveWorkspaceId();
|
||||||
|
if (workspaceId == null) return;
|
||||||
|
|
||||||
const args = { workspaceId };
|
const args = { workspaceId };
|
||||||
if (workspaceId == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the things we need first, first
|
// Set the things we need first, first
|
||||||
jotaiStore.set(httpRequestsAtom, await invokeCmd('cmd_list_http_requests', args));
|
jotaiStore.set(httpRequestsAtom, await invokeCmd('cmd_list_http_requests', args));
|
||||||
@@ -43,5 +42,5 @@ async function sync() {
|
|||||||
jotaiStore.set(environmentsAtom, await invokeCmd('cmd_list_environments', args));
|
jotaiStore.set(environmentsAtom, await invokeCmd('cmd_list_environments', args));
|
||||||
|
|
||||||
// Single models
|
// Single models
|
||||||
jotaiStore.set(workspaceMetaAtom, await invokeCmd<WorkspaceMeta>('cmd_get_workspace_meta', args));
|
jotaiStore.set(workspaceMetaAtom, await getWorkspaceMeta(workspaceId));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
import type { Workspace } from '@yaakapp-internal/models';
|
|
||||||
import { getWorkspace } from '../lib/store';
|
|
||||||
import { invokeCmd } from '../lib/tauri';
|
|
||||||
import { useFastMutation } from './useFastMutation';
|
|
||||||
|
|
||||||
export function useUpdateWorkspace(id: string | null) {
|
|
||||||
return useFastMutation<Workspace, unknown, Partial<Workspace> | ((w: Workspace) => Workspace)>({
|
|
||||||
mutationKey: ['update_workspace', id],
|
|
||||||
mutationFn: async (v) => {
|
|
||||||
const workspace = await getWorkspace(id);
|
|
||||||
if (workspace == null) {
|
|
||||||
throw new Error("Can't update a null workspace");
|
|
||||||
}
|
|
||||||
|
|
||||||
const newWorkspace = typeof v === 'function' ? v(workspace) : { ...workspace, ...v };
|
|
||||||
return invokeCmd('cmd_update_workspace', { workspace: newWorkspace });
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,13 +1,8 @@
|
|||||||
import type { WorkspaceMeta } from '@yaakapp-internal/models';
|
import type { WorkspaceMeta } from '@yaakapp-internal/models';
|
||||||
import { atom, useAtomValue } from 'jotai';
|
import { atom, useAtomValue } from 'jotai';
|
||||||
|
|
||||||
export const workspaceMetaAtom = atom<WorkspaceMeta>();
|
export const workspaceMetaAtom = atom<WorkspaceMeta | null>(null);
|
||||||
|
|
||||||
export function useWorkspaceMeta() {
|
export function useWorkspaceMeta() {
|
||||||
const workspaceMeta = useAtomValue(workspaceMetaAtom);
|
return useAtomValue(workspaceMetaAtom);
|
||||||
if (!workspaceMeta) {
|
|
||||||
throw new Error('WorkspaceMeta not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
return workspaceMeta;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { type } from '@tauri-apps/plugin-os';
|
||||||
|
|
||||||
|
const os = type();
|
||||||
|
export const revealInFinderText =
|
||||||
|
os === 'macos'
|
||||||
|
? 'Reveal in Finder'
|
||||||
|
: os === 'windows'
|
||||||
|
? 'Show in Explorer'
|
||||||
|
: 'Show in File Manager';
|
||||||
@@ -7,6 +7,7 @@ import type {
|
|||||||
Plugin,
|
Plugin,
|
||||||
Settings,
|
Settings,
|
||||||
Workspace,
|
Workspace,
|
||||||
|
WorkspaceMeta,
|
||||||
} from '@yaakapp-internal/models';
|
} from '@yaakapp-internal/models';
|
||||||
import { invokeCmd } from './tauri';
|
import { invokeCmd } from './tauri';
|
||||||
|
|
||||||
@@ -59,6 +60,10 @@ export async function getWorkspace(id: string | null): Promise<Workspace | null>
|
|||||||
return workspace;
|
return workspace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getWorkspaceMeta(workspaceId: string) {
|
||||||
|
return invokeCmd<WorkspaceMeta>('cmd_get_workspace_meta', { workspaceId });
|
||||||
|
}
|
||||||
|
|
||||||
export async function listWorkspaces(): Promise<Workspace[]> {
|
export async function listWorkspaces(): Promise<Workspace[]> {
|
||||||
const workspaces: Workspace[] = (await invokeCmd('cmd_list_workspaces')) ?? [];
|
const workspaces: Workspace[] = (await invokeCmd('cmd_list_workspaces')) ?? [];
|
||||||
return workspaces;
|
return workspaces;
|
||||||
|
|||||||
Reference in New Issue
Block a user