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

fix: wrong types in message/query request body #26

Merged
merged 3 commits into from
Apr 4, 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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-toast": "^1.1.5",
"@tanstack/react-query": "^5.22.2",
Expand Down
33 changes: 33 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion src/components/bottom-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ export function BottomBar() {
const handleExpand = () => {
const panel = ref.current
if (panel) {
console.log(panel.getSize())
if (panel.getSize() <= 5) {
panel.collapse()
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/sidebar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export function Sidebar() {
const queries = data?.queries.filter((q) => !builtinQueries.has(q.url)) ?? []

return (
<aside className="flex flex-col justify-between px-3 pt-4 pb-2 min-w-64 overflow-y-auto border-r text-sm">
<aside className="flex flex-col justify-between px-3 pt-4 pb-2 min-w-64 w-64 overflow-y-auto border-r text-sm">
<div className="space-y-2">
<CreatePersona />
<SidebarMessages messages={messages} />
Expand Down
11 changes: 7 additions & 4 deletions src/components/sidebar/messages.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { zodResolver } from '@hookform/resolvers/zod'
import { useQuery, useQueryClient } from '@tanstack/react-query'
import { BookDashed, MessageSquareCode, Loader } from 'lucide-react'
import { useForm } from 'react-hook-form'
Expand All @@ -17,7 +18,6 @@ import {
FormLabel,
FormMessage,
} from '@/components/ui/form'
import { Input } from '@/components/ui/input'
import {
Select,
SelectContent,
Expand All @@ -30,7 +30,7 @@ import { useCardinal } from '@/lib/cardinal-provider'
import { gameMessageQueryOptions, worldQueryOptions } from '@/lib/query-options'
import { WorldField } from '@/lib/types'

import { formatName } from './utils'
import { defaultValues, formSchema, formatName, goTypeToInputComponent } from './utils'

interface SidebarMessagesProps {
messages: WorldField[]
Expand Down Expand Up @@ -75,7 +75,10 @@ function Message({ message }: MessageProp) {
const { cardinalUrl, isCardinalConnected, personas, setPersonas } = useCardinal()
const { data } = useQuery(worldQueryOptions({ cardinalUrl, isCardinalConnected }))
const queryClient = useQueryClient()
const form = useForm()
const form = useForm({
resolver: zodResolver(formSchema(message)),
defaultValues: defaultValues(message),
})

// @ts-ignore
const handleSubmit = async (values) => {
Expand Down Expand Up @@ -172,7 +175,7 @@ function Message({ message }: MessageProp) {
</span>
</FormLabel>
<FormControl>
<Input required className="h-8" {...field} />
{goTypeToInputComponent(message.fields[param], field)}
</FormControl>
<FormMessage />
</FormItem>
Expand Down
15 changes: 7 additions & 8 deletions src/components/sidebar/queries.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { zodResolver } from '@hookform/resolvers/zod'
import { useQueryClient } from '@tanstack/react-query'
import { BookDashed, Loader, SearchCode } from 'lucide-react'
import { useForm } from 'react-hook-form'
Expand All @@ -17,12 +18,11 @@ import {
FormLabel,
FormMessage,
} from '@/components/ui/form'
import { Input } from '@/components/ui/input'
import { useCardinal } from '@/lib/cardinal-provider'
import { gameQueryQueryOptions } from '@/lib/query-options'
import { WorldField } from '@/lib/types'

import { formatName } from './utils'
import { defaultValues, formSchema, formatName, goTypeToInputComponent } from './utils'

interface SidebarQueriesProps {
queries: WorldField[]
Expand Down Expand Up @@ -75,9 +75,10 @@ interface QueryProp {
function Query({ query }: QueryProp) {
const { cardinalUrl, isCardinalConnected } = useCardinal()
const queryClient = useQueryClient()
// TODO: fix uncontrolled component error by adding default values here, tho we need to set the
// schema first to do this. same goes to the useForm in ./messages.tsx
const form = useForm()
const form = useForm({
resolver: zodResolver(formSchema(query)),
defaultValues: defaultValues(query),
})

// @ts-ignore
const handleSubmit = (values) => {
Expand Down Expand Up @@ -125,9 +126,7 @@ function Query({ query }: QueryProp) {
{query.fields[param]}
</span>
</FormLabel>
<FormControl>
<Input required className="h-8" {...field} />
</FormControl>
<FormControl>{goTypeToInputComponent(query.fields[param], field)}</FormControl>
<FormMessage />
</FormItem>
)}
Expand Down
6 changes: 0 additions & 6 deletions src/components/sidebar/utils.ts

This file was deleted.

101 changes: 101 additions & 0 deletions src/components/sidebar/utils.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { ControllerRenderProps, FieldValues } from 'react-hook-form'
import { ZodType, z } from 'zod'

import { Input } from '@/components/ui/input'
import { Switch } from '@/components/ui/switch'
import { WorldField } from '@/lib/types'

export const formatName = (name: string) => {
return name
.split('-')
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
.join(' ')
}

const goNumberTypes = new Set([
'uint8',
'uint16',
'uint32',
'uint64',
'int8',
'int16',
'int32',
'int64',
'float32',
'float64',
'complex64',
'complex128',
'byte',
'rune',
'uint',
'int',
'uintptr',
])

// TODO: handle non-primitive types (struts, maps, arrays, slices)
// handling them would take more effort than expected, will continue this if the
// demands are high. users would have to refactor their message/query request structs,
// but message/query functionalities should still be fine if we only support primitives for now.
// edge cases:
// - []number -> '[]number'
// - struct{a string} -> js object {a: 'string'}
// - map[string]number -> 'map[string]number'
// - []struct{a string} -> '[]struct{a string}'
// - []Type -> '[]msg.Type' (this should be handled in cardinal /world handler)
export const goTypeToZodType = (type: string) => {
// if type is a primitive go type
if (goNumberTypes.has(type)) return z.coerce.number()
if (type === 'string') return z.string()
if (type === 'bool') return z.boolean().optional()

// else just default to string for now & parse it into an object
return z.string().transform((s) => JSON.parse(s) as object)
}

const goToDefaultJSType = (type: string) => {
if (goNumberTypes.has(type)) return 0
if (type === 'string') return ''
if (type === 'bool') return false
return ''
}

export const formSchema = (wf: WorldField): ZodType => {
return z.object({
persona: z.string(),
...Object.keys(wf.fields).reduce(
(acc, k) => ({ ...acc, [k]: goTypeToZodType(wf.fields[k]) }),
{},
),
})
}

// TODO: just allow any in typescript config smh
type anyObj = { [key: string]: string | number | boolean }
export const defaultValues = (wf: WorldField): anyObj => {
return {
persona: '',
...Object.keys(wf.fields).reduce(
(acc, k) => ({ ...acc, [k]: goToDefaultJSType(wf.fields[k]) }),
{},
),
}
}

export const goTypeToInputComponent = (
type: string,
field: ControllerRenderProps<FieldValues, string>,
) => {
if (goNumberTypes.has(type)) {
return <Input required type="number" className="h-8" {...field} />
}
if (type === 'string') {
return <Input required className="h-8" {...field} />
}
if (type === 'bool') {
return (
<Switch checked={field.value as boolean} onCheckedChange={field.onChange} className="block" />
)
}
// default to string
return <Input required className="h-8" {...field} />
}
27 changes: 27 additions & 0 deletions src/components/ui/switch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import * as SwitchPrimitives from '@radix-ui/react-switch'
import * as React from 'react'

import { cn } from '@/lib/utils'

const Switch = React.forwardRef<
React.ElementRef<typeof SwitchPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
>(({ className, ...props }, ref) => (
<SwitchPrimitives.Root
className={cn(
'peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input',
className,
)}
{...props}
ref={ref}
>
<SwitchPrimitives.Thumb
className={cn(
'pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0',
)}
/>
</SwitchPrimitives.Root>
))
Switch.displayName = SwitchPrimitives.Root.displayName

export { Switch }