mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-25 10:18:31 +02:00
Make prompt() to return null on cancel
This commit is contained in:
@@ -19,7 +19,7 @@
|
||||
"start": "npm run app-dev",
|
||||
"app-build": "tauri build",
|
||||
"app-dev": "tauri dev --no-watch --config ./src-tauri/tauri-dev.conf.json",
|
||||
"bootstrap": "run-p bootstrap:* && npm run --workspace plugin-runtime build",
|
||||
"bootstrap": "run-p bootstrap:* && npm run --workspaces --if-present bootstrap",
|
||||
"bootstrap:vendor-node": "node scripts/vendor-node.cjs",
|
||||
"bootstrap:vendor-plugins": "node scripts/vendor-plugins.cjs",
|
||||
"bootstrap:vendor-protoc": "node scripts/vendor-protoc.cjs",
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
{
|
||||
"name": "@yaakapp/api",
|
||||
"version": "0.2.13",
|
||||
"version": "0.2.15",
|
||||
"main": "lib/index.js",
|
||||
"typings": "./lib/index.d.ts",
|
||||
"files": [
|
||||
"lib/**/*"
|
||||
],
|
||||
"scripts": {
|
||||
"bootstrap": "npm run build",
|
||||
"build": "run-s build:copy-types build:tsc",
|
||||
"build:tsc": "tsc",
|
||||
"build:copy-types": "run-p build:copy-types:*",
|
||||
|
||||
@@ -59,21 +59,15 @@ export type ImportResponse = { resources: ImportResources, };
|
||||
|
||||
export type InternalEvent = { id: string, pluginRefId: string, replyId: string | null, payload: InternalEventPayload, windowContext: WindowContext, };
|
||||
|
||||
export type InternalEventPayload = { "type": "boot_request" } & BootRequest | { "type": "boot_response" } & BootResponse | { "type": "reload_request" } | { "type": "reload_response" } | { "type": "terminate_request" } | { "type": "terminate_response" } | { "type": "import_request" } & ImportRequest | { "type": "import_response" } & ImportResponse | { "type": "filter_request" } & FilterRequest | { "type": "filter_response" } & FilterResponse | { "type": "export_http_request_request" } & ExportHttpRequestRequest | { "type": "export_http_request_response" } & ExportHttpRequestResponse | { "type": "send_http_request_request" } & SendHttpRequestRequest | { "type": "send_http_request_response" } & SendHttpRequestResponse | { "type": "get_http_request_actions_request" } & GetHttpRequestActionsRequest | { "type": "get_http_request_actions_response" } & GetHttpRequestActionsResponse | { "type": "call_http_request_action_request" } & CallHttpRequestActionRequest | { "type": "get_template_functions_request" } | { "type": "get_template_functions_response" } & GetTemplateFunctionsResponse | { "type": "call_template_function_request" } & CallTemplateFunctionRequest | { "type": "call_template_function_response" } & CallTemplateFunctionResponse | { "type": "copy_text_request" } & CopyTextRequest | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "template_render_request" } & TemplateRenderRequest | { "type": "template_render_response" } & TemplateRenderResponse | { "type": "show_toast_request" } & ShowToastRequest | { "type": "show_prompt_request" } & ShowPromptRequest | { "type": "show_prompt_response" } & ShowPromptResponse | { "type": "get_http_request_by_id_request" } & GetHttpRequestByIdRequest | { "type": "get_http_request_by_id_response" } & GetHttpRequestByIdResponse | { "type": "find_http_responses_request" } & FindHttpResponsesRequest | { "type": "find_http_responses_response" } & FindHttpResponsesResponse | { "type": "empty_response" };
|
||||
export type InternalEventPayload = { "type": "boot_request" } & BootRequest | { "type": "boot_response" } & BootResponse | { "type": "reload_request" } | { "type": "reload_response" } | { "type": "terminate_request" } | { "type": "terminate_response" } | { "type": "import_request" } & ImportRequest | { "type": "import_response" } & ImportResponse | { "type": "filter_request" } & FilterRequest | { "type": "filter_response" } & FilterResponse | { "type": "export_http_request_request" } & ExportHttpRequestRequest | { "type": "export_http_request_response" } & ExportHttpRequestResponse | { "type": "send_http_request_request" } & SendHttpRequestRequest | { "type": "send_http_request_response" } & SendHttpRequestResponse | { "type": "get_http_request_actions_request" } & GetHttpRequestActionsRequest | { "type": "get_http_request_actions_response" } & GetHttpRequestActionsResponse | { "type": "call_http_request_action_request" } & CallHttpRequestActionRequest | { "type": "get_template_functions_request" } | { "type": "get_template_functions_response" } & GetTemplateFunctionsResponse | { "type": "call_template_function_request" } & CallTemplateFunctionRequest | { "type": "call_template_function_response" } & CallTemplateFunctionResponse | { "type": "copy_text_request" } & CopyTextRequest | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "template_render_request" } & TemplateRenderRequest | { "type": "template_render_response" } & TemplateRenderResponse | { "type": "show_toast_request" } & ShowToastRequest | { "type": "prompt_text_request" } & PromptTextRequest | { "type": "prompt_text_response" } & PromptTextResponse | { "type": "get_http_request_by_id_request" } & GetHttpRequestByIdRequest | { "type": "get_http_request_by_id_response" } & GetHttpRequestByIdResponse | { "type": "find_http_responses_request" } & FindHttpResponsesRequest | { "type": "find_http_responses_response" } & FindHttpResponsesResponse | { "type": "empty_response" };
|
||||
|
||||
export type OpenFileFilter = { name: string, extensions: Array<string>, };
|
||||
export type OpenFileFilter = { name: string,
|
||||
/**
|
||||
* File extensions to require
|
||||
*/
|
||||
extensions: Array<string>, };
|
||||
|
||||
export type RenderHttpRequestRequest = { httpRequest: HttpRequest, purpose: RenderPurpose, };
|
||||
|
||||
export type RenderHttpRequestResponse = { httpRequest: HttpRequest, };
|
||||
|
||||
export type RenderPurpose = "send" | "preview";
|
||||
|
||||
export type SendHttpRequestRequest = { httpRequest: HttpRequest, };
|
||||
|
||||
export type SendHttpRequestResponse = { httpResponse: HttpResponse, };
|
||||
|
||||
export type ShowPromptRequest = { id: string, title: string, label: string, description?: string, defaultValue?: string, placeholder?: string,
|
||||
export type PromptTextRequest = { id: string, title: string, label: string, description?: string, defaultValue?: string, placeholder?: string,
|
||||
/**
|
||||
* Text to add to the confirmation button
|
||||
*/
|
||||
@@ -87,7 +81,17 @@ cancelText?: string,
|
||||
*/
|
||||
require?: boolean, };
|
||||
|
||||
export type ShowPromptResponse = { value: string, };
|
||||
export type PromptTextResponse = { value: string | null, };
|
||||
|
||||
export type RenderHttpRequestRequest = { httpRequest: HttpRequest, purpose: RenderPurpose, };
|
||||
|
||||
export type RenderHttpRequestResponse = { httpRequest: HttpRequest, };
|
||||
|
||||
export type RenderPurpose = "send" | "preview";
|
||||
|
||||
export type SendHttpRequestRequest = { httpRequest: HttpRequest, };
|
||||
|
||||
export type SendHttpRequestResponse = { httpResponse: HttpResponse, };
|
||||
|
||||
export type ShowToastRequest = { message: string, color?: Color, icon?: Icon, };
|
||||
|
||||
@@ -95,19 +99,131 @@ export type TemplateFunction = { name: string, aliases?: Array<string>, args: Ar
|
||||
|
||||
export type TemplateFunctionArg = { "type": "text" } & TemplateFunctionTextArg | { "type": "select" } & TemplateFunctionSelectArg | { "type": "checkbox" } & TemplateFunctionCheckboxArg | { "type": "http_request" } & TemplateFunctionHttpRequestArg | { "type": "file" } & TemplateFunctionFileArg;
|
||||
|
||||
export type TemplateFunctionBaseArg = { name: string, optional?: boolean, label?: string, defaultValue?: string, };
|
||||
export type TemplateFunctionBaseArg = {
|
||||
/**
|
||||
* The name of the argument. Should be `camelCase` format
|
||||
*/
|
||||
name: string,
|
||||
/**
|
||||
* Whether the user must fill in the argument
|
||||
*/
|
||||
optional?: boolean,
|
||||
/**
|
||||
* The label of the input
|
||||
*/
|
||||
label?: string,
|
||||
/**
|
||||
* The default value
|
||||
*/
|
||||
defaultValue?: string, };
|
||||
|
||||
export type TemplateFunctionCheckboxArg = { name: string, optional?: boolean, label?: string, defaultValue?: string, };
|
||||
export type TemplateFunctionCheckboxArg = {
|
||||
/**
|
||||
* The name of the argument. Should be `camelCase` format
|
||||
*/
|
||||
name: string,
|
||||
/**
|
||||
* Whether the user must fill in the argument
|
||||
*/
|
||||
optional?: boolean,
|
||||
/**
|
||||
* The label of the input
|
||||
*/
|
||||
label?: string,
|
||||
/**
|
||||
* The default value
|
||||
*/
|
||||
defaultValue?: string, };
|
||||
|
||||
export type TemplateFunctionFileArg = { title: string, multiple?: boolean, directory?: boolean, defaultPath?: string, filters?: Array<OpenFileFilter>, name: string, optional?: boolean, label?: string, defaultValue?: string, };
|
||||
export type TemplateFunctionFileArg = {
|
||||
/**
|
||||
* The title of the file selection window
|
||||
*/
|
||||
title: string,
|
||||
/**
|
||||
* Allow selecting multiple files
|
||||
*/
|
||||
multiple?: boolean, directory?: boolean, defaultPath?: string, filters?: Array<OpenFileFilter>,
|
||||
/**
|
||||
* The name of the argument. Should be `camelCase` format
|
||||
*/
|
||||
name: string,
|
||||
/**
|
||||
* Whether the user must fill in the argument
|
||||
*/
|
||||
optional?: boolean,
|
||||
/**
|
||||
* The label of the input
|
||||
*/
|
||||
label?: string,
|
||||
/**
|
||||
* The default value
|
||||
*/
|
||||
defaultValue?: string, };
|
||||
|
||||
export type TemplateFunctionHttpRequestArg = { name: string, optional?: boolean, label?: string, defaultValue?: string, };
|
||||
export type TemplateFunctionHttpRequestArg = {
|
||||
/**
|
||||
* The name of the argument. Should be `camelCase` format
|
||||
*/
|
||||
name: string,
|
||||
/**
|
||||
* Whether the user must fill in the argument
|
||||
*/
|
||||
optional?: boolean,
|
||||
/**
|
||||
* The label of the input
|
||||
*/
|
||||
label?: string,
|
||||
/**
|
||||
* The default value
|
||||
*/
|
||||
defaultValue?: string, };
|
||||
|
||||
export type TemplateFunctionSelectArg = { options: Array<TemplateFunctionSelectOption>, name: string, optional?: boolean, label?: string, defaultValue?: string, };
|
||||
export type TemplateFunctionSelectArg = {
|
||||
/**
|
||||
* The options that will be available in the select input
|
||||
*/
|
||||
options: Array<TemplateFunctionSelectOption>,
|
||||
/**
|
||||
* The name of the argument. Should be `camelCase` format
|
||||
*/
|
||||
name: string,
|
||||
/**
|
||||
* Whether the user must fill in the argument
|
||||
*/
|
||||
optional?: boolean,
|
||||
/**
|
||||
* The label of the input
|
||||
*/
|
||||
label?: string,
|
||||
/**
|
||||
* The default value
|
||||
*/
|
||||
defaultValue?: string, };
|
||||
|
||||
export type TemplateFunctionSelectOption = { name: string, value: string, };
|
||||
export type TemplateFunctionSelectOption = { label: string, value: string, };
|
||||
|
||||
export type TemplateFunctionTextArg = { placeholder?: string, name: string, optional?: boolean, label?: string, defaultValue?: string, };
|
||||
export type TemplateFunctionTextArg = {
|
||||
/**
|
||||
* Placeholder for the text input
|
||||
*/
|
||||
placeholder?: string,
|
||||
/**
|
||||
* The name of the argument. Should be `camelCase` format
|
||||
*/
|
||||
name: string,
|
||||
/**
|
||||
* Whether the user must fill in the argument
|
||||
*/
|
||||
optional?: boolean,
|
||||
/**
|
||||
* The label of the input
|
||||
*/
|
||||
label?: string,
|
||||
/**
|
||||
* The default value
|
||||
*/
|
||||
defaultValue?: string, };
|
||||
|
||||
export type TemplateRenderRequest = { data: JsonValue, purpose: RenderPurpose, };
|
||||
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import {
|
||||
ShowPromptRequest,
|
||||
ShowPromptResponse,
|
||||
TemplateRenderRequest,
|
||||
TemplateRenderResponse,
|
||||
FindHttpResponsesRequest,
|
||||
FindHttpResponsesResponse,
|
||||
GetHttpRequestByIdRequest,
|
||||
GetHttpRequestByIdResponse,
|
||||
PromptTextRequest,
|
||||
PromptTextResponse,
|
||||
RenderHttpRequestRequest,
|
||||
RenderHttpRequestResponse,
|
||||
SendHttpRequestRequest,
|
||||
SendHttpRequestResponse,
|
||||
ShowToastRequest,
|
||||
TemplateRenderRequest,
|
||||
TemplateRenderResponse,
|
||||
} from '..';
|
||||
|
||||
export type Context = {
|
||||
@@ -22,7 +22,7 @@ export type Context = {
|
||||
show(args: ShowToastRequest): void;
|
||||
};
|
||||
prompt: {
|
||||
show(args: ShowPromptRequest): Promise<ShowPromptResponse['value']>;
|
||||
text(args: PromptTextRequest): Promise<PromptTextResponse['value']>;
|
||||
};
|
||||
httpRequest: {
|
||||
send(args: SendHttpRequestRequest): Promise<SendHttpRequestResponse['httpResponse']>;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"name": "@yaakapp-internal/plugin-runtime",
|
||||
"scripts": {
|
||||
"bootstrap": "npm run build",
|
||||
"build": "run-p build:*",
|
||||
"build:main": "esbuild src/index.ts --bundle --platform=node --outfile=../src-tauri/vendored/plugin-runtime/index.cjs",
|
||||
"build:worker": "esbuild src/index.worker.ts --bundle --platform=node --outfile=../src-tauri/vendored/plugin-runtime/index.worker.cjs",
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
import {
|
||||
RenderHttpRequestResponse,
|
||||
TemplateRenderResponse,
|
||||
WindowContext,
|
||||
} from '@yaakapp-internal/plugin';
|
||||
import {
|
||||
BootRequest,
|
||||
Context,
|
||||
FindHttpResponsesResponse,
|
||||
GetHttpRequestByIdResponse,
|
||||
HttpRequestAction,
|
||||
ImportResponse,
|
||||
InternalEvent,
|
||||
InternalEventPayload,
|
||||
PromptTextResponse,
|
||||
RenderHttpRequestResponse,
|
||||
SendHttpRequestResponse,
|
||||
ShowPromptResponse,
|
||||
TemplateFunction,
|
||||
} from '@yaakapp/api';
|
||||
TemplateRenderResponse,
|
||||
WindowContext,
|
||||
} from '@yaakapp-internal/plugin';
|
||||
import { Context } from '@yaakapp/api';
|
||||
import { HttpRequestActionPlugin } from '@yaakapp/api/lib/plugins/HttpRequestActionPlugin';
|
||||
import { TemplateFunctionPlugin } from '@yaakapp/api/lib/plugins/TemplateFunctionPlugin';
|
||||
import interceptStdout from 'intercept-stdout';
|
||||
@@ -140,9 +138,9 @@ async function initialize() {
|
||||
},
|
||||
},
|
||||
prompt: {
|
||||
async show(args) {
|
||||
const reply: ShowPromptResponse = await sendAndWaitForReply(event.windowContext, {
|
||||
type: 'show_prompt_request',
|
||||
async text(args) {
|
||||
const reply: PromptTextResponse = await sendAndWaitForReply(event.windowContext, {
|
||||
type: 'prompt_text_request',
|
||||
...args,
|
||||
});
|
||||
return reply.value;
|
||||
|
||||
@@ -45,6 +45,7 @@ pub async fn send_http_request<R: Runtime>(
|
||||
&WindowContext::from_window(window),
|
||||
RenderPurpose::Send,
|
||||
);
|
||||
|
||||
let rendered_request =
|
||||
render_http_request(&request, &workspace, environment.as_ref(), &cb).await;
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ use yaak_plugin_runtime::events::{
|
||||
BootResponse, CallHttpRequestActionRequest, FilterResponse, FindHttpResponsesResponse,
|
||||
GetHttpRequestActionsResponse, GetHttpRequestByIdResponse, GetTemplateFunctionsResponse, Icon,
|
||||
InternalEvent, InternalEventPayload, RenderHttpRequestResponse, RenderPurpose,
|
||||
SendHttpRequestResponse, ShowPromptResponse, ShowToastRequest, TemplateRenderResponse,
|
||||
SendHttpRequestResponse, PromptTextResponse, ShowToastRequest, TemplateRenderResponse,
|
||||
WindowContext,
|
||||
};
|
||||
use yaak_plugin_runtime::plugin_handle::PluginHandle;
|
||||
@@ -1081,6 +1081,18 @@ async fn cmd_send_http_request(
|
||||
// that has not yet been saved in the DB.
|
||||
request: HttpRequest,
|
||||
) -> Result<HttpResponse, String> {
|
||||
let response = create_default_http_response(&window, &request.id)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let (cancel_tx, mut cancel_rx) = tokio::sync::watch::channel(false);
|
||||
window.listen_any(
|
||||
format!("cancel_http_response_{}", response.id),
|
||||
move |_event| {
|
||||
let _ = cancel_tx.send(true);
|
||||
},
|
||||
);
|
||||
|
||||
let environment = match environment_id {
|
||||
Some(id) => match get_environment(&window, id).await {
|
||||
Ok(env) => Some(env),
|
||||
@@ -1101,18 +1113,6 @@ async fn cmd_send_http_request(
|
||||
None => None,
|
||||
};
|
||||
|
||||
let response = create_default_http_response(&window, &request.id)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let (cancel_tx, mut cancel_rx) = tokio::sync::watch::channel(false);
|
||||
window.listen_any(
|
||||
format!("cancel_http_response_{}", response.id),
|
||||
move |_event| {
|
||||
let _ = cancel_tx.send(true);
|
||||
},
|
||||
);
|
||||
|
||||
send_http_request(
|
||||
&window,
|
||||
&request,
|
||||
@@ -2171,18 +2171,18 @@ async fn call_frontend<T: Serialize + Clone, R: Runtime>(
|
||||
window: WebviewWindow<R>,
|
||||
event_name: &str,
|
||||
args: T,
|
||||
) -> ShowPromptResponse {
|
||||
) -> PromptTextResponse {
|
||||
let reply_id = format!("{event_name}_reply");
|
||||
let payload = FrontendCall {
|
||||
args,
|
||||
reply_id: reply_id.clone(),
|
||||
};
|
||||
window.emit_to(window.label(), event_name, payload).unwrap();
|
||||
let (tx, mut rx) = tokio::sync::watch::channel(ShowPromptResponse::default());
|
||||
let (tx, mut rx) = tokio::sync::watch::channel(PromptTextResponse::default());
|
||||
|
||||
let event_id = window.clone().listen(reply_id, move |ev| {
|
||||
println!("GOT REPLY {ev:?}");
|
||||
let resp: ShowPromptResponse = serde_json::from_str(ev.payload()).unwrap();
|
||||
let resp: PromptTextResponse = serde_json::from_str(ev.payload()).unwrap();
|
||||
_ = tx.send(resp);
|
||||
});
|
||||
|
||||
@@ -2220,11 +2220,11 @@ async fn handle_plugin_event<R: Runtime>(
|
||||
};
|
||||
None
|
||||
}
|
||||
InternalEventPayload::ShowPromptRequest(req) => {
|
||||
InternalEventPayload::PromptTextRequest(req) => {
|
||||
let window = get_window_from_window_context(app_handle, &window_context)
|
||||
.expect("Failed to find window for render");
|
||||
let resp = call_frontend(window, "show_prompt", req).await;
|
||||
Some(InternalEventPayload::ShowPromptResponse(resp))
|
||||
Some(InternalEventPayload::PromptTextResponse(resp))
|
||||
}
|
||||
InternalEventPayload::FindHttpResponsesRequest(req) => {
|
||||
let http_responses = list_http_responses(
|
||||
|
||||
@@ -59,21 +59,15 @@ export type ImportResponse = { resources: ImportResources, };
|
||||
|
||||
export type InternalEvent = { id: string, pluginRefId: string, replyId: string | null, payload: InternalEventPayload, windowContext: WindowContext, };
|
||||
|
||||
export type InternalEventPayload = { "type": "boot_request" } & BootRequest | { "type": "boot_response" } & BootResponse | { "type": "reload_request" } | { "type": "reload_response" } | { "type": "terminate_request" } | { "type": "terminate_response" } | { "type": "import_request" } & ImportRequest | { "type": "import_response" } & ImportResponse | { "type": "filter_request" } & FilterRequest | { "type": "filter_response" } & FilterResponse | { "type": "export_http_request_request" } & ExportHttpRequestRequest | { "type": "export_http_request_response" } & ExportHttpRequestResponse | { "type": "send_http_request_request" } & SendHttpRequestRequest | { "type": "send_http_request_response" } & SendHttpRequestResponse | { "type": "get_http_request_actions_request" } & GetHttpRequestActionsRequest | { "type": "get_http_request_actions_response" } & GetHttpRequestActionsResponse | { "type": "call_http_request_action_request" } & CallHttpRequestActionRequest | { "type": "get_template_functions_request" } | { "type": "get_template_functions_response" } & GetTemplateFunctionsResponse | { "type": "call_template_function_request" } & CallTemplateFunctionRequest | { "type": "call_template_function_response" } & CallTemplateFunctionResponse | { "type": "copy_text_request" } & CopyTextRequest | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "template_render_request" } & TemplateRenderRequest | { "type": "template_render_response" } & TemplateRenderResponse | { "type": "show_toast_request" } & ShowToastRequest | { "type": "show_prompt_request" } & ShowPromptRequest | { "type": "show_prompt_response" } & ShowPromptResponse | { "type": "get_http_request_by_id_request" } & GetHttpRequestByIdRequest | { "type": "get_http_request_by_id_response" } & GetHttpRequestByIdResponse | { "type": "find_http_responses_request" } & FindHttpResponsesRequest | { "type": "find_http_responses_response" } & FindHttpResponsesResponse | { "type": "empty_response" };
|
||||
export type InternalEventPayload = { "type": "boot_request" } & BootRequest | { "type": "boot_response" } & BootResponse | { "type": "reload_request" } | { "type": "reload_response" } | { "type": "terminate_request" } | { "type": "terminate_response" } | { "type": "import_request" } & ImportRequest | { "type": "import_response" } & ImportResponse | { "type": "filter_request" } & FilterRequest | { "type": "filter_response" } & FilterResponse | { "type": "export_http_request_request" } & ExportHttpRequestRequest | { "type": "export_http_request_response" } & ExportHttpRequestResponse | { "type": "send_http_request_request" } & SendHttpRequestRequest | { "type": "send_http_request_response" } & SendHttpRequestResponse | { "type": "get_http_request_actions_request" } & GetHttpRequestActionsRequest | { "type": "get_http_request_actions_response" } & GetHttpRequestActionsResponse | { "type": "call_http_request_action_request" } & CallHttpRequestActionRequest | { "type": "get_template_functions_request" } | { "type": "get_template_functions_response" } & GetTemplateFunctionsResponse | { "type": "call_template_function_request" } & CallTemplateFunctionRequest | { "type": "call_template_function_response" } & CallTemplateFunctionResponse | { "type": "copy_text_request" } & CopyTextRequest | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "template_render_request" } & TemplateRenderRequest | { "type": "template_render_response" } & TemplateRenderResponse | { "type": "show_toast_request" } & ShowToastRequest | { "type": "prompt_text_request" } & PromptTextRequest | { "type": "prompt_text_response" } & PromptTextResponse | { "type": "get_http_request_by_id_request" } & GetHttpRequestByIdRequest | { "type": "get_http_request_by_id_response" } & GetHttpRequestByIdResponse | { "type": "find_http_responses_request" } & FindHttpResponsesRequest | { "type": "find_http_responses_response" } & FindHttpResponsesResponse | { "type": "empty_response" };
|
||||
|
||||
export type OpenFileFilter = { name: string, extensions: Array<string>, };
|
||||
export type OpenFileFilter = { name: string,
|
||||
/**
|
||||
* File extensions to require
|
||||
*/
|
||||
extensions: Array<string>, };
|
||||
|
||||
export type RenderHttpRequestRequest = { httpRequest: HttpRequest, purpose: RenderPurpose, };
|
||||
|
||||
export type RenderHttpRequestResponse = { httpRequest: HttpRequest, };
|
||||
|
||||
export type RenderPurpose = "send" | "preview";
|
||||
|
||||
export type SendHttpRequestRequest = { httpRequest: HttpRequest, };
|
||||
|
||||
export type SendHttpRequestResponse = { httpResponse: HttpResponse, };
|
||||
|
||||
export type ShowPromptRequest = { id: string, title: string, label: string, description?: string, defaultValue?: string, placeholder?: string,
|
||||
export type PromptTextRequest = { id: string, title: string, label: string, description?: string, defaultValue?: string, placeholder?: string,
|
||||
/**
|
||||
* Text to add to the confirmation button
|
||||
*/
|
||||
@@ -87,7 +81,17 @@ cancelText?: string,
|
||||
*/
|
||||
require?: boolean, };
|
||||
|
||||
export type ShowPromptResponse = { value: string, };
|
||||
export type PromptTextResponse = { value: string | null, };
|
||||
|
||||
export type RenderHttpRequestRequest = { httpRequest: HttpRequest, purpose: RenderPurpose, };
|
||||
|
||||
export type RenderHttpRequestResponse = { httpRequest: HttpRequest, };
|
||||
|
||||
export type RenderPurpose = "send" | "preview";
|
||||
|
||||
export type SendHttpRequestRequest = { httpRequest: HttpRequest, };
|
||||
|
||||
export type SendHttpRequestResponse = { httpResponse: HttpResponse, };
|
||||
|
||||
export type ShowToastRequest = { message: string, color?: Color, icon?: Icon, };
|
||||
|
||||
@@ -95,19 +99,131 @@ export type TemplateFunction = { name: string, aliases?: Array<string>, args: Ar
|
||||
|
||||
export type TemplateFunctionArg = { "type": "text" } & TemplateFunctionTextArg | { "type": "select" } & TemplateFunctionSelectArg | { "type": "checkbox" } & TemplateFunctionCheckboxArg | { "type": "http_request" } & TemplateFunctionHttpRequestArg | { "type": "file" } & TemplateFunctionFileArg;
|
||||
|
||||
export type TemplateFunctionBaseArg = { name: string, optional?: boolean, label?: string, defaultValue?: string, };
|
||||
export type TemplateFunctionBaseArg = {
|
||||
/**
|
||||
* The name of the argument. Should be `camelCase` format
|
||||
*/
|
||||
name: string,
|
||||
/**
|
||||
* Whether the user must fill in the argument
|
||||
*/
|
||||
optional?: boolean,
|
||||
/**
|
||||
* The label of the input
|
||||
*/
|
||||
label?: string,
|
||||
/**
|
||||
* The default value
|
||||
*/
|
||||
defaultValue?: string, };
|
||||
|
||||
export type TemplateFunctionCheckboxArg = { name: string, optional?: boolean, label?: string, defaultValue?: string, };
|
||||
export type TemplateFunctionCheckboxArg = {
|
||||
/**
|
||||
* The name of the argument. Should be `camelCase` format
|
||||
*/
|
||||
name: string,
|
||||
/**
|
||||
* Whether the user must fill in the argument
|
||||
*/
|
||||
optional?: boolean,
|
||||
/**
|
||||
* The label of the input
|
||||
*/
|
||||
label?: string,
|
||||
/**
|
||||
* The default value
|
||||
*/
|
||||
defaultValue?: string, };
|
||||
|
||||
export type TemplateFunctionFileArg = { title: string, multiple?: boolean, directory?: boolean, defaultPath?: string, filters?: Array<OpenFileFilter>, name: string, optional?: boolean, label?: string, defaultValue?: string, };
|
||||
export type TemplateFunctionFileArg = {
|
||||
/**
|
||||
* The title of the file selection window
|
||||
*/
|
||||
title: string,
|
||||
/**
|
||||
* Allow selecting multiple files
|
||||
*/
|
||||
multiple?: boolean, directory?: boolean, defaultPath?: string, filters?: Array<OpenFileFilter>,
|
||||
/**
|
||||
* The name of the argument. Should be `camelCase` format
|
||||
*/
|
||||
name: string,
|
||||
/**
|
||||
* Whether the user must fill in the argument
|
||||
*/
|
||||
optional?: boolean,
|
||||
/**
|
||||
* The label of the input
|
||||
*/
|
||||
label?: string,
|
||||
/**
|
||||
* The default value
|
||||
*/
|
||||
defaultValue?: string, };
|
||||
|
||||
export type TemplateFunctionHttpRequestArg = { name: string, optional?: boolean, label?: string, defaultValue?: string, };
|
||||
export type TemplateFunctionHttpRequestArg = {
|
||||
/**
|
||||
* The name of the argument. Should be `camelCase` format
|
||||
*/
|
||||
name: string,
|
||||
/**
|
||||
* Whether the user must fill in the argument
|
||||
*/
|
||||
optional?: boolean,
|
||||
/**
|
||||
* The label of the input
|
||||
*/
|
||||
label?: string,
|
||||
/**
|
||||
* The default value
|
||||
*/
|
||||
defaultValue?: string, };
|
||||
|
||||
export type TemplateFunctionSelectArg = { options: Array<TemplateFunctionSelectOption>, name: string, optional?: boolean, label?: string, defaultValue?: string, };
|
||||
export type TemplateFunctionSelectArg = {
|
||||
/**
|
||||
* The options that will be available in the select input
|
||||
*/
|
||||
options: Array<TemplateFunctionSelectOption>,
|
||||
/**
|
||||
* The name of the argument. Should be `camelCase` format
|
||||
*/
|
||||
name: string,
|
||||
/**
|
||||
* Whether the user must fill in the argument
|
||||
*/
|
||||
optional?: boolean,
|
||||
/**
|
||||
* The label of the input
|
||||
*/
|
||||
label?: string,
|
||||
/**
|
||||
* The default value
|
||||
*/
|
||||
defaultValue?: string, };
|
||||
|
||||
export type TemplateFunctionSelectOption = { name: string, value: string, };
|
||||
export type TemplateFunctionSelectOption = { label: string, value: string, };
|
||||
|
||||
export type TemplateFunctionTextArg = { placeholder?: string, name: string, optional?: boolean, label?: string, defaultValue?: string, };
|
||||
export type TemplateFunctionTextArg = {
|
||||
/**
|
||||
* Placeholder for the text input
|
||||
*/
|
||||
placeholder?: string,
|
||||
/**
|
||||
* The name of the argument. Should be `camelCase` format
|
||||
*/
|
||||
name: string,
|
||||
/**
|
||||
* Whether the user must fill in the argument
|
||||
*/
|
||||
optional?: boolean,
|
||||
/**
|
||||
* The label of the input
|
||||
*/
|
||||
label?: string,
|
||||
/**
|
||||
* The default value
|
||||
*/
|
||||
defaultValue?: string, };
|
||||
|
||||
export type TemplateRenderRequest = { data: JsonValue, purpose: RenderPurpose, };
|
||||
|
||||
|
||||
@@ -76,8 +76,8 @@ pub enum InternalEventPayload {
|
||||
|
||||
ShowToastRequest(ShowToastRequest),
|
||||
|
||||
ShowPromptRequest(ShowPromptRequest),
|
||||
ShowPromptResponse(ShowPromptResponse),
|
||||
PromptTextRequest(PromptTextRequest),
|
||||
PromptTextResponse(PromptTextResponse),
|
||||
|
||||
GetHttpRequestByIdRequest(GetHttpRequestByIdRequest),
|
||||
GetHttpRequestByIdResponse(GetHttpRequestByIdResponse),
|
||||
@@ -215,7 +215,7 @@ pub struct ShowToastRequest {
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
pub struct ShowPromptRequest {
|
||||
pub struct PromptTextRequest {
|
||||
// A unique ID to identify the prompt (eg. "enter-password")
|
||||
pub id: String,
|
||||
// Title to show on the prompt dialog
|
||||
@@ -242,8 +242,8 @@ pub struct ShowPromptRequest {
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
pub struct ShowPromptResponse {
|
||||
pub value: String,
|
||||
pub struct PromptTextResponse {
|
||||
pub value: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
@@ -294,6 +294,9 @@ pub struct GetTemplateFunctionsResponse {
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
pub struct TemplateFunction {
|
||||
pub name: String,
|
||||
|
||||
/// Also support alternative names. This is useful for not breaking existing
|
||||
/// tags when changing the `name` property
|
||||
#[ts(optional)]
|
||||
pub aliases: Option<Vec<String>>,
|
||||
pub args: Vec<TemplateFunctionArg>,
|
||||
@@ -314,11 +317,18 @@ pub enum TemplateFunctionArg {
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
pub struct TemplateFunctionBaseArg {
|
||||
/// The name of the argument. Should be `camelCase` format
|
||||
pub name: String,
|
||||
|
||||
/// Whether the user must fill in the argument
|
||||
#[ts(optional)]
|
||||
pub optional: Option<bool>,
|
||||
|
||||
/// The label of the input
|
||||
#[ts(optional)]
|
||||
pub label: Option<String>,
|
||||
|
||||
/// The default value
|
||||
#[ts(optional)]
|
||||
pub default_value: Option<String>,
|
||||
}
|
||||
@@ -329,6 +339,8 @@ pub struct TemplateFunctionBaseArg {
|
||||
pub struct TemplateFunctionTextArg {
|
||||
#[serde(flatten)]
|
||||
pub base: TemplateFunctionBaseArg,
|
||||
|
||||
/// Placeholder for the text input
|
||||
#[ts(optional)]
|
||||
pub placeholder: Option<String>,
|
||||
}
|
||||
@@ -347,13 +359,23 @@ pub struct TemplateFunctionHttpRequestArg {
|
||||
pub struct TemplateFunctionFileArg {
|
||||
#[serde(flatten)]
|
||||
pub base: TemplateFunctionBaseArg,
|
||||
|
||||
/// The title of the file selection window
|
||||
pub title: String,
|
||||
|
||||
/// Allow selecting multiple files
|
||||
#[ts(optional)]
|
||||
pub multiple: Option<bool>,
|
||||
|
||||
// Select a directory, not a file
|
||||
#[ts(optional)]
|
||||
pub directory: Option<bool>,
|
||||
|
||||
// Default file path for selection dialog
|
||||
#[ts(optional)]
|
||||
pub default_path: Option<String>,
|
||||
|
||||
// Specify to only allow selection of certain file extensions
|
||||
#[ts(optional)]
|
||||
pub filters: Option<Vec<OpenFileFilter>>,
|
||||
}
|
||||
@@ -363,6 +385,7 @@ pub struct TemplateFunctionFileArg {
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
pub struct OpenFileFilter {
|
||||
pub name: String,
|
||||
/// File extensions to require
|
||||
pub extensions: Vec<String>,
|
||||
}
|
||||
|
||||
@@ -372,6 +395,8 @@ pub struct OpenFileFilter {
|
||||
pub struct TemplateFunctionSelectArg {
|
||||
#[serde(flatten)]
|
||||
pub base: TemplateFunctionBaseArg,
|
||||
|
||||
/// The options that will be available in the select input
|
||||
pub options: Vec<TemplateFunctionSelectOption>,
|
||||
}
|
||||
|
||||
@@ -387,7 +412,7 @@ pub struct TemplateFunctionCheckboxArg {
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "events.ts")]
|
||||
pub struct TemplateFunctionSelectOption {
|
||||
pub name: String,
|
||||
pub label: String,
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ export function CookieDropdown() {
|
||||
placeholder: 'New name',
|
||||
defaultValue: activeCookieJar?.name,
|
||||
});
|
||||
if (name == null) return;
|
||||
updateCookieJar.mutate({ name });
|
||||
},
|
||||
},
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Dialog } from './core/Dialog';
|
||||
type DialogEntry = {
|
||||
id: string;
|
||||
render: ({ hide }: { hide: () => void }) => React.ReactNode;
|
||||
} & Omit<DialogProps, 'onClose' | 'open' | 'children'>;
|
||||
} & Omit<DialogProps, 'open' | 'children'>;
|
||||
|
||||
interface State {
|
||||
dialogs: DialogEntry[];
|
||||
|
||||
@@ -279,6 +279,7 @@ function SidebarButton({
|
||||
placeholder: 'New Name',
|
||||
defaultValue: environment.name,
|
||||
});
|
||||
if (name == null) return;
|
||||
updateEnvironment.mutate({ name });
|
||||
},
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useQueryClient } from '@tanstack/react-query';
|
||||
import { emit } from '@tauri-apps/api/event';
|
||||
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
|
||||
import type { AnyModel } from '@yaakapp-internal/models';
|
||||
import type { ShowPromptRequest, ShowPromptResponse } from '@yaakapp-internal/plugin';
|
||||
import type { PromptTextRequest, PromptTextResponse } from '@yaakapp-internal/plugin';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { useEffect } from 'react';
|
||||
import { useEnsureActiveCookieJar, useMigrateActiveCookieJarId } from '../hooks/useActiveCookieJar';
|
||||
@@ -180,11 +180,11 @@ export function GlobalHooks() {
|
||||
useListenToTauriEvent('zoom_reset', zoom.zoomReset);
|
||||
|
||||
const prompt = usePrompt();
|
||||
useListenToTauriEvent<{ replyId: string; args: ShowPromptRequest }>(
|
||||
useListenToTauriEvent<{ replyId: string; args: PromptTextRequest }>(
|
||||
'show_prompt',
|
||||
async (event) => {
|
||||
const value = await prompt(event.payload.args);
|
||||
const result: ShowPromptResponse = { value };
|
||||
const result: PromptTextResponse = { value };
|
||||
await emit(event.payload.replyId, result);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -49,6 +49,7 @@ export const RequestMethodDropdown = memo(function RequestMethodDropdown({
|
||||
description: 'Enter a custom method name',
|
||||
placeholder: 'CUSTOM',
|
||||
});
|
||||
if (newMethod == null) return;
|
||||
onChange(newMethod);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -767,6 +767,7 @@ function SidebarItem({
|
||||
placeholder: 'New Name',
|
||||
defaultValue: itemName,
|
||||
});
|
||||
if (name == null) return;
|
||||
updateAnyFolder.mutate({ id: itemId, update: (f) => ({ ...f, name }) });
|
||||
},
|
||||
},
|
||||
|
||||
@@ -2,11 +2,13 @@ import type {
|
||||
TemplateFunction,
|
||||
TemplateFunctionArg,
|
||||
TemplateFunctionCheckboxArg,
|
||||
TemplateFunctionFileArg,
|
||||
TemplateFunctionHttpRequestArg,
|
||||
TemplateFunctionSelectArg,
|
||||
TemplateFunctionTextArg,
|
||||
} from '@yaakapp-internal/plugin';
|
||||
import type { FnArg, Tokens } from '@yaakapp-internal/template';
|
||||
import classNames from 'classnames';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useActiveRequest } from '../hooks/useActiveRequest';
|
||||
import { useDebouncedValue } from '../hooks/useDebouncedValue';
|
||||
@@ -20,6 +22,7 @@ import { InlineCode } from './core/InlineCode';
|
||||
import { PlainInput } from './core/PlainInput';
|
||||
import { Select } from './core/Select';
|
||||
import { VStack } from './core/Stacks';
|
||||
import { SelectFile } from './SelectFile';
|
||||
|
||||
const NULL_ARG = '__NULL__';
|
||||
|
||||
@@ -50,8 +53,8 @@ export function TemplateFunctionDialog({ templateFunction, hide, initialTokens,
|
||||
return initial;
|
||||
});
|
||||
|
||||
const setArgValue = useCallback((name: string, value: string | boolean) => {
|
||||
setArgValues((v) => ({ ...v, [name]: value }));
|
||||
const setArgValue = useCallback((name: string, value: string | boolean | null) => {
|
||||
setArgValues((v) => ({ ...v, [name]: value == null ? '__NULL__' : value }));
|
||||
}, []);
|
||||
|
||||
const tokens: Tokens = useMemo(() => {
|
||||
@@ -90,6 +93,7 @@ export function TemplateFunctionDialog({ templateFunction, hide, initialTokens,
|
||||
|
||||
const debouncedTagText = useDebouncedValue(tagText.data ?? '', 200);
|
||||
const rendered = useRenderTemplate(debouncedTagText);
|
||||
const tooLarge = (rendered.data ?? '').length > 10000;
|
||||
|
||||
return (
|
||||
<VStack className="pb-3" space={4}>
|
||||
@@ -133,10 +137,29 @@ export function TemplateFunctionDialog({ templateFunction, hide, initialTokens,
|
||||
value={argValues[a.name] ? String(argValues[a.name]) : '__ERROR__'}
|
||||
/>
|
||||
);
|
||||
case 'file':
|
||||
return (
|
||||
<FileArg
|
||||
key={i}
|
||||
arg={a}
|
||||
onChange={(v) => setArgValue(a.name, v)}
|
||||
filePath={argValues[a.name] ? String(argValues[a.name]) : '__ERROR__'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</VStack>
|
||||
<InlineCode className="select-text cursor-text">{rendered.data || <> </>}</InlineCode>
|
||||
<VStack className="w-full">
|
||||
<div className="text-sm text-text-subtle">Preview</div>
|
||||
<InlineCode
|
||||
className={classNames(
|
||||
'whitespace-pre select-text cursor-text max-h-[10rem] overflow-y-auto hide-scrollbars',
|
||||
tooLarge && 'italic text-danger',
|
||||
)}
|
||||
>
|
||||
{tooLarge ? 'too large to preview' : rendered.data || <> </>}
|
||||
</InlineCode>
|
||||
</VStack>
|
||||
<Button color="primary" onClick={handleDone}>
|
||||
Done
|
||||
</Button>
|
||||
@@ -165,7 +188,13 @@ function TextArg({
|
||||
name={arg.name}
|
||||
onChange={handleChange}
|
||||
defaultValue={value === NULL_ARG ? '' : value}
|
||||
label={arg.label ?? arg.name}
|
||||
require={!arg.optional}
|
||||
label={
|
||||
<>
|
||||
{arg.label ?? arg.name}
|
||||
{arg.optional && <span> (optional)</span>}
|
||||
</>
|
||||
}
|
||||
hideLabel={arg.label == null}
|
||||
placeholder={arg.placeholder ?? arg.defaultValue ?? ''}
|
||||
/>
|
||||
@@ -197,6 +226,24 @@ function SelectArg({
|
||||
);
|
||||
}
|
||||
|
||||
function FileArg({
|
||||
arg,
|
||||
filePath,
|
||||
onChange,
|
||||
}: {
|
||||
arg: TemplateFunctionFileArg;
|
||||
filePath: string;
|
||||
onChange: (v: string | null) => void;
|
||||
}) {
|
||||
return (
|
||||
<SelectFile
|
||||
onChange={({ filePath }) => onChange(filePath)}
|
||||
filePath={filePath === '__NULL__' ? null : filePath}
|
||||
directory={!!arg.directory}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function HttpRequestArg({
|
||||
arg,
|
||||
value,
|
||||
|
||||
@@ -59,7 +59,7 @@ export function TemplateVariableDialog({ hide, onChange, initialTokens }: Props)
|
||||
/>
|
||||
</VStack>
|
||||
<VStack>
|
||||
<div className="text-sm text-text-subtle">Render Preview</div>
|
||||
<div className="text-sm text-text-subtle">Preview</div>
|
||||
<InlineCode className="select-text cursor-text">{rendered.data}</InlineCode>
|
||||
</VStack>
|
||||
<Button color="primary" onClick={handleDone}>
|
||||
|
||||
@@ -66,6 +66,7 @@ export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({
|
||||
placeholder: 'New Name',
|
||||
defaultValue: activeWorkspace?.name,
|
||||
});
|
||||
if (name == null) return;
|
||||
updateWorkspace.mutate({ name });
|
||||
},
|
||||
},
|
||||
|
||||
@@ -10,7 +10,7 @@ import { IconButton } from './IconButton';
|
||||
export interface DialogProps {
|
||||
children: ReactNode;
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
onClose?: () => void;
|
||||
title?: ReactNode;
|
||||
description?: ReactNode;
|
||||
className?: string;
|
||||
@@ -44,7 +44,7 @@ export function Dialog({
|
||||
'Escape',
|
||||
() => {
|
||||
if (!open) return;
|
||||
onClose();
|
||||
onClose?.();
|
||||
},
|
||||
{},
|
||||
[open],
|
||||
|
||||
@@ -26,7 +26,7 @@ export type InputProps = Omit<
|
||||
> & {
|
||||
name?: string;
|
||||
type?: 'text' | 'password';
|
||||
label: string;
|
||||
label: ReactNode;
|
||||
hideLabel?: boolean;
|
||||
labelPosition?: 'top' | 'left';
|
||||
labelClassName?: string;
|
||||
|
||||
@@ -512,7 +512,7 @@ function PairEditorRow({
|
||||
leftSlot: <Icon icon="pencil" />,
|
||||
hidden: !pairContainer.pair.isFile,
|
||||
onSelect: async () => {
|
||||
const v = await prompt({
|
||||
const contentType = await prompt({
|
||||
id: 'content-type',
|
||||
require: false,
|
||||
title: 'Override Content-Type',
|
||||
@@ -522,7 +522,8 @@ function PairEditorRow({
|
||||
confirmText: 'Set',
|
||||
description: 'Leave blank to auto-detect',
|
||||
});
|
||||
handleChangeValueContentType(v);
|
||||
if (contentType == null) return;
|
||||
handleChangeValueContentType(contentType);
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -8,7 +8,7 @@ import { HStack } from '../components/core/Stacks';
|
||||
export type PromptProps = Omit<ShowPromptRequest, 'id' | 'title' | 'description'> & {
|
||||
description?: ReactNode;
|
||||
onHide: () => void;
|
||||
onResult: (value: string) => void;
|
||||
onResult: (value: string | null) => void;
|
||||
};
|
||||
|
||||
export function Prompt({
|
||||
|
||||
@@ -9,24 +9,30 @@ export function useCreateFolder() {
|
||||
const workspace = useActiveWorkspace();
|
||||
const prompt = usePrompt();
|
||||
|
||||
return useMutation<Folder, unknown, Partial<Pick<Folder, 'name' | 'sortPriority' | 'folderId'>>>({
|
||||
return useMutation<void, unknown, Partial<Pick<Folder, 'name' | 'sortPriority' | 'folderId'>>>({
|
||||
mutationKey: ['create_folder'],
|
||||
mutationFn: async (patch) => {
|
||||
if (workspace === null) {
|
||||
throw new Error("Cannot create folder when there's no active workspace");
|
||||
}
|
||||
patch.name =
|
||||
patch.name ||
|
||||
(await prompt({
|
||||
|
||||
if (!patch.name) {
|
||||
const name = await prompt({
|
||||
id: 'new-folder',
|
||||
label: 'Name',
|
||||
defaultValue: 'Folder',
|
||||
title: 'New Folder',
|
||||
confirmText: 'Create',
|
||||
placeholder: 'Name',
|
||||
}));
|
||||
});
|
||||
if (name == null) {
|
||||
return;
|
||||
}
|
||||
patch.name = name;
|
||||
}
|
||||
|
||||
patch.sortPriority = patch.sortPriority || -Date.now();
|
||||
return invokeCmd('cmd_create_folder', { workspaceId: workspace.id, ...patch });
|
||||
await invokeCmd('cmd_create_folder', { workspaceId: workspace.id, ...patch });
|
||||
},
|
||||
onSettled: () => trackEvent('folder', 'create'),
|
||||
});
|
||||
|
||||
@@ -4,19 +4,27 @@ import type { PromptProps } from './Prompt';
|
||||
import { Prompt } from './Prompt';
|
||||
|
||||
type Props = Pick<DialogProps, 'title' | 'description'> &
|
||||
Omit<PromptProps, 'onResult' | 'onHide'> & { id: string };
|
||||
Omit<PromptProps, 'onClose' | 'onHide' | 'onResult'> & { id: string };
|
||||
|
||||
export function usePrompt() {
|
||||
const dialog = useDialog();
|
||||
return ({ id, title, description, ...props }: Props) =>
|
||||
new Promise((onResult: PromptProps['onResult']) => {
|
||||
new Promise((resolve: PromptProps['onResult']) => {
|
||||
dialog.show({
|
||||
id,
|
||||
title,
|
||||
description,
|
||||
hideX: true,
|
||||
size: 'sm',
|
||||
render: ({ hide: onHide }) => Prompt({ onHide, onResult, ...props }),
|
||||
render: ({ hide }) =>
|
||||
Prompt({
|
||||
onHide: () => {
|
||||
hide();
|
||||
resolve(null);
|
||||
},
|
||||
onResult: resolve,
|
||||
...props,
|
||||
}),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -33,6 +33,9 @@ export function useRenameRequest(requestId: string | null) {
|
||||
defaultValue: request.name,
|
||||
confirmText: 'Save',
|
||||
});
|
||||
|
||||
if (name == null) return;
|
||||
|
||||
if (request.model === 'http_request') {
|
||||
updateHttpRequest.mutate({ id: request.id, update: (r: HttpRequest) => ({ ...r, name }) });
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user