@@ -8,13 +8,14 @@ import SoundIndicator from "@/components/sound-indicator";
8
8
import { useHypr } from "@/contexts" ;
9
9
import { useEnhancePendingState } from "@/hooks/enhance-pending" ;
10
10
import { commands as analyticsCommands } from "@hypr/plugin-analytics" ;
11
+ import { commands as dbCommands } from "@hypr/plugin-db" ;
11
12
import { commands as listenerCommands } from "@hypr/plugin-listener" ;
12
13
import { commands as localSttCommands } from "@hypr/plugin-local-stt" ;
13
14
import { Button } from "@hypr/ui/components/ui/button" ;
14
15
import { Popover , PopoverContent , PopoverTrigger } from "@hypr/ui/components/ui/popover" ;
15
16
import { Spinner } from "@hypr/ui/components/ui/spinner" ;
17
+ import { Textarea } from "@hypr/ui/components/ui/textarea" ;
16
18
import { toast } from "@hypr/ui/components/ui/toast" ;
17
- import { Tooltip , TooltipContent , TooltipTrigger } from "@hypr/ui/components/ui/tooltip" ;
18
19
import { cn } from "@hypr/ui/lib/utils" ;
19
20
import { useOngoingSession , useSession } from "@hypr/utils/contexts" ;
20
21
import ShinyButton from "./shiny-button" ;
@@ -150,25 +151,106 @@ export default function ListenButton({ sessionId }: { sessionId: string }) {
150
151
}
151
152
152
153
function WhenInactiveAndMeetingNotEnded ( { disabled, onClick } : { disabled : boolean ; onClick : ( ) => void } ) {
154
+ const [ open , setOpen ] = useState ( false ) ;
155
+ const [ instructionText , setInstructionText ] = useState ( "" ) ;
156
+
157
+ const configQuery = useQuery ( {
158
+ queryKey : [ "config" , "general" ] ,
159
+ queryFn : async ( ) => await dbCommands . getConfig ( ) ,
160
+ } ) ;
161
+
162
+ useEffect ( ( ) => {
163
+ if ( configQuery . data ?. general ?. jargons ) {
164
+ setInstructionText ( ( configQuery . data . general . jargons ?? [ ] ) . join ( ", " ) ) ;
165
+ }
166
+ } , [ configQuery . data ] ) ;
167
+
168
+ const mutation = useMutation ( {
169
+ mutationFn : async ( jargons : string ) => {
170
+ if ( ! configQuery . data ) {
171
+ return ;
172
+ }
173
+
174
+ const nextGeneral = {
175
+ ...( configQuery . data . general ?? { } ) ,
176
+ jargons : [ jargons ] ,
177
+ } ;
178
+ await dbCommands . setConfig ( {
179
+ ...configQuery . data ,
180
+ general : nextGeneral ,
181
+ } ) ;
182
+ } ,
183
+ onSuccess : ( ) => {
184
+ configQuery . refetch ( ) ;
185
+ } ,
186
+ onError : console . error ,
187
+ } ) ;
188
+
189
+ const handleSaveJargons = ( ) => {
190
+ const currentConfigJargons = ( configQuery . data ?. general ?. jargons ?? [ ] ) . join ( ", " ) ;
191
+ if ( instructionText !== currentConfigJargons ) {
192
+ mutation . mutate ( instructionText ) ;
193
+ }
194
+ } ;
195
+
153
196
return (
154
- < Tooltip >
155
- < TooltipTrigger asChild >
197
+ < Popover
198
+ open = { open }
199
+ onOpenChange = { ( newOpen ) => {
200
+ setOpen ( newOpen ) ;
201
+ if ( ! newOpen ) {
202
+ handleSaveJargons ( ) ;
203
+ }
204
+ } }
205
+ >
206
+ < PopoverTrigger asChild >
156
207
< button
157
208
disabled = { disabled }
158
- onClick = { onClick }
159
209
className = { cn ( [
160
- "w-9 h-9 rounded-full border-2 transition-all hover:scale-95 cursor-pointer outline-none p-0 flex items-center justify-center shadow-[inset_0_0_0_2px_rgba(255,255,255,0.8)] " ,
210
+ "w-9 h-9 rounded-full border-2 transition-all hover:scale-95 cursor-pointer outline-none p-0 flex items-center justify-center" ,
161
211
disabled ? "bg-neutral-200 border-neutral-400" : "bg-red-500 border-neutral-400" ,
212
+ "shadow-[inset_0_0_0_2px_rgba(255,255,255,0.8)]" ,
162
213
] ) }
163
214
>
164
215
</ button >
165
- </ TooltipTrigger >
166
- < TooltipContent side = "bottom" align = "end" >
167
- < p >
168
- < Trans > Start recording</ Trans >
169
- </ p >
170
- </ TooltipContent >
171
- </ Tooltip >
216
+ </ PopoverTrigger >
217
+ < PopoverContent className = "w-80" align = "end" >
218
+ < div className = "flex flex-col gap-3" >
219
+ < div className = "flex flex-col gap-2" >
220
+ < div className = "text-sm font-medium text-neutral-700" >
221
+ < Trans > Custom instruction</ Trans >
222
+ </ div >
223
+ < Textarea
224
+ value = { instructionText }
225
+ onChange = { ( e ) => setInstructionText ( e . target . value ) }
226
+ placeholder = "ex) Hyprnote, JDCE, Fastrepl, John, Yujong"
227
+ className = "min-h-[80px] resize-none border-neutral-300 text-sm focus-visible:ring-0 focus-visible:ring-offset-0"
228
+ />
229
+ < p className = "text-xs text-neutral-400" >
230
+ < Trans >
231
+ Provide descriptions about the meeting. Company specific terms, acronyms, jargons... any thing!
232
+ </ Trans >
233
+ </ p >
234
+ </ div >
235
+
236
+ < Button
237
+ onClick = { ( ) => {
238
+ handleSaveJargons ( ) ;
239
+ onClick ( ) ;
240
+ setOpen ( false ) ;
241
+ } }
242
+ disabled = { disabled }
243
+ className = "w-full flex items-center gap-2"
244
+ >
245
+ < div className = "relative flex-shrink-0" >
246
+ < div className = "w-3 h-3 rounded-full bg-red-500" > </ div >
247
+ < div className = "absolute top-0 left-0 w-3 h-3 rounded-full bg-red-500 animate-ping opacity-75" > </ div >
248
+ </ div >
249
+ < Trans > Start Meeting</ Trans >
250
+ </ Button >
251
+ </ div >
252
+ </ PopoverContent >
253
+ </ Popover >
172
254
) ;
173
255
}
174
256
0 commit comments