Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Move recording-related controls to Sidebar #178

Merged
merged 4 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions src/views/Generator/GeneraterControls/GeneratorControls.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { useState } from 'react'
import { Button, ButtonProps, Tooltip } from '@radix-ui/themes'
import { RecordingSelector } from '../RecordingSelector'
import { Allowlist } from '../Allowlist'

import { useScriptPreview } from '@/hooks/useScriptPreview'
import { exportScript } from '../Generator.utils'
import { ValidatorDialog } from './ValidatorDialog'
import { useState } from 'react'

interface GeneratorControlsProps {
onSave: () => void
Expand All @@ -18,9 +17,6 @@ export function GeneratorControls({ onSave, isDirty }: GeneratorControlsProps) {

return (
<>
<RecordingSelector />
<Allowlist />

{!!preview && (
<>
<ButtonWithTooltip
Expand Down
77 changes: 46 additions & 31 deletions src/views/Generator/GeneratorSidebar/RequestList.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { ScrollArea } from '@radix-ui/themes'
import { Flex, ScrollArea } from '@radix-ui/themes'
import { Allotment } from 'allotment'
import { useState } from 'react'
import { css } from '@emotion/react'
import { useShallowCompareEffect } from 'react-use'

import { WebLogView } from '@/components/WebLogView'
import { ProxyData } from '@/types'
import { Details } from '@/components/WebLogView/Details'
import { useShallowCompareEffect } from 'react-use'
import { Filter } from '@/components/WebLogView/Filter'
import { useFilterRequests } from '@/components/WebLogView/Filter.hooks'
import { RecordingSelector } from '../RecordingSelector'
import { Allowlist } from '../Allowlist'

interface RequestListProps {
requests: ProxyData[]
Expand All @@ -17,39 +20,51 @@ export function RequestList({ requests }: RequestListProps) {
const [selectedRequest, setSelectedRequest] = useState<ProxyData | null>(null)
const { filter, setFilter, filteredRequests } = useFilterRequests(requests)

// Preserve the selected request when modifiying rules
// Preserve the selected request when modifying rules
useShallowCompareEffect(() => {
setSelectedRequest(null)
}, [requests])

return (
<Allotment vertical defaultSizes={[1, 2]}>
<Allotment.Pane minSize={200}>
<ScrollArea scrollbars="vertical">
<Filter
filter={filter}
setFilter={setFilter}
css={{
borderRadius: 0,
outline: 'none',
boxShadow: '0 1px 0 var(--gray-a5)',
}}
size="2"
/>
<WebLogView
requests={filteredRequests}
selectedRequestId={selectedRequest?.id}
onSelectRequest={setSelectedRequest}
noRequestsMessage="No requests matched the filter."
/>
</ScrollArea>
</Allotment.Pane>
<Allotment.Pane minSize={300} visible={selectedRequest !== null}>
<Details
selectedRequest={selectedRequest}
onSelectRequest={setSelectedRequest}
/>
</Allotment.Pane>
</Allotment>
<Flex direction="column" height="100%">
<Flex justify="between" align="center" p="2" wrap="wrap" gap="2">
<RecordingSelector />
<Allowlist />
</Flex>
<div
css={css`
flex-grow: 1;
`}
>
<Allotment vertical defaultSizes={[1, 2]}>
<Allotment.Pane minSize={200}>
<ScrollArea scrollbars="vertical">
<Filter
filter={filter}
setFilter={setFilter}
css={{
borderRadius: 0,
outline: 'none',
boxShadow: '0 1px 0 var(--gray-a5)',
}}
size="2"
/>
<WebLogView
requests={filteredRequests}
selectedRequestId={selectedRequest?.id}
onSelectRequest={setSelectedRequest}
noRequestsMessage="No requests matched the filter."
/>
</ScrollArea>
</Allotment.Pane>
<Allotment.Pane minSize={300} visible={selectedRequest !== null}>
<Details
selectedRequest={selectedRequest}
onSelectRequest={setSelectedRequest}
/>
</Allotment.Pane>
</Allotment>
</div>
</Flex>
)
}
79 changes: 48 additions & 31 deletions src/views/Generator/RecordingSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,57 +1,74 @@
import { Button, DropdownMenu } from '@radix-ui/themes'
import { Flex, IconButton, Select, Text, Tooltip } from '@radix-ui/themes'
import { PlusIcon } from '@radix-ui/react-icons'
import { css } from '@emotion/react'

import { useGeneratorStore } from '@/store/generator'
import { harToProxyData } from '@/utils/harToProxyData'
import { useStudioUIStore } from '@/store/ui'
import { CaretDownIcon } from '@radix-ui/react-icons'
import { getFileNameWithoutExtension } from '@/utils/file'
import { useToast } from '@/store/ui/useToast'

export function RecordingSelector() {
const recordingPath = useGeneratorStore((store) => store.recordingPath)
const setRecording = useGeneratorStore((store) => store.setRecording)
const recordings = useStudioUIStore((store) => store.recordings)
const showToast = useToast()

const handleOpen = async (filePath: string) => {
const harFile = await window.studio.har.openFile(filePath)
try {
const harFile = await window.studio.har.openFile(filePath)

const proxyData = harToProxyData(harFile.content)
setRecording(proxyData, harFile.name)
const proxyData = harToProxyData(harFile.content)
setRecording(proxyData, harFile.name)
} catch (error) {
showToast({
title: 'Failed to open recording',
status: 'error',
})
}
}

const handleImport = async () => {
const filePath = await window.studio.har.importFile()
try {
const filePath = await window.studio.har.importFile()

if (!filePath) return
if (!filePath) return

await handleOpen(filePath)
await handleOpen(filePath)
} catch (error) {
showToast({
title: 'Failed to import recording',
status: 'error',
})
}
}

return (
<DropdownMenu.Root>
<DropdownMenu.Trigger>
<Button variant="ghost" mr="2">
{recordingPath
? getFileNameWithoutExtension(recordingPath)
: 'Select recording'}
<CaretDownIcon />
</Button>
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.RadioGroup
value={recordingPath}
onValueChange={handleOpen}
>
<Flex gap="2" align="center">
<Text size="2" weight="medium" as="label" htmlFor="recording-selector">
Recording
</Text>
<Select.Root value={recordingPath} onValueChange={handleOpen}>
<Select.Trigger
id="recording-selector"
placeholder="Select recording"
css={css`
max-width: 200px;
`}
/>
<Select.Content position="popper">
{recordings.map((harFileName) => (
<DropdownMenu.RadioItem value={harFileName} key={harFileName}>
<Select.Item value={harFileName} key={harFileName}>
{getFileNameWithoutExtension(harFileName)}
</DropdownMenu.RadioItem>
</Select.Item>
))}
</DropdownMenu.RadioGroup>
{recordings.length > 0 && <DropdownMenu.Separator />}
<DropdownMenu.Item onSelect={handleImport}>
Import HAR file
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>
</Select.Content>
</Select.Root>
<Tooltip content="Import recording">
<IconButton variant="ghost" color="gray" onClick={handleImport}>
<PlusIcon />
</IconButton>
</Tooltip>
</Flex>
)
}
Loading