diff --git a/src-tauri/src/analytics.rs b/src-tauri/src/analytics.rs index 53a71afb..94ba0210 100644 --- a/src-tauri/src/analytics.rs +++ b/src-tauri/src/analytics.rs @@ -1,8 +1,8 @@ use log::{debug, warn}; use serde::{Deserialize, Serialize}; use serde_json::json; -use sqlx::{Pool, Sqlite}; use sqlx::types::JsonValue; +use sqlx::{Pool, Sqlite}; use tauri::{AppHandle, Manager}; use crate::{is_dev, models}; @@ -15,6 +15,7 @@ pub enum AnalyticsResource { Dialog, Environment, Folder, + GrpcRequest, HttpRequest, HttpResponse, KeyValue, @@ -30,6 +31,7 @@ impl AnalyticsResource { "CookieJar" => Some(AnalyticsResource::CookieJar), "Environment" => Some(AnalyticsResource::Environment), "Folder" => Some(AnalyticsResource::Folder), + "GrpcRequest" => Some(AnalyticsResource::GrpcRequest), "HttpRequest" => Some(AnalyticsResource::HttpRequest), "HttpResponse" => Some(AnalyticsResource::HttpResponse), "KeyValue" => Some(AnalyticsResource::KeyValue), @@ -89,6 +91,7 @@ fn resource_name(resource: AnalyticsResource) -> &'static str { AnalyticsResource::Dialog => "dialog", AnalyticsResource::Environment => "environment", AnalyticsResource::Folder => "folder", + AnalyticsResource::GrpcRequest => "grpc_request", AnalyticsResource::HttpRequest => "http_request", AnalyticsResource::HttpResponse => "http_response", AnalyticsResource::KeyValue => "key_value", diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 90e0cc86..2cf28ea2 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -44,13 +44,14 @@ use crate::http::send_http_request; use crate::models::{ cancel_pending_responses, create_response, delete_all_responses, delete_cookie_jar, delete_environment, delete_folder, delete_request, delete_response, delete_workspace, - duplicate_request, find_cookie_jars, find_environments, find_folders, find_requests, - find_responses, find_workspaces, generate_id, get_cookie_jar, get_environment, get_folder, - get_http_request, get_key_value_raw, get_or_create_settings, get_response, get_workspace, - get_workspace_export_resources, set_key_value_raw, update_response_if_id, update_settings, - upsert_cookie_jar, upsert_environment, upsert_folder, upsert_request, upsert_workspace, - CookieJar, Environment, EnvironmentVariable, Folder, HttpRequest, HttpResponse, KeyValue, - Settings, Workspace, + duplicate_grpc_request, duplicate_http_request, list_cookie_jars, list_folders, list_requests, + list_responses, list_workspaces, generate_id, get_cookie_jar, get_environment, get_folder, + get_grpc_request, get_http_request, get_key_value_raw, get_or_create_settings, get_response, + get_workspace, get_workspace_export_resources, list_environments, list_grpc_requests, + set_key_value_raw, update_response_if_id, update_settings, upsert_cookie_jar, + upsert_environment, upsert_folder, upsert_grpc_request, upsert_http_request, upsert_workspace, + CookieJar, Environment, EnvironmentVariable, Folder, GrpcRequest, HttpRequest, HttpResponse, + KeyValue, Settings, Workspace, }; use crate::plugin::{ImportResources, ImportResult}; use crate::updates::{update_mode_from_str, UpdateMode, YaakUpdater}; @@ -467,7 +468,7 @@ async fn cmd_import_data( } for r in r.resources.requests { - let x = upsert_request(db, r) + let x = upsert_http_request(db, r) .await .expect("Failed to create request"); imported_resources.requests.push(x.clone()); @@ -747,7 +748,46 @@ async fn cmd_create_environment( } #[tauri::command] -async fn cmd_create_request( +async fn cmd_create_grpc_request( + workspace_id: &str, + name: &str, + sort_priority: f64, + folder_id: Option<&str>, + window: Window, + db_state: State<'_, Mutex>>, +) -> Result { + let db = &*db_state.lock().await; + let created = upsert_grpc_request( + db, + &GrpcRequest { + workspace_id: workspace_id.to_string(), + name: name.to_string(), + folder_id: folder_id.map(|s| s.to_string()), + sort_priority, + ..Default::default() + }, + ) + .await + .expect("Failed to create grpc request"); + + emit_and_return(&window, "created_model", created) +} + +#[tauri::command] +async fn cmd_duplicate_grpc_request( + id: &str, + window: Window, + db_state: State<'_, Mutex>>, +) -> Result { + let db = &*db_state.lock().await; + let request = duplicate_grpc_request(db, id) + .await + .expect("Failed to duplicate grpc request"); + emit_and_return(&window, "updated_model", request) +} + +#[tauri::command] +async fn cmd_create_http_request( workspace_id: &str, name: &str, sort_priority: f64, @@ -756,7 +796,7 @@ async fn cmd_create_request( db_state: State<'_, Mutex>>, ) -> Result { let db = &*db_state.lock().await; - let created_request = upsert_request( + let created_request = upsert_http_request( db, HttpRequest { workspace_id: workspace_id.to_string(), @@ -768,21 +808,21 @@ async fn cmd_create_request( }, ) .await - .expect("Failed to create request"); + .expect("Failed to create http request"); emit_and_return(&window, "created_model", created_request) } #[tauri::command] -async fn cmd_duplicate_request( +async fn cmd_duplicate_http_request( id: &str, window: Window, db_state: State<'_, Mutex>>, ) -> Result { let db = &*db_state.lock().await; - let request = duplicate_request(db, id) + let request = duplicate_http_request(db, id) .await - .expect("Failed to duplicate request"); + .expect("Failed to duplicate http request"); emit_and_return(&window, "updated_model", request) } @@ -815,20 +855,46 @@ async fn cmd_update_environment( } #[tauri::command] -async fn cmd_update_request( +async fn cmd_update_grpc_request( + request: GrpcRequest, + window: Window, + db_state: State<'_, Mutex>>, +) -> Result { + let db = &*db_state.lock().await; + let updated_request = upsert_grpc_request(db, &request) + .await + .expect("Failed to update grpc request"); + emit_and_return(&window, "updated_model", updated_request) +} + +#[tauri::command] +async fn cmd_update_http_request( request: HttpRequest, window: Window, db_state: State<'_, Mutex>>, ) -> Result { let db = &*db_state.lock().await; - let updated_request = upsert_request(db, request) + let updated_request = upsert_http_request(db, request) .await .expect("Failed to update request"); emit_and_return(&window, "updated_model", updated_request) } #[tauri::command] -async fn cmd_delete_request( +async fn cmd_delete_grpc_request( + window: Window, + db_state: State<'_, Mutex>>, + request_id: &str, +) -> Result { + let db = &*db_state.lock().await; + let req = delete_request(db, request_id) + .await + .expect("Failed to delete request"); + emit_and_return(&window, "deleted_model", req) +} + +#[tauri::command] +async fn cmd_delete_http_request( window: Window, db_state: State<'_, Mutex>>, request_id: &str, @@ -846,7 +912,7 @@ async fn cmd_list_folders( db_state: State<'_, Mutex>>, ) -> Result, String> { let db = &*db_state.lock().await; - find_folders(db, workspace_id) + list_folders(db, workspace_id) .await .map_err(|e| e.to_string()) } @@ -917,12 +983,25 @@ async fn cmd_delete_environment( } #[tauri::command] -async fn cmd_list_requests( +async fn cmd_list_grpc_requests( + workspace_id: &str, + db_state: State<'_, Mutex>>, +) -> Result, String> { + let db = &*db_state.lock().await; + let requests = list_grpc_requests(db, workspace_id) + .await + .expect("Failed to find grpc requests"); + // .map_err(|e| e.to_string()) + Ok(requests) +} + +#[tauri::command] +async fn cmd_list_http_requests( workspace_id: &str, db_state: State<'_, Mutex>>, ) -> Result, String> { let db = &*db_state.lock().await; - let requests = find_requests(db, workspace_id) + let requests = list_requests(db, workspace_id) .await .expect("Failed to find requests"); // .map_err(|e| e.to_string()) @@ -935,7 +1014,7 @@ async fn cmd_list_environments( db_state: State<'_, Mutex>>, ) -> Result, String> { let db = &*db_state.lock().await; - let environments = find_environments(db, workspace_id) + let environments = list_environments(db, workspace_id) .await .expect("Failed to find environments"); @@ -972,7 +1051,16 @@ async fn cmd_get_folder( } #[tauri::command] -async fn cmd_get_request( +async fn cmd_get_grpc_request( + id: &str, + db_state: State<'_, Mutex>>, +) -> Result { + let db = &*db_state.lock().await; + get_grpc_request(db, id).await.map_err(|e| e.to_string()) +} + +#[tauri::command] +async fn cmd_get_http_request( id: &str, db_state: State<'_, Mutex>>, ) -> Result { @@ -995,7 +1083,7 @@ async fn cmd_list_cookie_jars( db_state: State<'_, Mutex>>, ) -> Result, String> { let db = &*db_state.lock().await; - let cookie_jars = find_cookie_jars(db, workspace_id) + let cookie_jars = list_cookie_jars(db, workspace_id) .await .expect("Failed to find cookie jars"); @@ -1041,7 +1129,7 @@ async fn cmd_list_responses( db_state: State<'_, Mutex>>, ) -> Result, String> { let db = &*db_state.lock().await; - find_responses(db, request_id, limit) + list_responses(db, request_id, limit) .await .map_err(|e| e.to_string()) } @@ -1075,7 +1163,7 @@ async fn cmd_list_workspaces( db_state: State<'_, Mutex>>, ) -> Result, String> { let db = &*db_state.lock().await; - let workspaces = find_workspaces(db) + let workspaces = list_workspaces(db) .await .expect("Failed to find workspaces"); if workspaces.is_empty() { @@ -1203,23 +1291,26 @@ fn main() { cmd_create_cookie_jar, cmd_create_environment, cmd_create_folder, - cmd_create_request, + cmd_create_grpc_request, + cmd_create_http_request, cmd_create_workspace, cmd_delete_all_responses, cmd_delete_cookie_jar, cmd_delete_environment, cmd_delete_folder, - cmd_delete_request, + cmd_delete_grpc_request, + cmd_delete_http_request, cmd_delete_response, cmd_delete_workspace, - cmd_duplicate_request, + cmd_duplicate_http_request, cmd_export_data, cmd_filter_response, cmd_get_cookie_jar, cmd_get_environment, cmd_get_folder, cmd_get_key_value, - cmd_get_request, + cmd_get_http_request, + cmd_get_grpc_request, cmd_get_settings, cmd_get_workspace, cmd_grpc_call_unary, @@ -1231,7 +1322,8 @@ fn main() { cmd_list_cookie_jars, cmd_list_environments, cmd_list_folders, - cmd_list_requests, + cmd_list_http_requests, + cmd_list_grpc_requests, cmd_list_responses, cmd_list_workspaces, cmd_new_window, @@ -1243,7 +1335,8 @@ fn main() { cmd_update_cookie_jar, cmd_update_environment, cmd_update_folder, - cmd_update_request, + cmd_update_grpc_request, + cmd_update_http_request, cmd_update_settings, cmd_update_workspace, ]) diff --git a/src-tauri/src/models.rs b/src-tauri/src/models.rs index bf56b4ff..8e969421 100644 --- a/src-tauri/src/models.rs +++ b/src-tauri/src/models.rs @@ -355,7 +355,7 @@ pub async fn get_key_value_raw(db: &Pool, namespace: &str, key: &str) -> .ok() } -pub async fn find_workspaces(db: &Pool) -> Result, sqlx::Error> { +pub async fn list_workspaces(db: &Pool) -> Result, sqlx::Error> { sqlx::query_as!( Workspace, r#" @@ -398,7 +398,7 @@ pub async fn delete_workspace(db: &Pool, id: &str) -> Result, id: &str) -> Result, workspace_id: &str, ) -> Result, sqlx::Error> { @@ -454,6 +454,15 @@ pub async fn delete_cookie_jar(db: &Pool, id: &str) -> Result, + id: &str, +) -> Result { + let mut request = get_grpc_request(db, id).await?.clone(); + request.id = "".to_string(); + upsert_grpc_request(db, &request).await +} + pub async fn upsert_grpc_request( db: &Pool, request: &GrpcRequest, @@ -687,7 +696,7 @@ pub async fn upsert_cookie_jar( get_cookie_jar(db, &id).await } -pub async fn find_environments( +pub async fn list_environments( db: &Pool, workspace_id: &str, ) -> Result, sqlx::Error> { @@ -832,7 +841,7 @@ pub async fn get_folder(db: &Pool, id: &str) -> Result, workspace_id: &str, ) -> Result, sqlx::Error> { @@ -896,13 +905,19 @@ pub async fn upsert_folder(db: &Pool, r: Folder) -> Result, id: &str) -> Result { +pub async fn duplicate_http_request( + db: &Pool, + id: &str, +) -> Result { let mut request = get_http_request(db, id).await?.clone(); request.id = "".to_string(); - upsert_request(db, request).await + upsert_http_request(db, request).await } -pub async fn upsert_request(db: &Pool, r: HttpRequest) -> Result { +pub async fn upsert_http_request( + db: &Pool, + r: HttpRequest, +) -> Result { let id = match r.id.as_str() { "" => generate_id(Some("rq")), _ => r.id.to_string(), @@ -952,7 +967,7 @@ pub async fn upsert_request(db: &Pool, r: HttpRequest) -> Result, workspace_id: &str, ) -> Result, sqlx::Error> { @@ -1171,7 +1186,7 @@ pub async fn get_response(db: &Pool, id: &str) -> Result, request_id: &str, limit: Option, @@ -1197,7 +1212,7 @@ pub async fn find_responses( .await } -pub async fn find_responses_by_workspace_id( +pub async fn list_responses_by_workspace_id( db: &Pool, workspace_id: &str, ) -> Result, sqlx::Error> { @@ -1243,7 +1258,7 @@ pub async fn delete_response(db: &Pool, id: &str) -> Result, request_id: &str) -> Result<(), sqlx::Error> { - for r in find_responses(db, request_id, None).await? { + for r in list_responses(db, request_id, None).await? { delete_response(db, &r.id).await?; } Ok(()) @@ -1289,13 +1304,13 @@ pub async fn get_workspace_export_resources( timestamp: chrono::Utc::now().naive_utc(), resources: WorkspaceExportResources { workspaces: vec![workspace], - environments: find_environments(db, workspace_id) + environments: list_environments(db, workspace_id) .await .expect("Failed to get environments"), - folders: find_folders(db, workspace_id) + folders: list_folders(db, workspace_id) .await .expect("Failed to get folders"), - requests: find_requests(db, workspace_id) + requests: list_requests(db, workspace_id) .await .expect("Failed to get requests"), }, diff --git a/src-web/components/AppRouter.tsx b/src-web/components/AppRouter.tsx index f3d61137..c3e3725c 100644 --- a/src-web/components/AppRouter.tsx +++ b/src-web/components/AppRouter.tsx @@ -1,7 +1,7 @@ import { createBrowserRouter, Navigate, Outlet, RouterProvider } from 'react-router-dom'; import { routePaths, useAppRoutes } from '../hooks/useAppRoutes'; import { useRecentRequests } from '../hooks/useRecentRequests'; -import { useRequests } from '../hooks/useRequests'; +import { useHttpRequests } from '../hooks/useHttpRequests'; import { GlobalHooks } from './GlobalHooks'; import Workspace from './Workspace'; import Workspaces from './Workspaces'; @@ -49,7 +49,7 @@ export function AppRouter() { function WorkspaceOrRedirect() { const recentRequests = useRecentRequests(); const activeEnvironmentId = useActiveEnvironmentId(); - const requests = useRequests(); + const requests = useHttpRequests(); const request = requests.find((r) => r.id === recentRequests[0]); const routes = useAppRoutes(); diff --git a/src-web/components/BasicAuth.tsx b/src-web/components/BasicAuth.tsx index ed1bd61c..459a7b97 100644 --- a/src-web/components/BasicAuth.tsx +++ b/src-web/components/BasicAuth.tsx @@ -1,4 +1,4 @@ -import { useUpdateRequest } from '../hooks/useUpdateRequest'; +import { useUpdateHttpRequest } from '../hooks/useUpdateHttpRequest'; import type { HttpRequest } from '../lib/models'; import { Input } from './core/Input'; import { VStack } from './core/Stacks'; @@ -9,7 +9,7 @@ interface Props { } export function BasicAuth({ requestId, authentication }: Props) { - const updateRequest = useUpdateRequest(requestId); + const updateRequest = useUpdateHttpRequest(requestId); return ( diff --git a/src-web/components/BearerAuth.tsx b/src-web/components/BearerAuth.tsx index 1a63f049..db149670 100644 --- a/src-web/components/BearerAuth.tsx +++ b/src-web/components/BearerAuth.tsx @@ -1,4 +1,4 @@ -import { useUpdateRequest } from '../hooks/useUpdateRequest'; +import { useUpdateHttpRequest } from '../hooks/useUpdateHttpRequest'; import type { HttpRequest } from '../lib/models'; import { Input } from './core/Input'; import { VStack } from './core/Stacks'; @@ -9,7 +9,7 @@ interface Props { } export function BearerAuth({ requestId, authentication }: Props) { - const updateRequest = useUpdateRequest(requestId); + const updateRequest = useUpdateHttpRequest(requestId); return ( diff --git a/src-web/components/GlobalHooks.tsx b/src-web/components/GlobalHooks.tsx index d7a6e993..fbcc875a 100644 --- a/src-web/components/GlobalHooks.tsx +++ b/src-web/components/GlobalHooks.tsx @@ -8,7 +8,7 @@ import { useListenToTauriEvent } from '../hooks/useListenToTauriEvent'; import { useRecentEnvironments } from '../hooks/useRecentEnvironments'; import { useRecentRequests } from '../hooks/useRecentRequests'; import { useRecentWorkspaces } from '../hooks/useRecentWorkspaces'; -import { requestsQueryKey } from '../hooks/useRequests'; +import { httpRequestsQueryKey } from '../hooks/useHttpRequests'; import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey'; import { responsesQueryKey } from '../hooks/useResponses'; import { settingsQueryKey } from '../hooks/useSettings'; @@ -47,7 +47,7 @@ export function GlobalHooks() { const queryKey = payload.model === 'http_request' - ? requestsQueryKey(payload) + ? httpRequestsQueryKey(payload) : payload.model === 'http_response' ? responsesQueryKey(payload) : payload.model === 'workspace' @@ -76,7 +76,7 @@ export function GlobalHooks() { const queryKey = payload.model === 'http_request' - ? requestsQueryKey(payload) + ? httpRequestsQueryKey(payload) : payload.model === 'http_response' ? responsesQueryKey(payload) : payload.model === 'workspace' @@ -115,7 +115,7 @@ export function GlobalHooks() { if (payload.model === 'workspace') { queryClient.setQueryData(workspacesQueryKey(), removeById(payload)); } else if (payload.model === 'http_request') { - queryClient.setQueryData(requestsQueryKey(payload), removeById(payload)); + queryClient.setQueryData(httpRequestsQueryKey(payload), removeById(payload)); } else if (payload.model === 'http_response') { queryClient.setQueryData(responsesQueryKey(payload), removeById(payload)); } else if (payload.model === 'key_value') { diff --git a/src-web/components/GrpcConnectionLayout.tsx b/src-web/components/GrpcConnectionLayout.tsx index 5de7415f..5b17286e 100644 --- a/src-web/components/GrpcConnectionLayout.tsx +++ b/src-web/components/GrpcConnectionLayout.tsx @@ -208,7 +208,7 @@ export function GrpcConnectionLayout({ style }: Props) { className="border border-highlight" size="sm" title="to-do" - hotkeyAction={grpc.isStreaming ? undefined : 'request.send'} + hotkeyAction={grpc.isStreaming ? undefined : 'http_request.send'} onClick={grpc.isStreaming ? handleCancel : handleConnect} icon={ grpc.isStreaming @@ -227,7 +227,7 @@ export function GrpcConnectionLayout({ style }: Props) { className="border border-highlight" size="sm" title="to-do" - hotkeyAction="request.send" + hotkeyAction="grpc_request.send" onClick={() => grpc.send.mutateAsync({ message: message.value ?? '' })} icon="sendHorizontal" /> @@ -331,7 +331,7 @@ export function GrpcConnectionLayout({ style }: Props) { forceUpdateKey={resp} /> ) : ( - + )} ) diff --git a/src-web/components/RecentRequestsDropdown.tsx b/src-web/components/RecentRequestsDropdown.tsx index ec509949..9800a819 100644 --- a/src-web/components/RecentRequestsDropdown.tsx +++ b/src-web/components/RecentRequestsDropdown.tsx @@ -7,7 +7,7 @@ import { useActiveWorkspaceId } from '../hooks/useActiveWorkspaceId'; import { useAppRoutes } from '../hooks/useAppRoutes'; import { useHotKey } from '../hooks/useHotKey'; import { useRecentRequests } from '../hooks/useRecentRequests'; -import { useRequests } from '../hooks/useRequests'; +import { useHttpRequests } from '../hooks/useHttpRequests'; import { fallbackRequestName } from '../lib/fallbackRequestName'; import type { ButtonProps } from './core/Button'; import { Button } from './core/Button'; @@ -19,7 +19,7 @@ export function RecentRequestsDropdown({ className }: Pick allRecentRequestIds.slice(1), [allRecentRequestIds]); diff --git a/src-web/components/RequestPane.tsx b/src-web/components/RequestPane.tsx index 196d1dd9..a5498a05 100644 --- a/src-web/components/RequestPane.tsx +++ b/src-web/components/RequestPane.tsx @@ -6,7 +6,7 @@ import { useActiveRequest } from '../hooks/useActiveRequest'; import { useIsResponseLoading } from '../hooks/useIsResponseLoading'; import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey'; import { useSendRequest } from '../hooks/useSendRequest'; -import { useUpdateRequest } from '../hooks/useUpdateRequest'; +import { useUpdateHttpRequest } from '../hooks/useUpdateHttpRequest'; import { tryFormatJson } from '../lib/formatters'; import type { HttpHeader, HttpRequest, HttpUrlParameter } from '../lib/models'; import { @@ -43,9 +43,9 @@ interface Props { const useActiveTab = createGlobalState('body'); export const RequestPane = memo(function RequestPane({ style, fullHeight, className }: Props) { - const activeRequest = useActiveRequest(); + const activeRequest = useActiveRequest('http_request'); const activeRequestId = activeRequest?.id ?? null; - const updateRequest = useUpdateRequest(activeRequestId); + const updateRequest = useUpdateHttpRequest(activeRequestId); const [activeTab, setActiveTab] = useActiveTab(); const [forceUpdateHeaderEditorKey, setForceUpdateHeaderEditorKey] = useState(0); const { updateKey: forceUpdateKey } = useRequestUpdateKey(activeRequest?.id ?? null); diff --git a/src-web/components/ResponsePane.tsx b/src-web/components/ResponsePane.tsx index 1b737725..18f9fd7a 100644 --- a/src-web/components/ResponsePane.tsx +++ b/src-web/components/ResponsePane.tsx @@ -110,7 +110,7 @@ export const ResponsePane = memo(function ResponsePane({ style, className }: Pro <> )} diff --git a/src-web/components/Sidebar.tsx b/src-web/components/Sidebar.tsx index 36105362..3cfaa432 100644 --- a/src-web/components/Sidebar.tsx +++ b/src-web/components/Sidebar.tsx @@ -11,26 +11,27 @@ import { useActiveRequestId } from '../hooks/useActiveRequestId'; import { useActiveWorkspace } from '../hooks/useActiveWorkspace'; import { useAppRoutes } from '../hooks/useAppRoutes'; import { useCreateFolder } from '../hooks/useCreateFolder'; -import { useCreateRequest } from '../hooks/useCreateRequest'; +import { useCreateHttpRequest } from '../hooks/useCreateHttpRequest'; import { useDeleteAnyRequest } from '../hooks/useDeleteAnyRequest'; import { useDeleteFolder } from '../hooks/useDeleteFolder'; import { useDeleteRequest } from '../hooks/useDeleteRequest'; import { useDuplicateRequest } from '../hooks/useDuplicateRequest'; import { useFolders } from '../hooks/useFolders'; +import { useGrpcRequests } from '../hooks/useGrpcRequests'; import { useHotKey } from '../hooks/useHotKey'; import { useKeyValue } from '../hooks/useKeyValue'; import { useLatestResponse } from '../hooks/useLatestResponse'; import { usePrompt } from '../hooks/usePrompt'; -import { useRequests } from '../hooks/useRequests'; +import { useHttpRequests } from '../hooks/useHttpRequests'; import { useSendManyRequests } from '../hooks/useSendFolder'; import { useSendRequest } from '../hooks/useSendRequest'; import { useSidebarHidden } from '../hooks/useSidebarHidden'; import { useUpdateAnyFolder } from '../hooks/useUpdateAnyFolder'; -import { useUpdateAnyRequest } from '../hooks/useUpdateAnyRequest'; -import { useUpdateRequest } from '../hooks/useUpdateRequest'; +import { useUpdateAnyHttpRequest } from '../hooks/useUpdateAnyHttpRequest'; +import { useUpdateHttpRequest } from '../hooks/useUpdateHttpRequest'; import { fallbackRequestName } from '../lib/fallbackRequestName'; import { NAMESPACE_NO_SYNC } from '../lib/keyValueStore'; -import type { Folder, HttpRequest, Workspace } from '../lib/models'; +import type { Folder, GrpcRequest, HttpRequest, Workspace } from '../lib/models'; import { isResponseLoading } from '../lib/models'; import { ContextMenu } from './core/Dropdown'; import { Icon } from './core/Icon'; @@ -48,7 +49,7 @@ enum ItemTypes { } interface TreeNode { - item: Workspace | Folder | HttpRequest; + item: Workspace | Folder | HttpRequest | GrpcRequest; children: TreeNode[]; depth: number; } @@ -58,7 +59,8 @@ export function Sidebar({ className }: Props) { const sidebarRef = useRef(null); const activeRequestId = useActiveRequestId(); const activeEnvironmentId = useActiveEnvironmentId(); - const requests = useRequests(); + const httpRequests = useHttpRequests(); + const grpcRequests = useGrpcRequests(); const folders = useFolders(); const deleteAnyRequest = useDeleteAnyRequest(); const activeWorkspace = useActiveWorkspace(); @@ -67,7 +69,7 @@ export function Sidebar({ className }: Props) { const [hasFocus, setHasFocus] = useState(false); const [selectedId, setSelectedId] = useState(null); const [selectedTree, setSelectedTree] = useState(null); - const updateAnyRequest = useUpdateAnyRequest(); + const updateAnyRequest = useUpdateAnyHttpRequest(); const updateAnyFolder = useUpdateAnyFolder(); const [draggingId, setDraggingId] = useState(null); const [hoveredTree, setHoveredTree] = useState(null); @@ -78,7 +80,7 @@ export function Sidebar({ className }: Props) { namespace: NAMESPACE_NO_SYNC, }); - useHotKey('request.duplicate', () => { + useHotKey('http_request.duplicate', () => { duplicateRequest.mutate(); }); @@ -110,7 +112,7 @@ export function Sidebar({ className }: Props) { // Put requests and folders into a tree structure const next = (node: TreeNode): TreeNode => { - const childItems = [...requests, ...folders].filter((f) => + const childItems = [...httpRequests, ...grpcRequests, ...folders].filter((f) => node.item.model === 'workspace' ? f.folderId == null : f.folderId === node.item.id, ); @@ -119,7 +121,7 @@ export function Sidebar({ className }: Props) { for (const item of childItems) { treeParentMap[item.id] = node; node.children.push(next({ item, children: [], depth })); - if (item.model === 'http_request') { + if (item.model !== 'folder') { selectableRequests.push({ id: item.id, index: selectableRequestIndex++, tree: node }); } } @@ -129,7 +131,7 @@ export function Sidebar({ className }: Props) { const tree = next({ item: activeWorkspace, children: [], depth: 0 }); return { tree, treeParentMap, selectableRequests }; - }, [activeWorkspace, requests, folders]); + }, [activeWorkspace, httpRequests, grpcRequests, folders]); const focusActiveRequest = useCallback( ( @@ -160,7 +162,7 @@ export function Sidebar({ className }: Props) { ); const handleSelect = useCallback( - (id: string) => { + async (id: string) => { const tree = treeParentMap[id ?? 'n/a'] ?? null; const children = tree?.children ?? []; const node = children.find((m) => m.item.id === id) ?? null; @@ -171,7 +173,7 @@ export function Sidebar({ className }: Props) { const { item } = node; if (item.model === 'folder') { - collapsed.set((c) => ({ ...c, [item.id]: !c[item.id] })); + await collapsed.set((c) => ({ ...c, [item.id]: !c[item.id] })); } else { routes.navigate('request', { requestId: id, @@ -339,6 +341,10 @@ export function Sidebar({ className }: Props) { if (child.item.model === 'folder') { const updateFolder = (f: Folder) => ({ ...f, sortPriority, folderId }); return updateAnyFolder.mutateAsync({ id: child.item.id, update: updateFolder }); + } else if (child.item.model === 'grpc_request') { + // TODO + // const updateRequest = (r: HttpRequest) => ({ ...r, sortPriority, folderId }); + // return updateAnyRequest.mutateAsync({ id: child.item.id, update: updateRequest }); } else if (child.item.model === 'http_request') { const updateRequest = (r: HttpRequest) => ({ ...r, sortPriority, folderId }); return updateAnyRequest.mutateAsync({ id: child.item.id, update: updateRequest }); @@ -350,6 +356,10 @@ export function Sidebar({ className }: Props) { if (child.item.model === 'folder') { const updateFolder = (f: Folder) => ({ ...f, sortPriority, folderId }); await updateAnyFolder.mutateAsync({ id: child.item.id, update: updateFolder }); + } else if (child.item.model === 'grpc_request') { + // TODO + // const updateRequest = (r: HttpRequest) => ({ ...r, sortPriority, folderId }); + // await updateAnyRequest.mutateAsync({ id: child.item.id, update: updateRequest }); } else if (child.item.model === 'http_request') { const updateRequest = (r: HttpRequest) => ({ ...r, sortPriority, folderId }); await updateAnyRequest.mutateAsync({ id: child.item.id, update: updateRequest }); @@ -454,7 +464,9 @@ function SidebarItems({ itemId={child.item.id} itemName={child.item.name} itemFallbackName={ - child.item.model === 'http_request' ? fallbackRequestName(child.item) : 'New Folder' + child.item.model === 'http_request' || child.item.model === 'grpc_request' + ? fallbackRequestName(child.item) + : 'New Folder' } itemModel={child.item.model} onMove={handleMove} @@ -524,16 +536,15 @@ const SidebarItem = forwardRef(function SidebarItem( ref: ForwardedRef, ) { const activeRequest = useActiveRequest(); - const createRequest = useCreateRequest(); + const createRequest = useCreateHttpRequest(); const createFolder = useCreateFolder(); const deleteFolder = useDeleteFolder(itemId); const deleteRequest = useDeleteRequest(itemId); const duplicateRequest = useDuplicateRequest({ id: itemId, navigateAfter: true }); const sendRequest = useSendRequest(itemId); - const sendAndDownloadRequest = useSendRequest(itemId, { download: true }); const sendManyRequests = useSendManyRequests(); const latestResponse = useLatestResponse(itemId); - const updateRequest = useUpdateRequest(itemId); + const updateRequest = useUpdateHttpRequest(itemId); const updateAnyFolder = useUpdateAnyFolder(); const prompt = usePrompt(); const [editing, setEditing] = useState(false); @@ -570,7 +581,7 @@ const SidebarItem = forwardRef(function SidebarItem( ); const handleStartEditing = useCallback(() => { - if (itemModel !== 'http_request') return; + if (itemModel !== 'http_request' && itemModel !== 'grpc_request') return; setEditing(true); }, [setEditing, itemModel]); @@ -652,7 +663,7 @@ const SidebarItem = forwardRef(function SidebarItem( { key: 'sendRequest', label: 'Send', - hotKeyAction: 'request.send', + hotKeyAction: 'http_request.send', hotKeyLabelOnly: true, // Already bound in URL bar leftSlot: , onSelect: () => sendRequest.mutate(), @@ -661,7 +672,7 @@ const SidebarItem = forwardRef(function SidebarItem( { key: 'duplicateRequest', label: 'Duplicate', - hotKeyAction: 'request.duplicate', + hotKeyAction: 'http_request.duplicate', hotKeyLabelOnly: true, // Would trigger for every request (bad) leftSlot: , onSelect: () => { diff --git a/src-web/components/SidebarActions.tsx b/src-web/components/SidebarActions.tsx index f0b86e6e..421d26f9 100644 --- a/src-web/components/SidebarActions.tsx +++ b/src-web/components/SidebarActions.tsx @@ -1,6 +1,7 @@ import { memo } from 'react'; import { useCreateFolder } from '../hooks/useCreateFolder'; -import { useCreateRequest } from '../hooks/useCreateRequest'; +import { useCreateGrpcRequest } from '../hooks/useCreateGrpcRequest'; +import { useCreateHttpRequest } from '../hooks/useCreateHttpRequest'; import { useSidebarHidden } from '../hooks/useSidebarHidden'; import { trackEvent } from '../lib/analytics'; import { Dropdown } from './core/Dropdown'; @@ -8,16 +9,17 @@ import { IconButton } from './core/IconButton'; import { HStack } from './core/Stacks'; export const SidebarActions = memo(function SidebarActions() { - const createRequest = useCreateRequest(); + const createHttpRequest = useCreateHttpRequest(); + const createGrpcRequest = useCreateGrpcRequest(); const createFolder = useCreateFolder(); const { hidden, toggle } = useSidebarHidden(); return ( { + onClick={async () => { trackEvent('Sidebar', 'Toggle'); - toggle(); + await toggle(); }} className="pointer-events-auto" size="sm" @@ -28,14 +30,19 @@ export const SidebarActions = memo(function SidebarActions() { createRequest.mutate({}), + key: 'create-http-request', + label: 'HTTP Request', + hotKeyAction: 'http_request.create', + onSelect: () => createHttpRequest.mutate({}), + }, + { + key: 'create-grpc-request', + label: 'GRPC Request', + onSelect: () => createGrpcRequest.mutate({}), }, { key: 'create-folder', - label: 'New Folder', + label: 'Folder', onSelect: () => createFolder.mutate({}), }, ]} diff --git a/src-web/components/UrlBar.tsx b/src-web/components/UrlBar.tsx index bd02ee98..21f842b3 100644 --- a/src-web/components/UrlBar.tsx +++ b/src-web/components/UrlBar.tsx @@ -83,7 +83,7 @@ export const UrlBar = memo(function UrlBar({ className="w-8 mr-0.5 my-0.5" icon={isLoading ? 'update' : submitIcon} spin={isLoading} - hotkeyAction="request.send" + hotkeyAction="http_request.send" /> ) } diff --git a/src-web/components/Workspace.tsx b/src-web/components/Workspace.tsx index 975beb9d..b0b9b1e8 100644 --- a/src-web/components/Workspace.tsx +++ b/src-web/components/Workspace.tsx @@ -34,6 +34,7 @@ export default function Workspace() { const { setWidth, width, resetWidth } = useSidebarWidth(); const { hide, show, hidden } = useSidebarHidden(); const activeRequest = useActiveRequest(); + console.log('ACTIVE REQUEST', activeRequest); const windowSize = useWindowSize(); const [floating, setFloating] = useState(false); @@ -166,7 +167,7 @@ export default function Workspace() { > - {activeRequest?.name.includes('gRPC') ? ( + {activeRequest?.model === 'grpc_request' ? ( ) : ( diff --git a/src-web/components/core/SplitLayout.tsx b/src-web/components/core/SplitLayout.tsx index a073bfdf..81c6ca40 100644 --- a/src-web/components/core/SplitLayout.tsx +++ b/src-web/components/core/SplitLayout.tsx @@ -140,7 +140,7 @@ export function SplitLayout({ const activeRequestId = useActiveRequestId(); if (activeRequestId === null) { - return ; + return ; } return ( diff --git a/src-web/hooks/useActiveRequest.ts b/src-web/hooks/useActiveRequest.ts index 0c8e96df..1af9b2bf 100644 --- a/src-web/hooks/useActiveRequest.ts +++ b/src-web/hooks/useActiveRequest.ts @@ -1,9 +1,28 @@ -import type { HttpRequest } from '../lib/models'; +import { r } from 'vitest/dist/types-94cfe4b4'; +import type { GrpcRequest, HttpRequest } from '../lib/models'; import { useActiveRequestId } from './useActiveRequestId'; -import { useRequests } from './useRequests'; +import { useGrpcRequests } from './useGrpcRequests'; +import { useHttpRequests } from './useHttpRequests'; -export function useActiveRequest(): HttpRequest | null { - const requestId = useActiveRequestId(); - const requests = useRequests(); - return requests.find((r) => r.id === requestId) ?? null; +interface TypeMap { + http_request: HttpRequest; + grpc_request: GrpcRequest; +} + +export function useActiveRequest( + model?: T | undefined, +): TypeMap[T] | null { + const requestId = useActiveRequestId(); + const httpRequests = useHttpRequests(); + const grpcRequests = useGrpcRequests(); + + if (model === 'http_request') { + return (httpRequests.find((r) => r.id === requestId) ?? null) as TypeMap[T] | null; + } else if (model === 'grpc_request') { + return (grpcRequests.find((r) => r.id === requestId) ?? null) as TypeMap[T] | null; + } else { + return (grpcRequests.find((r) => r.id === requestId) ?? + httpRequests.find((r) => r.id === requestId) ?? + null) as TypeMap[T] | null; + } } diff --git a/src-web/hooks/useCreateGrpcRequest.ts b/src-web/hooks/useCreateGrpcRequest.ts new file mode 100644 index 00000000..adaa7020 --- /dev/null +++ b/src-web/hooks/useCreateGrpcRequest.ts @@ -0,0 +1,55 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { invoke } from '@tauri-apps/api'; +import { trackEvent } from '../lib/analytics'; +import type { GrpcRequest, HttpRequest } from '../lib/models'; +import { useActiveEnvironmentId } from './useActiveEnvironmentId'; +import { useActiveRequest } from './useActiveRequest'; +import { useActiveWorkspaceId } from './useActiveWorkspaceId'; +import { useAppRoutes } from './useAppRoutes'; +import { grpcRequestsQueryKey } from './useGrpcRequests'; +import { httpRequestsQueryKey } from './useHttpRequests'; + +export function useCreateGrpcRequest() { + const workspaceId = useActiveWorkspaceId(); + const activeEnvironmentId = useActiveEnvironmentId(); + // const activeRequest = useActiveRequest(); + const activeRequest = null; + const routes = useAppRoutes(); + const queryClient = useQueryClient(); + + return useMutation< + GrpcRequest, + unknown, + Partial> + >({ + mutationFn: (patch) => { + if (workspaceId === null) { + throw new Error("Cannot create grpc request when there's no active workspace"); + } + if (patch.sortPriority === undefined) { + if (activeRequest != null) { + // Place above currently-active request + // patch.sortPriority = activeRequest.sortPriority + 0.0001; + } else { + // Place at the very top + patch.sortPriority = -Date.now(); + } + } + // patch.folderId = patch.folderId; // TODO: || activeRequest?.folderId; + return invoke('cmd_create_grpc_request', { workspaceId, name: '', ...patch }); + }, + onSettled: () => trackEvent('GrpcRequest', 'Create'), + onSuccess: async (request) => { + queryClient.setQueryData( + grpcRequestsQueryKey({ workspaceId: request.workspaceId }), + (requests) => [...(requests ?? []), request], + ); + // TODO: This should navigate to the new request + routes.navigate('request', { + workspaceId: request.workspaceId, + requestId: request.id, + environmentId: activeEnvironmentId ?? undefined, + }); + }, + }); +} diff --git a/src-web/hooks/useCreateRequest.ts b/src-web/hooks/useCreateHttpRequest.ts similarity index 87% rename from src-web/hooks/useCreateRequest.ts rename to src-web/hooks/useCreateHttpRequest.ts index cb367c01..19daee46 100644 --- a/src-web/hooks/useCreateRequest.ts +++ b/src-web/hooks/useCreateHttpRequest.ts @@ -6,9 +6,9 @@ import { useActiveEnvironmentId } from './useActiveEnvironmentId'; import { useActiveRequest } from './useActiveRequest'; import { useActiveWorkspaceId } from './useActiveWorkspaceId'; import { useAppRoutes } from './useAppRoutes'; -import { requestsQueryKey } from './useRequests'; +import { httpRequestsQueryKey } from './useHttpRequests'; -export function useCreateRequest() { +export function useCreateHttpRequest() { const workspaceId = useActiveWorkspaceId(); const activeEnvironmentId = useActiveEnvironmentId(); const activeRequest = useActiveRequest(); @@ -34,12 +34,12 @@ export function useCreateRequest() { } } patch.folderId = patch.folderId || activeRequest?.folderId; - return invoke('cmd_create_request', { workspaceId, name: '', ...patch }); + return invoke('cmd_create_http_request', { workspaceId, name: '', ...patch }); }, onSettled: () => trackEvent('HttpRequest', 'Create'), onSuccess: async (request) => { queryClient.setQueryData( - requestsQueryKey({ workspaceId: request.workspaceId }), + httpRequestsQueryKey({ workspaceId: request.workspaceId }), (requests) => [...(requests ?? []), request], ); routes.navigate('request', { diff --git a/src-web/hooks/useDeleteAnyRequest.tsx b/src-web/hooks/useDeleteAnyRequest.tsx index e1373419..58ba62c0 100644 --- a/src-web/hooks/useDeleteAnyRequest.tsx +++ b/src-web/hooks/useDeleteAnyRequest.tsx @@ -6,7 +6,7 @@ import { fallbackRequestName } from '../lib/fallbackRequestName'; import type { HttpRequest } from '../lib/models'; import { getRequest } from '../lib/store'; import { useConfirm } from './useConfirm'; -import { requestsQueryKey } from './useRequests'; +import { httpRequestsQueryKey } from './useHttpRequests'; import { responsesQueryKey } from './useResponses'; export function useDeleteAnyRequest() { @@ -27,7 +27,7 @@ export function useDeleteAnyRequest() { ), }); if (!confirmed) return null; - return invoke('cmd_delete_request', { requestId: id }); + return invoke('cmd_delete_http_request', { requestId: id }); }, onSettled: () => trackEvent('HttpRequest', 'Delete'), onSuccess: async (request) => { @@ -36,7 +36,7 @@ export function useDeleteAnyRequest() { const { workspaceId, id: requestId } = request; queryClient.setQueryData(responsesQueryKey({ requestId }), []); // Responses were deleted - queryClient.setQueryData(requestsQueryKey({ workspaceId }), (requests) => + queryClient.setQueryData(httpRequestsQueryKey({ workspaceId }), (requests) => (requests ?? []).filter((r) => r.id !== requestId), ); }, diff --git a/src-web/hooks/useDeleteFolder.tsx b/src-web/hooks/useDeleteFolder.tsx index 70e6c125..377c9baa 100644 --- a/src-web/hooks/useDeleteFolder.tsx +++ b/src-web/hooks/useDeleteFolder.tsx @@ -6,7 +6,7 @@ import type { Folder } from '../lib/models'; import { getFolder } from '../lib/store'; import { useConfirm } from './useConfirm'; import { foldersQueryKey } from './useFolders'; -import { requestsQueryKey } from './useRequests'; +import { httpRequestsQueryKey } from './useHttpRequests'; export function useDeleteFolder(id: string | null) { const queryClient = useQueryClient(); @@ -36,7 +36,7 @@ export function useDeleteFolder(id: string | null) { const { workspaceId } = folder; // Nesting makes it hard to clean things up, so just clear everything that could have been deleted - await queryClient.invalidateQueries(requestsQueryKey({ workspaceId })); + await queryClient.invalidateQueries(httpRequestsQueryKey({ workspaceId })); await queryClient.invalidateQueries(foldersQueryKey({ workspaceId })); }, }); diff --git a/src-web/hooks/useDeleteWorkspace.tsx b/src-web/hooks/useDeleteWorkspace.tsx index 88796bbb..36615228 100644 --- a/src-web/hooks/useDeleteWorkspace.tsx +++ b/src-web/hooks/useDeleteWorkspace.tsx @@ -6,7 +6,7 @@ import type { Workspace } from '../lib/models'; import { useActiveWorkspaceId } from './useActiveWorkspaceId'; import { useAppRoutes } from './useAppRoutes'; import { useConfirm } from './useConfirm'; -import { requestsQueryKey } from './useRequests'; +import { httpRequestsQueryKey } from './useHttpRequests'; import { workspacesQueryKey } from './useWorkspaces'; export function useDeleteWorkspace(workspace: Workspace | null) { @@ -43,8 +43,8 @@ export function useDeleteWorkspace(workspace: Workspace | null) { } // Also clean up other things that may have been deleted - queryClient.setQueryData(requestsQueryKey({ workspaceId }), []); - await queryClient.invalidateQueries(requestsQueryKey({ workspaceId })); + queryClient.setQueryData(httpRequestsQueryKey({ workspaceId }), []); + await queryClient.invalidateQueries(httpRequestsQueryKey({ workspaceId })); }, }); } diff --git a/src-web/hooks/useDuplicateRequest.ts b/src-web/hooks/useDuplicateRequest.ts index a73b6c19..f844067e 100644 --- a/src-web/hooks/useDuplicateRequest.ts +++ b/src-web/hooks/useDuplicateRequest.ts @@ -5,7 +5,7 @@ import type { HttpRequest } from '../lib/models'; import { useActiveEnvironmentId } from './useActiveEnvironmentId'; import { useActiveWorkspaceId } from './useActiveWorkspaceId'; import { useAppRoutes } from './useAppRoutes'; -import { requestsQueryKey } from './useRequests'; +import { httpRequestsQueryKey } from './useHttpRequests'; export function useDuplicateRequest({ id, @@ -21,12 +21,12 @@ export function useDuplicateRequest({ return useMutation({ mutationFn: async () => { if (id === null) throw new Error("Can't duplicate a null request"); - return invoke('cmd_duplicate_request', { id }); + return invoke('cmd_duplicate_http_request', { id }); }, onSettled: () => trackEvent('HttpRequest', 'Duplicate'), onSuccess: async (request) => { queryClient.setQueryData( - requestsQueryKey({ workspaceId: request.workspaceId }), + httpRequestsQueryKey({ workspaceId: request.workspaceId }), (requests) => [...(requests ?? []), request], ); if (navigateAfter && activeWorkspaceId !== null) { diff --git a/src-web/hooks/useGrpcRequests.ts b/src-web/hooks/useGrpcRequests.ts new file mode 100644 index 00000000..0a699b67 --- /dev/null +++ b/src-web/hooks/useGrpcRequests.ts @@ -0,0 +1,22 @@ +import { useQuery } from '@tanstack/react-query'; +import { invoke } from '@tauri-apps/api'; +import type { GrpcRequest } from '../lib/models'; +import { useActiveWorkspaceId } from './useActiveWorkspaceId'; + +export function grpcRequestsQueryKey({ workspaceId }: { workspaceId: string }) { + return ['grpc_requests', { workspaceId }]; +} + +export function useGrpcRequests() { + const workspaceId = useActiveWorkspaceId(); + return ( + useQuery({ + enabled: workspaceId != null, + queryKey: grpcRequestsQueryKey({ workspaceId: workspaceId ?? 'n/a' }), + queryFn: async () => { + if (workspaceId == null) return []; + return (await invoke('cmd_list_grpc_requests', { workspaceId })) as GrpcRequest[]; + }, + }).data ?? [] + ); +} diff --git a/src-web/hooks/useHotKey.ts b/src-web/hooks/useHotKey.ts index e9da3aea..67637624 100644 --- a/src-web/hooks/useHotKey.ts +++ b/src-web/hooks/useHotKey.ts @@ -6,11 +6,11 @@ import { useOsInfo } from './useOsInfo'; export type HotkeyAction = | 'environmentEditor.toggle' - | 'grpc.send' | 'hotkeys.showHelp' - | 'request.create' - | 'request.duplicate' - | 'request.send' + | 'grpc_request.send' + | 'http_request.create' + | 'http_request.duplicate' + | 'http_request.send' | 'requestSwitcher.next' | 'requestSwitcher.prev' | 'settings.show' @@ -20,11 +20,11 @@ export type HotkeyAction = const hotkeys: Record = { 'environmentEditor.toggle': ['CmdCtrl+Shift+e'], - 'grpc.send': ['CmdCtrl+Enter', 'CmdCtrl+r'], + 'grpc_request.send': ['CmdCtrl+Enter', 'CmdCtrl+r'], 'hotkeys.showHelp': ['CmdCtrl+Shift+/'], - 'request.create': ['CmdCtrl+n'], - 'request.duplicate': ['CmdCtrl+d'], - 'request.send': ['CmdCtrl+Enter', 'CmdCtrl+r'], + 'http_request.create': ['CmdCtrl+n'], + 'http_request.duplicate': ['CmdCtrl+d'], + 'http_request.send': ['CmdCtrl+Enter', 'CmdCtrl+r'], 'requestSwitcher.next': ['Control+Shift+Tab'], 'requestSwitcher.prev': ['Control+Tab'], 'settings.show': ['CmdCtrl+,'], @@ -35,11 +35,11 @@ const hotkeys: Record = { const hotkeyLabels: Record = { 'environmentEditor.toggle': 'Edit Environments', - 'grpc.send': 'Send Message', + 'grpc_request.send': 'Send Message', 'hotkeys.showHelp': 'Show Keyboard Shortcuts', - 'request.create': 'New Request', - 'request.duplicate': 'Duplicate Request', - 'request.send': 'Send Request', + 'http_request.create': 'New Request', + 'http_request.duplicate': 'Duplicate Request', + 'http_request.send': 'Send Request', 'requestSwitcher.next': 'Go To Previous Request', 'requestSwitcher.prev': 'Go To Next Request', 'settings.show': 'Open Settings', diff --git a/src-web/hooks/useRequests.ts b/src-web/hooks/useHttpRequests.ts similarity index 62% rename from src-web/hooks/useRequests.ts rename to src-web/hooks/useHttpRequests.ts index c0743e72..a5896dfa 100644 --- a/src-web/hooks/useRequests.ts +++ b/src-web/hooks/useHttpRequests.ts @@ -3,19 +3,19 @@ import { invoke } from '@tauri-apps/api'; import type { HttpRequest } from '../lib/models'; import { useActiveWorkspaceId } from './useActiveWorkspaceId'; -export function requestsQueryKey({ workspaceId }: { workspaceId: string }) { +export function httpRequestsQueryKey({ workspaceId }: { workspaceId: string }) { return ['http_requests', { workspaceId }]; } -export function useRequests() { +export function useHttpRequests() { const workspaceId = useActiveWorkspaceId(); return ( useQuery({ enabled: workspaceId != null, - queryKey: requestsQueryKey({ workspaceId: workspaceId ?? 'n/a' }), + queryKey: httpRequestsQueryKey({ workspaceId: workspaceId ?? 'n/a' }), queryFn: async () => { if (workspaceId == null) return []; - return (await invoke('cmd_list_requests', { workspaceId })) as HttpRequest[]; + return (await invoke('cmd_list_http_requests', { workspaceId })) as HttpRequest[]; }, }).data ?? [] ); diff --git a/src-web/hooks/useRequest.ts b/src-web/hooks/useRequest.ts index fe0f717a..7e93fbb1 100644 --- a/src-web/hooks/useRequest.ts +++ b/src-web/hooks/useRequest.ts @@ -1,7 +1,7 @@ import type { HttpRequest } from '../lib/models'; -import { useRequests } from './useRequests'; +import { useHttpRequests } from './useHttpRequests'; export function useRequest(id: string | null): HttpRequest | null { - const requests = useRequests(); + const requests = useHttpRequests(); return requests.find((r) => r.id === id) ?? null; } diff --git a/src-web/hooks/useUpdateAnyRequest.ts b/src-web/hooks/useUpdateAnyHttpRequest.ts similarity index 79% rename from src-web/hooks/useUpdateAnyRequest.ts rename to src-web/hooks/useUpdateAnyHttpRequest.ts index 85e89d0f..a18bddc3 100644 --- a/src-web/hooks/useUpdateAnyRequest.ts +++ b/src-web/hooks/useUpdateAnyHttpRequest.ts @@ -2,9 +2,9 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { invoke } from '@tauri-apps/api'; import type { HttpRequest } from '../lib/models'; import { getRequest } from '../lib/store'; -import { requestsQueryKey } from './useRequests'; +import { httpRequestsQueryKey } from './useHttpRequests'; -export function useUpdateAnyRequest() { +export function useUpdateAnyHttpRequest() { const queryClient = useQueryClient(); return useMutation< @@ -20,14 +20,14 @@ export function useUpdateAnyRequest() { const patchedRequest = typeof update === 'function' ? update(request) : { ...request, ...update }; - await invoke('cmd_update_request', { request: patchedRequest }); + await invoke('cmd_update_http_request', { request: patchedRequest }); }, onMutate: async ({ id, update }) => { const request = await getRequest(id); if (request === null) return; const patchedRequest = typeof update === 'function' ? update(request) : { ...request, ...update }; - queryClient.setQueryData(requestsQueryKey(request), (requests) => + queryClient.setQueryData(httpRequestsQueryKey(request), (requests) => (requests ?? []).map((r) => (r.id === patchedRequest.id ? patchedRequest : r)), ); }, diff --git a/src-web/hooks/useUpdateRequest.ts b/src-web/hooks/useUpdateHttpRequest.ts similarity index 62% rename from src-web/hooks/useUpdateRequest.ts rename to src-web/hooks/useUpdateHttpRequest.ts index 6c6b82d6..3a4faee7 100644 --- a/src-web/hooks/useUpdateRequest.ts +++ b/src-web/hooks/useUpdateHttpRequest.ts @@ -1,9 +1,9 @@ import { useMutation } from '@tanstack/react-query'; import type { HttpRequest } from '../lib/models'; -import { useUpdateAnyRequest } from './useUpdateAnyRequest'; +import { useUpdateAnyHttpRequest } from './useUpdateAnyHttpRequest'; -export function useUpdateRequest(id: string | null) { - const updateAnyRequest = useUpdateAnyRequest(); +export function useUpdateHttpRequest(id: string | null) { + const updateAnyRequest = useUpdateAnyHttpRequest(); return useMutation | ((r: HttpRequest) => HttpRequest)>({ mutationFn: async (update) => updateAnyRequest.mutateAsync({ id: id ?? 'n/a', update }), }); diff --git a/src-web/lib/analytics.ts b/src-web/lib/analytics.ts index b72f86a5..742ae760 100644 --- a/src-web/lib/analytics.ts +++ b/src-web/lib/analytics.ts @@ -9,6 +9,7 @@ export function trackEvent( | 'Workspace' | 'Environment' | 'Folder' + | 'GrpcRequest' | 'HttpRequest' | 'HttpResponse' | 'KeyValue', diff --git a/src-web/lib/fallbackRequestName.ts b/src-web/lib/fallbackRequestName.ts index 6746377b..bb7a0ed5 100644 --- a/src-web/lib/fallbackRequestName.ts +++ b/src-web/lib/fallbackRequestName.ts @@ -1,6 +1,6 @@ -import type { HttpRequest } from './models'; +import type { GrpcRequest, HttpRequest } from './models'; -export function fallbackRequestName(r: HttpRequest | null): string { +export function fallbackRequestName(r: HttpRequest | GrpcRequest | null): string { if (r == null) return ''; if (r.name) { @@ -9,7 +9,7 @@ export function fallbackRequestName(r: HttpRequest | null): string { const withoutVariables = r.url.replace(/\$\{\[[^\]]+]}/g, ''); if (withoutVariables.trim() === '') { - return 'New Request'; + return r.model === 'http_request' ? 'New HTTP Request' : 'new gRPC Request'; } const fixedUrl = r.url.match(/^https?:\/\//) ? r.url : 'http://' + r.url; diff --git a/src-web/lib/models.ts b/src-web/lib/models.ts index 42dab3ba..8a752f17 100644 --- a/src-web/lib/models.ts +++ b/src-web/lib/models.ts @@ -12,6 +12,7 @@ export const AUTH_TYPE_BEARER = 'bearer'; export type Model = | Settings | Workspace + | GrpcRequest | HttpRequest | HttpResponse | KeyValue @@ -101,6 +102,18 @@ export interface HttpUrlParameter { enabled?: boolean; } +export interface GrpcRequest extends BaseModel { + readonly workspaceId: string; + readonly model: 'grpc_request'; + folderId: string | null; + sortPriority: number; + name: string; + url: string; + service: string | null; + method: string | null; + message: string; +} + export interface HttpRequest extends BaseModel { readonly workspaceId: string; readonly model: 'http_request'; diff --git a/src-web/lib/store.ts b/src-web/lib/store.ts index 3c1896a2..97bc437f 100644 --- a/src-web/lib/store.ts +++ b/src-web/lib/store.ts @@ -7,7 +7,7 @@ export async function getSettings(): Promise { export async function getRequest(id: string | null): Promise { if (id === null) return null; - const request: HttpRequest = (await invoke('cmd_get_request', { id })) ?? null; + const request: HttpRequest = (await invoke('cmd_get_http_request', { id })) ?? null; if (request == null) { return null; }