Hot keys and cleanup

This commit is contained in:
Gregory Schier
2023-04-03 07:59:49 -07:00
parent c1d7f6e544
commit 712626ac1f
11 changed files with 149 additions and 129 deletions

View File

@@ -5,19 +5,22 @@ on:
jobs:
build-artifacts:
runs-on: ${{ matrix.platform }}
strategy:
fail-fast: false
matrix:
# platform: [ ubuntu-latest, macos-latest, windows-latest ]
platform: [ macos-latest ]
include:
- os: macos-latest
target: aarch64-apple-darwin
- os: macos-latest
target: x86_64-apple-darwin
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
with:
targets: 'aarch64-apple-darwin,x86_64-apple-darwin'
targets: ${{ matrix.target }}
- name: Cache Rust
uses: actions/cache@v2
with:
@@ -31,7 +34,7 @@ jobs:
node-version: 18
cache: 'npm'
- name: install dependencies (ubuntu only)
if: matrix.platform == 'ubuntu-latest'
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf
@@ -55,7 +58,7 @@ jobs:
with:
tagName: 'v__VERSION__'
releaseName: 'Release __VERSION__'
releaseBody: 'See the assets to download this version and install.'
releaseDraft: false
releaseBody: '<!-- Release Notes -->'
releaseDraft: true
prerelease: false
args: '--target universal-apple-darwin'
args: '--target ${{ matrix.target }}'

View File

@@ -79,11 +79,8 @@ async fn actually_send_ephemeral_request(
let start = std::time::Instant::now();
let mut url_string = request.url.to_string();
let mut variables = HashMap::new();
variables.insert("PROJECT_ID", "project_123");
variables.insert("TOKEN", "s3cret");
variables.insert("DOMAIN", "schier.co");
variables.insert("BASE_URL", "https://schier.co");
let variables: HashMap<&str, &str> = HashMap::new();
// variables.insert("", "");
let re = Regex::new(r"\$\{\[\s*([^]\s]+)\s*]}").expect("Failed to create regex");
url_string = re
@@ -608,6 +605,14 @@ fn create_window(handle: &AppHandle<Wry>) -> Window<Wry> {
.add_item(
CustomMenuItem::new("focus_url".to_string(), "Focus URL").accelerator("CmdOrCtrl+l"),
)
.add_item(
CustomMenuItem::new("new_request".to_string(), "New Request")
.accelerator("CmdOrCtrl+n"),
)
.add_item(
CustomMenuItem::new("duplicate_request".to_string(), "Duplicate Request")
.accelerator("CmdOrCtrl+d"),
)
.add_item(CustomMenuItem::new("new_window".to_string(), "New Window"));
if is_dev() {
test_menu = test_menu
@@ -652,6 +657,8 @@ fn create_window(handle: &AppHandle<Wry>) -> Window<Wry> {
"toggle_sidebar" => win2.emit("toggle_sidebar", true).unwrap(),
"focus_url" => win2.emit("focus_url", true).unwrap(),
"send_request" => win2.emit("send_request", true).unwrap(),
"new_request" => _ = win2.emit("new_request", true).unwrap(),
"duplicate_request" => _ = win2.emit("duplicate_request", true).unwrap(),
"refresh" => win2.eval("location.reload()").unwrap(),
"new_window" => _ = create_window(&handle2),
"toggle_devtools" => {

View File

@@ -8,7 +8,7 @@
},
"package": {
"productName": "Yaak",
"version": "2023.0.10"
"version": "2023.0.11"
},
"tauri": {
"windows": [],

View File

@@ -4,6 +4,7 @@ import { useDeleteRequest } from '../hooks/useDeleteRequest';
import { useDuplicateRequest } from '../hooks/useDuplicateRequest';
import { useRequest } from '../hooks/useRequest';
import { Dropdown } from './core/Dropdown';
import { HotKey } from './core/HotKey';
import { Icon } from './core/Icon';
import { InlineCode } from './core/InlineCode';
@@ -25,6 +26,7 @@ export function RequestActionsDropdown({ requestId, children }: Props) {
label: 'Duplicate',
onSelect: duplicateRequest.mutate,
leftSlot: <Icon icon="copy" />,
rightSlot: <HotKey>D</HotKey>,
},
{
label: 'Delete',

View File

@@ -80,105 +80,109 @@ export const ResponsePane = memo(function ResponsePane({ style, className }: Pro
'shadow shadow-gray-100 dark:shadow-gray-0 relative',
)}
>
<HStack
alignItems="center"
className={classnames(
'italic text-gray-700 text-sm w-full flex-shrink-0',
// Remove a bit of space because the tabs have lots too
'-mb-1.5',
)}
>
{activeResponse && (
<>
<div className="whitespace-nowrap p-3 py-2">
<StatusColor statusCode={activeResponse.status}>
{activeResponse.status}
{activeResponse.statusReason && ` ${activeResponse.statusReason}`}
</StatusColor>
&nbsp;&bull;&nbsp;
{activeResponse.elapsed}ms &nbsp;&bull;&nbsp;
{Math.round(activeResponse.body.length / 1000)} KB
</div>
{activeResponse && (
<>
<HStack
alignItems="center"
className={classnames(
'italic text-gray-700 text-sm w-full flex-shrink-0',
// Remove a bit of space because the tabs have lots too
'-mb-1.5',
)}
>
{activeResponse && (
<>
<div className="whitespace-nowrap p-3 py-2">
<StatusColor statusCode={activeResponse.status}>
{activeResponse.status}
{activeResponse.statusReason && ` ${activeResponse.statusReason}`}
</StatusColor>
&nbsp;&bull;&nbsp;
{activeResponse.elapsed}ms &nbsp;&bull;&nbsp;
{Math.round(activeResponse.body.length / 1000)} KB
</div>
<Dropdown
items={[
{
label: viewMode === 'pretty' ? 'View Raw' : 'View Prettified',
onSelect: toggleViewMode,
},
{ type: 'separator', label: 'Actions' },
{
label: 'Clear Response',
onSelect: deleteResponse.mutate,
disabled: responses.length === 0,
},
{
label: `Clear ${responses.length} ${pluralize('Response', responses.length)}`,
onSelect: deleteAllResponses.mutate,
hidden: responses.length <= 1,
disabled: responses.length === 0,
},
{ type: 'separator', label: 'History' },
...responses.slice(0, 10).map((r) => ({
label: r.status + ' - ' + r.elapsed + ' ms',
leftSlot: activeResponse?.id === r.id ? <Icon icon="check" /> : <></>,
onSelect: () => setPinnedResponseId(r.id),
})),
]}
<Dropdown
items={[
{
label: viewMode === 'pretty' ? 'View Raw' : 'View Prettified',
onSelect: toggleViewMode,
},
{ type: 'separator', label: 'Actions' },
{
label: 'Clear Response',
onSelect: deleteResponse.mutate,
disabled: responses.length === 0,
},
{
label: `Clear ${responses.length} ${pluralize('Response', responses.length)}`,
onSelect: deleteAllResponses.mutate,
hidden: responses.length <= 1,
disabled: responses.length === 0,
},
{ type: 'separator', label: 'History' },
...responses.slice(0, 10).map((r) => ({
label: r.status + ' - ' + r.elapsed + ' ms',
leftSlot: activeResponse?.id === r.id ? <Icon icon="check" /> : <></>,
onSelect: () => setPinnedResponseId(r.id),
})),
]}
>
<IconButton
title="Show response history"
icon="triangleDown"
className="ml-auto"
size="sm"
iconSize="md"
/>
</Dropdown>
</>
)}
</HStack>
{activeResponse?.error ? (
<Banner className="m-2">{activeResponse.error}</Banner>
) : (
<Tabs
value={activeTab}
onChangeValue={setActiveTab}
label="Response"
className="px-3"
tabs={tabs}
>
<IconButton
title="Show response history"
icon="triangleDown"
className="ml-auto"
size="sm"
iconSize="md"
/>
</Dropdown>
</>
)}
</HStack>
{activeResponse?.error ? (
<Banner className="m-2">{activeResponse.error}</Banner>
) : (
<Tabs
value={activeTab}
onChangeValue={setActiveTab}
label="Response"
className="px-3"
tabs={tabs}
>
<TabContent value="body">
{activeResponse === null ? (
<EmptyStateText>No Response</EmptyStateText>
) : viewMode === 'pretty' && contentType.includes('html') ? (
<Webview
body={activeResponse.body}
contentType={contentType}
url={activeResponse.url}
/>
) : viewMode === 'pretty' && contentType.includes('json') ? (
<Editor
readOnly
forceUpdateKey={`pretty::${activeResponse.updatedAt}`}
className="bg-gray-50 dark:!bg-gray-100"
defaultValue={tryFormatJson(activeResponse?.body)}
contentType={contentType}
/>
) : activeResponse?.body ? (
<Editor
readOnly
forceUpdateKey={activeResponse.updatedAt}
className="bg-gray-50 dark:!bg-gray-100"
defaultValue={activeResponse?.body}
contentType={contentType}
/>
) : null}
</TabContent>
<TabContent value="headers">
<ResponseHeaders headers={activeResponse?.headers ?? []} />
</TabContent>
</Tabs>
<TabContent value="body">
{!activeResponse.body ? (
<EmptyStateText>No Response</EmptyStateText>
) : viewMode === 'pretty' && contentType.includes('html') ? (
<Webview
body={activeResponse.body}
contentType={contentType}
url={activeResponse.url}
/>
) : viewMode === 'pretty' && contentType.includes('json') ? (
<Editor
readOnly
forceUpdateKey={`pretty::${activeResponse.updatedAt}`}
className="bg-gray-50 dark:!bg-gray-100"
defaultValue={tryFormatJson(activeResponse?.body)}
contentType={contentType}
/>
) : activeResponse?.body ? (
<Editor
readOnly
forceUpdateKey={activeResponse.updatedAt}
className="bg-gray-50 dark:!bg-gray-100"
defaultValue={activeResponse?.body}
contentType={contentType}
/>
) : null}
</TabContent>
<TabContent value="headers">
<ResponseHeaders headers={activeResponse?.headers ?? []} />
</TabContent>
</Tabs>
)}
</>
)}
</div>
);

View File

@@ -1,14 +1,26 @@
import { memo, useCallback } from 'react';
import { useActiveRequestId } from '../hooks/useActiveRequestId';
import { useCreateRequest } from '../hooks/useCreateRequest';
import { useDuplicateRequest } from '../hooks/useDuplicateRequest';
import { useSidebarHidden } from '../hooks/useSidebarHidden';
import { useTauriEvent } from '../hooks/useTauriEvent';
import { IconButton } from './core/IconButton';
export const SidebarActions = memo(function SidebarDisplayToggle() {
const { hidden, toggle } = useSidebarHidden();
const activeRequestId = useActiveRequestId();
const createRequest = useCreateRequest({ navigateAfter: true });
const duplicateRequest = useDuplicateRequest({ id: activeRequestId, navigateAfter: true });
const handleCreateRequest = useCallback(() => {
createRequest.mutate({ name: 'New Request' });
createRequest.mutate({});
}, [createRequest]);
useTauriEvent('new_request', () => {
createRequest.mutate({});
});
// TODO: Put this somewhere better
useTauriEvent('duplicate_request', () => {
duplicateRequest.mutate();
});
return (
<>

View File

@@ -13,6 +13,7 @@ interface Props {
export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Props) {
const activeRequest = useActiveRequest();
return (
<HStack
justifyContent="center"

View File

@@ -266,7 +266,7 @@ function MenuItem({ className, focused, onFocus, item, onSelect, ...props }: Men
onClick={handleClick}
className={classnames(
className,
'min-w-[8rem] outline-none px-2 mx-1.5 h-7 flex items-center text-sm text-gray-700 whitespace-nowrap pr-4',
'min-w-[8rem] outline-none px-2 mx-1.5 h-7 flex items-center text-sm text-gray-700 whitespace-nowrap',
'focus:bg-highlight focus:text-gray-900 rounded',
)}
{...props}

View File

@@ -3,18 +3,8 @@ import type { CompletionContext } from '@codemirror/autocomplete';
const openTag = '${[ ';
const closeTag = ' ]}';
const variables = [
{ name: 'DOMAIN' },
{ name: 'BASE_URL' },
{ name: 'CONTENT_THINGY' },
{ name: 'TOKEN' },
{ name: 'PROJECT_ID' },
{ name: 'DUMMY' },
{ name: 'DUMMY_2' },
{ name: 'STRIPE_PUB_KEY' },
{ name: 'RAILWAY_TOKEN' },
{ name: 'SECRET' },
{ name: 'PORT' },
const variables: { name: string }[] = [
// TODO: Put variables here
];
const MIN_MATCH_VAR = 2;

View File

@@ -5,7 +5,7 @@ export function HotKey({ children }: HTMLAttributes<HTMLSpanElement>) {
return (
<span
className={classnames(
'bg-gray-400 bg-opacity-20 px-1.5 py-0.5 rounded text-sm',
'bg-highlightSecondary bg-opacity-20 px-1.5 py-0.5 rounded text-sm',
'font-mono text-gray-500 tracking-widest',
)}
>

View File

@@ -16,8 +16,9 @@ export function useCreateRequest({ navigateAfter }: { navigateAfter: boolean })
if (workspaceId === null) {
throw new Error("Cannot create request when there's no active workspace");
}
const sortPriority = maxSortPriority(requests) + 1000;
return invoke('create_request', { sortPriority, workspaceId, ...patch });
patch.name = patch.name || 'New Request';
patch.sortPriority = patch.sortPriority || maxSortPriority(requests) + 1000;
return invoke('create_request', { workspaceId, ...patch });
},
onSuccess: async (request) => {
queryClient.setQueryData<HttpRequest[]>(