@@ -9,14 +9,7 @@ import {
99 VariantType ,
1010 useToastProvider ,
1111} from '@openfun/cunningham-react' ;
12- import {
13- PropsWithChildren ,
14- ReactNode ,
15- useCallback ,
16- useEffect ,
17- useMemo ,
18- useState ,
19- } from 'react' ;
12+ import { PropsWithChildren , ReactNode , useMemo } from 'react' ;
2013import { useTranslation } from 'react-i18next' ;
2114
2215import { isAPIError } from '@/api' ;
@@ -70,9 +63,8 @@ export function AIGroupButton() {
7063 const { t } = useTranslation ( ) ;
7164 const { currentDoc } = useDocStore ( ) ;
7265 const { data : docOptions } = useDocOptions ( ) ;
73- const [ languages , setLanguages ] = useState < LanguageTranslate [ ] > ( [ ] ) ;
7466
75- useEffect ( ( ) => {
67+ const languages = useMemo ( ( ) => {
7668 const languages = docOptions ?. actions . POST . language . choices ;
7769
7870 if ( ! languages ) {
@@ -90,7 +82,7 @@ export function AIGroupButton() {
9082 'pl' ,
9183 ] ) ;
9284
93- setLanguages ( languages ) ;
85+ return languages ;
9486 } , [ docOptions ?. actions . POST . language . choices ] ) ;
9587
9688 const show = useMemo ( ( ) => {
@@ -220,45 +212,19 @@ const AIMenuItemTransform = ({
220212 children,
221213 icon,
222214} : PropsWithChildren < AIMenuItemTransform > ) => {
223- const editor = useBlockNoteEditor ( ) ;
224215 const { mutateAsync : requestAI , isPending } = useDocAITransform ( ) ;
225- const handleAIError = useHandleAIError ( ) ;
226-
227- const handleAIAction = useCallback ( async ( ) => {
228- const selectedBlocks = editor . getSelection ( ) ?. blocks ;
229-
230- if ( ! selectedBlocks || selectedBlocks . length === 0 ) {
231- return ;
232- }
233216
234- const markdown = await editor . blocksToMarkdownLossy ( selectedBlocks ) ;
235-
236- try {
237- const responseAI = await requestAI ( {
238- text : markdown ,
239- action,
240- docId,
241- } ) ;
242-
243- if ( ! responseAI . answer ) {
244- return ;
245- }
246-
247- const blockMarkdown = await editor . tryParseMarkdownToBlocks (
248- responseAI . answer ,
249- ) ;
250- editor . replaceBlocks ( selectedBlocks , blockMarkdown ) ;
251- } catch ( error ) {
252- handleAIError ( error ) ;
253- }
254- } , [ editor , requestAI , action , docId , handleAIError ] ) ;
217+ const requestAIAction = async ( markdown : string ) => {
218+ const responseAI = await requestAI ( {
219+ text : markdown ,
220+ action,
221+ docId,
222+ } ) ;
223+ return responseAI . answer ;
224+ } ;
255225
256226 return (
257- < AIMenuItem
258- icon = { icon }
259- handleAIAction = { handleAIAction }
260- isPending = { isPending }
261- >
227+ < AIMenuItem icon = { icon } requestAI = { requestAIAction } isPending = { isPending } >
262228 { children }
263229 </ AIMenuItem >
264230 ) ;
@@ -276,43 +242,21 @@ const AIMenuItemTranslate = ({
276242 icon,
277243 language,
278244} : PropsWithChildren < AIMenuItemTranslate > ) => {
279- const editor = useBlockNoteEditor ( ) ;
280245 const { mutateAsync : requestAI , isPending } = useDocAITranslate ( ) ;
281- const handleAIError = useHandleAIError ( ) ;
282-
283- const handleAIAction = useCallback ( async ( ) => {
284- const selectedBlocks = editor . getSelection ( ) ?. blocks ;
285-
286- if ( ! selectedBlocks || selectedBlocks . length === 0 ) {
287- return ;
288- }
289-
290- const markdown = await editor . blocksToMarkdownLossy ( selectedBlocks ) ;
291-
292- try {
293- const responseAI = await requestAI ( {
294- text : markdown ,
295- language,
296- docId,
297- } ) ;
298246
299- if ( ! responseAI . answer ) {
300- return ;
301- }
302-
303- const blockMarkdown = await editor . tryParseMarkdownToBlocks (
304- responseAI . answer ,
305- ) ;
306- editor . replaceBlocks ( selectedBlocks , blockMarkdown ) ;
307- } catch ( error ) {
308- handleAIError ( error ) ;
309- }
310- } , [ editor , requestAI , language , docId , handleAIError ] ) ;
247+ const requestAITranslate = async ( markdown : string ) => {
248+ const responseAI = await requestAI ( {
249+ text : markdown ,
250+ language,
251+ docId,
252+ } ) ;
253+ return responseAI . answer ;
254+ } ;
311255
312256 return (
313257 < AIMenuItem
314258 icon = { icon }
315- handleAIAction = { handleAIAction }
259+ requestAI = { requestAITranslate }
316260 isPending = { isPending }
317261 >
318262 { children }
@@ -321,19 +265,45 @@ const AIMenuItemTranslate = ({
321265} ;
322266
323267interface AIMenuItemProps {
324- handleAIAction : ( ) => Promise < void > ;
268+ requestAI : ( markdown : string ) => Promise < string > ;
325269 isPending : boolean ;
326270 icon ?: ReactNode ;
327271}
328272
329273const AIMenuItem = ( {
330- handleAIAction ,
274+ requestAI ,
331275 isPending,
332276 children,
333277 icon,
334278} : PropsWithChildren < AIMenuItemProps > ) => {
335279 const Components = useComponentsContext ( ) ;
336280
281+ const editor = useBlockNoteEditor ( ) ;
282+ const handleAIError = useHandleAIError ( ) ;
283+
284+ const handleAIAction = async ( ) => {
285+ const selectedBlocks = editor . getSelection ( ) ?. blocks ;
286+
287+ if ( ! selectedBlocks || selectedBlocks . length === 0 ) {
288+ return ;
289+ }
290+
291+ const markdown = await editor . blocksToMarkdownLossy ( selectedBlocks ) ;
292+
293+ try {
294+ const responseAI = await requestAI ( markdown ) ;
295+
296+ if ( ! responseAI ) {
297+ return ;
298+ }
299+
300+ const blockMarkdown = await editor . tryParseMarkdownToBlocks ( responseAI ) ;
301+ editor . replaceBlocks ( selectedBlocks , blockMarkdown ) ;
302+ } catch ( error ) {
303+ handleAIError ( error ) ;
304+ }
305+ } ;
306+
337307 if ( ! Components ) {
338308 return null ;
339309 }
@@ -359,26 +329,12 @@ const useHandleAIError = () => {
359329 const { toast } = useToastProvider ( ) ;
360330 const { t } = useTranslation ( ) ;
361331
362- const handleAIError = useCallback (
363- ( error : unknown ) => {
364- if ( isAPIError ( error ) ) {
365- error . cause ?. forEach ( ( cause ) => {
366- if (
367- cause === 'Request was throttled. Expected available in 60 seconds.'
368- ) {
369- toast (
370- t ( 'Too many requests. Please wait 60 seconds.' ) ,
371- VariantType . ERROR ,
372- ) ;
373- }
374- } ) ;
375- }
376-
377- toast ( t ( 'AI seems busy! Please try again.' ) , VariantType . ERROR ) ;
378- console . error ( error ) ;
379- } ,
380- [ toast , t ] ,
381- ) ;
332+ return ( error : unknown ) => {
333+ if ( isAPIError ( error ) && error . status === 429 ) {
334+ toast ( t ( 'Too many requests. Please wait 60 seconds.' ) , VariantType . ERROR ) ;
335+ return ;
336+ }
382337
383- return handleAIError ;
338+ toast ( t ( 'AI seems busy! Please try again.' ) , VariantType . ERROR ) ;
339+ } ;
384340} ;
0 commit comments