Skip to content

Commit

Permalink
feat: wip state machine
Browse files Browse the repository at this point in the history
  • Loading branch information
Stormix committed Nov 11, 2023
1 parent 4f584c8 commit 7923784
Show file tree
Hide file tree
Showing 23 changed files with 520 additions and 277 deletions.
1 change: 1 addition & 0 deletions client/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ dist-ssr
*.njsproj
*.sln
*.sw?
*.env
Binary file modified client/bun.lockb
Binary file not shown.
2 changes: 2 additions & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.48.2",
"react-router-dom": "^6.18.0",
"react-use-websocket": "^4.5.0",
"tailwind-merge": "^2.0.0",
"tailwindcss": "^3.3.5",
"tailwindcss-animate": "^1.0.7",
"unique-username-generator": "^1.2.0",
"zod": "^3.22.4",
"zustand": "^4.4.6"
},
Expand Down
17 changes: 12 additions & 5 deletions client/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import { BrowserRouter, Route, Routes } from 'react-router-dom'
import Home from './components/pages/home'
import NotFound from './components/pages/not-found'
import Welcome from './components/pages/welcome'
import Layout from './components/template/layout'

const App = () => {
return (
<>
<Layout>
<Home />
</Layout>
</>
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route path="welcome" element={<Welcome />} />
<Route path="*" element={<NotFound />} />
</Route>
</Routes>
</BrowserRouter>
)
}

Expand Down
42 changes: 24 additions & 18 deletions client/src/components/molecules/chat.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
import { Button } from '@/components/ui/button'
import { Form, FormControl, FormDescription, FormField, FormItem, FormMessage } from '@/components/ui/form'
import { useChatStore } from '@/lib/store'
import { useStore } from '@/lib/store'
import { cn } from '@/lib/utils'
import { useOmegle } from '@/providers/omegle-provider'
import { zodResolver } from '@hookform/resolvers/zod'
import { Send } from 'lucide-react'
import { useEffect, useRef } from 'react'
import { useForm } from 'react-hook-form'
import { z } from 'zod'
import { Badge } from '../ui/badge'
import { Textarea } from '../ui/textarea'
import { useToast } from '../ui/use-toast'
import NameDialog from './name-dialog'
import SettingsDialog from './settings-dialog'

const formSchema = z.object({
message: z.string().min(1)
})

const Chat = () => {
const ref = useRef<HTMLDivElement>(null)
const { messages, name } = useChatStore()
const { sendMessage, strangerId } = useOmegle()
const { messages, me } = useStore()
const { sendMessage, stranger } = useOmegle()
const { toast } = useToast()

const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
Expand All @@ -33,7 +36,7 @@ const Chat = () => {
}, [messages])

const onSubmit = (data: z.infer<typeof formSchema>) => {
if (!strangerId) {
if (!stranger?.id) {
toast({
title: 'Error',
description: 'You are not connected to a chat.',
Expand All @@ -46,23 +49,19 @@ const Chat = () => {
}

return (
<div className="flex-grow flex flex-col h-full w-full max-h-full">
<div className="flex gap-2">
<span>
You're connected as <b>{name}</b>
</span>
<NameDialog />
</div>
<h3>Chat log: </h3>
<div className="flex-grow flex flex-col h-full w-full ">
<h3>
Chat log: <Badge>{me?.state}</Badge>{' '}
</h3>
<div className="flex-grow flex flex-col gap-2 overflow-y-auto h-5/6 py-8" ref={ref}>
{messages.map((message, i) => (
<div key={i} className="flex flex-col gap-2 ">
<span
className={cn('font-bold', {
'text-accent': message.sender === strangerId
'text-accent': message.sender === stranger?.id || message.sender === stranger?.name
})}
>
{message.sender}:{' '}
{message.sender}:
</span>
<span>{message.message}</span>
</div>
Expand All @@ -75,9 +74,10 @@ const Chat = () => {
control={form.control}
name="message"
render={({ field }) => (
<FormItem className="flex-grow">
<FormItem className="flex flex-grow flex-col w-full justify-center md:justify-normal">
<FormControl>
<Textarea
rows={4}
placeholder="Type your message here"
{...field}
onKeyDown={(e) => {
Expand All @@ -88,12 +88,18 @@ const Chat = () => {
}}
/>
</FormControl>
<FormDescription>This is your public display name.</FormDescription>
<FormDescription className="">
<span className="mr-2">
You're connected as <b>{me?.name}.</b>
</span>
<SettingsDialog />
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit" size={'lg'}>
<Button type="submit" size={'lg'} className="flex gap-2 w-full md:max-w-xs">
<Send className="w-4 h-4" />
Send message
</Button>
</form>
Expand Down
24 changes: 22 additions & 2 deletions client/src/components/molecules/footer.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,27 @@
const Footer = () => {
return (
<footer>
<p>© 2024</p>
<footer className="py-4">
<p className="text-sm leading-loose text-muted-foreground md:text-left">
Built by{' '}
<a
href="https://stormix.dev"
target="_blank"
rel="noreferrer"
className="font-medium underline underline-offset-4"
>
Stormix
</a>
. The source code is available on{' '}
<a
href="https://github.com/Stormix/msn"
target="_blank"
rel="noreferrer"
className="font-medium underline underline-offset-4"
>
GitHub
</a>
.
</p>
</footer>
)
}
Expand Down
4 changes: 2 additions & 2 deletions client/src/components/molecules/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { ModeToggle } from './mode-toggle'

const Header = () => {
return (
<header className="flex justify-between">
<h1>Header</h1>
<header className="flex justify-between py-4">
<h1 className="text-2xl font-bold">Chitchatly</h1>
<ModeToggle />
</header>
)
Expand Down
77 changes: 0 additions & 77 deletions client/src/components/molecules/name-dialog.tsx

This file was deleted.

97 changes: 97 additions & 0 deletions client/src/components/molecules/settings-dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { useStore } from '@/lib/store'
import { useOmegle } from '@/providers/omegle-provider'
import { zodResolver } from '@hookform/resolvers/zod'
import { DialogDescription } from '@radix-ui/react-dialog'
import { useState } from 'react'
import { useForm } from 'react-hook-form'
import { generateUsername } from 'unique-username-generator'
import { z } from 'zod'
import { Button } from '../ui/button'
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '../ui/dialog'
import { Form, FormControl, FormDescription, FormField, FormItem, FormMessage } from '../ui/form'
import { Input } from '../ui/input'

const formSchema = z.object({
name: z.string().min(1),
keywords: z.string().optional()
})

const SettingsDialog = () => {
const { keywords, saveSettings, me, setName } = useStore()
const [open, setOpen] = useState(!me?.name)
const { setName: setChatName } = useOmegle()

const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
name: me?.name || generateUsername(),
keywords: keywords?.join(', ') || ''
}
})

const onSubmit = (data: z.infer<typeof formSchema>) => {
setOpen(false)
saveSettings(data.keywords?.split(',').map((keyword) => keyword.trim()) ?? [])
setName(data.name)
setChatName?.(data.name)
}

return (
<Dialog open={open}>
<DialogTrigger>
<a onClick={() => setOpen(true)}>
(<span className="hover:underline">Change name</span>)
</a>
</DialogTrigger>
<DialogContent>
<DialogHeader className="gap-4">
<DialogTitle>Pick a name stranger!</DialogTitle>
<DialogDescription className="text-start text-sm">
This is the name that will be displayed to your chat partner. You can also add some keywords that you want
to talk about.
</DialogDescription>
</DialogHeader>
<Form {...form}>
<form
onSubmit={(e) => {
e.stopPropagation()
form.handleSubmit(onSubmit)(e)
}}
className="space-y-4"
>
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem className="flex-grow">
<FormControl>
<Input placeholder="What do you want to be called?" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="keywords"
render={({ field }) => (
<FormItem className="flex-grow">
<FormControl>
<Input placeholder="Topics you want to talk about (comma separated)" {...field} />
</FormControl>
<FormDescription>You can leave this blank if you want to talk about anything.</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<DialogFooter>
<Button type="submit">Save changes</Button>
</DialogFooter>
</form>
</Form>
</DialogContent>
</Dialog>
)
}

export default SettingsDialog
4 changes: 2 additions & 2 deletions client/src/components/pages/home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import Chat from '../molecules/chat'
import { Button } from '../ui/button'

const Home = () => {
const { meRef, strangerRef, call, startCall } = useOmegle()
const { meRef, strangerRef, call, connect: startCall } = useOmegle()

return (
<div className="flex flex-col md:flex-row h-full max-h-full gap-8">
<div className="flex flex-col md:flex-row h-full gap-8">
<div className="w-full md:w-2/5 md:max-w-2xl flex flex-col gap-4">
<div className="flex flex-col gap-4 h-full">
<div className="p-4 border-primary border rounded-md w-full h-1/2">
Expand Down
17 changes: 17 additions & 0 deletions client/src/components/pages/not-found.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useRouteError } from 'react-router-dom';

const NotFound = () => {
const error = useRouteError() as { statusText?: string; message?: string }

return (
<div className="container flex space-y-4">
<h1 className="text-4xl">Oops!</h1>
<p>Sorry, an unexpected error has occurred.</p>
<p>
<i>{error.statusText || error.message}</i>
</p>
</div>
)
}

export default NotFound
Loading

0 comments on commit 7923784

Please sign in to comment.