Преглед изворни кода

Refactor AiAssistantSidebar and MessageCard components to streamline message handling and improve code clarity

Shun Miyazawa пре 10 месеци
родитељ
комит
73c4b40f4b

+ 31 - 17
apps/app/src/features/openai/client/components/AiAssistant/AiAssistantSidebar/AiAssistantSidebar.tsx

@@ -29,7 +29,7 @@ import {
 import { useAiAssistantSidebar } from '../../../stores/ai-assistant';
 import { useSWRxThreads } from '../../../stores/thread';
 
-import { MessageCard, type MessageCardRole } from './MessageCard';
+import { MessageCard } from './MessageCard';
 import { ResizableTextarea } from './ResizableTextArea';
 
 import styles from './AiAssistantSidebar.module.scss';
@@ -78,7 +78,7 @@ const AiAssistantSidebarSubstance: React.FC<AiAssistantSidebarSubstanceProps> =
 
     // Views
     initialView: initialViewForKnowledgeAssistant,
-    generateMessageCard: generateMessageCardForKnowledgeAssistant,
+    // generateMessageCard: generateMessageCardForKnowledgeAssistant,
     generateModeSwitchesDropdown: generateModeSwitchesDropdownForKnowledgeAssistant,
     headerIcon: headerIconForKnowledgeAssistant,
     headerText: headerTextForKnowledgeAssistant,
@@ -92,11 +92,14 @@ const AiAssistantSidebarSubstance: React.FC<AiAssistantSidebarSubstanceProps> =
     form: formForEditorAssistant,
     resetForm: resetFormEditorAssistant,
     isTextSelected,
-    isGeneratingEditorText,
 
     // Views
     generateInitialView: generateInitialViewForEditorAssistant,
-    generateMessageCard: generateMessageCardForEditorAssistant,
+    generatingEditorTextLabel,
+    generateActionButtons,
+    // generateMessageCard: generateMessageCardForEditorAssistant,
+    // generatingEditorTextForMessageCardAdditionaltem,
+    // actionButtonsForMessageCardAdditionaltem,
     headerIcon: headerIconForEditorAssistant,
     headerText: headerTextForEditorAssistant,
     placeHolder: placeHolderForEditorAssistant,
@@ -355,18 +358,25 @@ const AiAssistantSidebarSubstance: React.FC<AiAssistantSidebarSubstanceProps> =
     return initialViewForKnowledgeAssistant;
   }, [generateInitialViewForEditorAssistant, initialViewForKnowledgeAssistant, isEditorAssistant, submit]);
 
-  const messageCard = useCallback(
-    (role: MessageCardRole, children: string, messageId?: string, messageLogs?: MessageLog[], generatingAnswerMessage?: MessageLog) => {
-      if (isEditorAssistant) {
-        if (messageId == null || messageLogs == null) {
-          return <></>;
-        }
-        return generateMessageCardForEditorAssistant(role, children, messageId, messageLogs, generatingAnswerMessage);
+  const messageCardAdditionaltemForGeneratingMessage = useMemo(() => {
+    if (isEditorAssistant) {
+      return generatingEditorTextLabel;
+    }
+
+    return <></>;
+  }, [generatingEditorTextLabel, isEditorAssistant]);
+
+
+  const messageCardAdditionaltemForGeneratedMessage = useCallback((messageId?: string) => {
+    if (isEditorAssistant) {
+      if (messageId == null || messageLogs == null) {
+        return <></>;
       }
+      return generateActionButtons(messageId, messageLogs, generatingAnswerMessage);
+    }
 
-      return generateMessageCardForKnowledgeAssistant(role, children);
-    }, [generateMessageCardForEditorAssistant, generateMessageCardForKnowledgeAssistant, isEditorAssistant],
-  );
+    return undefined;
+  }, [generateActionButtons, generatingAnswerMessage, isEditorAssistant, messageLogs]);
 
   return (
     <>
@@ -391,13 +401,18 @@ const AiAssistantSidebarSubstance: React.FC<AiAssistantSidebarSubstanceProps> =
               <div className="vstack gap-4 pb-2">
                 { messageLogs.map(message => (
                   <>
-                    {messageCard(message.isUserMessage ? 'user' : 'assistant', message.content, message.id, messageLogs, generatingAnswerMessage)}
+                    <MessageCard
+                      role={message.isUserMessage ? 'user' : 'assistant'}
+                      additionaltem={messageCardAdditionaltemForGeneratedMessage(message.id)}
+                    >
+                      {message.content}
+                    </MessageCard>
                   </>
                 )) }
                 { generatingAnswerMessage != null && (
                   <MessageCard
-                    isGeneratingEditorText={isGeneratingEditorText}
                     role="assistant"
+                    additionaltem={messageCardAdditionaltemForGeneratingMessage}
                   >
                     {generatingAnswerMessage.content}
                   </MessageCard>
@@ -477,7 +492,6 @@ const AiAssistantSidebarSubstance: React.FC<AiAssistantSidebarSubstanceProps> =
                 </Collapse>
               </div>
             )}
-
           </div>
         </div>
       </div>

+ 10 - 55
apps/app/src/features/openai/client/components/AiAssistant/AiAssistantSidebar/MessageCard.tsx

@@ -1,6 +1,5 @@
-import { useCallback, useState, type JSX } from 'react';
+import { type JSX } from 'react';
 
-import { LoadingSpinner } from '@growi/ui/dist/components';
 import type { LinkProps } from 'next/link';
 import { useTranslation } from 'react-i18next';
 import ReactMarkdown from 'react-markdown';
@@ -34,28 +33,14 @@ const NextLinkWrapper = (props: LinkProps & {children: string, href: string}): J
 };
 
 const AssistantMessageCard = ({
-  children, isGeneratingEditorText, showActionButtons, onAccept, onDiscard,
+  children,
+  additionaltem,
 }: {
   children: string,
-  showActionButtons?: boolean
-  isGeneratingEditorText?: boolean
-  onAccept?: () => void,
-  onDiscard?: () => void,
+  additionaltem?: JSX.Element,
 }): JSX.Element => {
   const { t } = useTranslation();
 
-  const [isActionButtonClicked, setIsActionButtonClicked] = useState(false);
-
-  const clickActionButtonHandler = useCallback((action: 'accept' | 'discard') => {
-    setIsActionButtonClicked(true);
-    if (action === 'accept') {
-      onAccept?.();
-      return;
-    }
-
-    onDiscard?.();
-  }, [onAccept, onDiscard]);
-
   return (
     <div className={`card border-0 ${moduleClass} ${assistantMessageCardModuleClass}`}>
       <div className="card-body d-flex">
@@ -67,32 +52,7 @@ const AssistantMessageCard = ({
             ? (
               <>
                 <ReactMarkdown components={{ a: NextLinkWrapper }}>{children}</ReactMarkdown>
-
-                {isGeneratingEditorText && (
-                  <div className="text-muted mb-3">
-                    <LoadingSpinner />
-                    <span className="ms-2">{t('sidebar_ai_assistant.text_generation_by_editor_assistant_label')}</span>
-                  </div>
-                )}
-
-                {showActionButtons && !isActionButtonClicked && (
-                  <div className="d-flex mt-2 justify-content-start">
-                    <button
-                      type="button"
-                      className="btn btn-outline-secondary me-2"
-                      onClick={() => clickActionButtonHandler('discard')}
-                    >
-                      {t('sidebar_ai_assistant.discard')}
-                    </button>
-                    <button
-                      type="button"
-                      className="btn btn-success"
-                      onClick={() => clickActionButtonHandler('accept')}
-                    >
-                      {t('sidebar_ai_assistant.accept')}
-                    </button>
-                  </div>
-                )}
+                { additionaltem }
               </>
             )
             : (
@@ -107,30 +67,25 @@ const AssistantMessageCard = ({
   );
 };
 
-export type MessageCardRole = 'user' | 'assistant';
+
+type MessageCardRole = 'user' | 'assistant';
 
 type Props = {
   role: MessageCardRole,
   children: string,
-  showActionButtons?: boolean,
-  isGeneratingEditorText?: boolean,
-  onDiscard?: () => void,
-  onAccept?: () => void,
+  additionaltem?: JSX.Element,
 }
 
 export const MessageCard = (props: Props): JSX.Element => {
   const {
-    role, children, showActionButtons, isGeneratingEditorText, onAccept, onDiscard,
+    role, children, additionaltem,
   } = props;
 
   return role === 'user'
     ? <UserMessageCard>{children}</UserMessageCard>
     : (
       <AssistantMessageCard
-        showActionButtons={showActionButtons}
-        isGeneratingEditorText={isGeneratingEditorText}
-        onAccept={onAccept}
-        onDiscard={onDiscard}
+        additionaltem={additionaltem}
       >{children}
       </AssistantMessageCard>
     );

+ 41 - 18
apps/app/src/features/openai/client/services/editor-assistant.tsx

@@ -1,5 +1,5 @@
 import {
-  useCallback, useEffect, useState, useRef, useMemo,
+  useCallback, useEffect, useState, useRef, useMemo, type FC,
 } from 'react';
 
 import { GlobalCodeMirrorEditorKey } from '@growi/editor';
@@ -31,7 +31,6 @@ import type { MessageLog } from '../../interfaces/message';
 import type { IThreadRelationHasId } from '../../interfaces/thread-relation';
 import { ThreadType } from '../../interfaces/thread-relation';
 import { AiAssistantDropdown } from '../components/AiAssistant/AiAssistantSidebar/AiAssistantDropdown';
-import { MessageCard, type MessageCardRole } from '../components/AiAssistant/AiAssistantSidebar/MessageCard';
 import { QuickMenuList } from '../components/AiAssistant/AiAssistantSidebar/QuickMenuList';
 import { useAiAssistantSidebar } from '../stores/ai-assistant';
 
@@ -52,8 +51,8 @@ interface ProcessMessage {
 interface GenerateInitialView {
   (onSubmit: (data: FormData) => Promise<void>): JSX.Element;
 }
-interface GenerateMessageCard {
-  (role: MessageCardRole, children: string, messageId: string, messageLogs: MessageLog[], generatingAnswerMessage?: MessageLog): JSX.Element;
+interface GenerateActionButtons {
+  (messageId: string, messageLogs: MessageLog[], generatingAnswerMessage?: MessageLog): JSX.Element;
 }
 export interface FormData {
   input: string,
@@ -77,7 +76,8 @@ type UseEditorAssistant = () => {
 
   // Views
   generateInitialView: GenerateInitialView,
-  generateMessageCard: GenerateMessageCard,
+  generatingEditorTextLabel?: JSX.Element,
+  generateActionButtons: GenerateActionButtons,
   headerIcon: JSX.Element,
   headerText: JSX.Element,
   placeHolder: string,
@@ -348,8 +348,7 @@ export const useEditorAssistant: UseEditorAssistant = () => {
     );
   }, [selectedAiAssistant]);
 
-
-  const generateMessageCard: GenerateMessageCard = useCallback((role, children, messageId, messageLogs, generatingAnswerMessage) => {
+  const generateActionButtons: GenerateActionButtons = useCallback((messageId, messageLogs, generatingAnswerMessage) => {
     const isActionButtonShown = (() => {
       if (!aiAssistantSidebarData?.isEditorAssistant) {
         return false;
@@ -374,7 +373,6 @@ export const useEditorAssistant: UseEditorAssistant = () => {
       return false;
     })();
 
-
     const accept = () => {
       if (codeMirrorEditor?.view == null) {
         return;
@@ -388,17 +386,41 @@ export const useEditorAssistant: UseEditorAssistant = () => {
       mutateIsEnableUnifiedMergeView(false);
     };
 
+    if (!isActionButtonShown) {
+      return <></>;
+    }
+
     return (
-      <MessageCard
-        role={role}
-        showActionButtons={isActionButtonShown}
-        onAccept={accept}
-        onDiscard={reject}
-      >
-        {children}
-      </MessageCard>
+      <div className="d-flex mt-2 justify-content-start">
+        <button
+          type="button"
+          className="btn btn-outline-secondary me-2"
+          onClick={reject}
+        >
+          {t('sidebar_ai_assistant.discard')}
+        </button>
+        <button
+          type="button"
+          className="btn btn-success"
+          onClick={accept}
+        >
+          {t('sidebar_ai_assistant.accept')}
+        </button>
+      </div>
+    );
+  }, [aiAssistantSidebarData?.isEditorAssistant, codeMirrorEditor?.view, isEnableUnifiedMergeView, mutateIsEnableUnifiedMergeView, t]);
+
+  const generatingEditorTextLabel = useMemo(() => {
+    return (
+      <>
+        {isGeneratingEditorText && (
+          <span className="text-thinking">
+            {t('sidebar_ai_assistant.text_generation_by_editor_assistant_label')}
+          </span>
+        )}
+      </>
     );
-  }, [aiAssistantSidebarData?.isEditorAssistant, codeMirrorEditor?.view, isEnableUnifiedMergeView, mutateIsEnableUnifiedMergeView]);
+  }, [isGeneratingEditorText, t]);
 
   return {
     createThread,
@@ -411,7 +433,8 @@ export const useEditorAssistant: UseEditorAssistant = () => {
 
     // Views
     generateInitialView,
-    generateMessageCard,
+    generatingEditorTextLabel,
+    generateActionButtons,
     headerIcon,
     headerText,
     placeHolder,

+ 3 - 18
apps/app/src/features/openai/client/services/knowledge-assistant.tsx

@@ -17,7 +17,6 @@ import type { MessageLog, MessageWithCustomMetaData } from '../../interfaces/mes
 import type { IThreadRelationHasId } from '../../interfaces/thread-relation';
 import { ThreadType } from '../../interfaces/thread-relation';
 import { AiAssistantChatInitialView } from '../components/AiAssistant/AiAssistantSidebar/AiAssistantChatInitialView';
-import { MessageCard, type MessageCardRole } from '../components/AiAssistant/AiAssistantSidebar/MessageCard';
 import { useAiAssistantSidebar } from '../stores/ai-assistant';
 import { useSWRMUTxMessages } from '../stores/message';
 import { useSWRMUTxThreads } from '../stores/thread';
@@ -36,10 +35,6 @@ interface ProcessMessage {
   ): void;
 }
 
-interface GenerateMessageCard {
-  (role: MessageCardRole, children: string): JSX.Element;
-}
-
 export interface FormData {
   input: string
   summaryMode?: boolean
@@ -59,7 +54,7 @@ type UseKnowledgeAssistant = () => {
 
   // Views
   initialView: JSX.Element
-  generateMessageCard: GenerateMessageCard
+  // generateMessageCard: GenerateMessageCard
   generateModeSwitchesDropdown: GenerateModeSwitchesDropdown
   headerIcon: JSX.Element
   headerText: JSX.Element
@@ -153,16 +148,6 @@ export const useKnowledgeAssistant: UseKnowledgeAssistant = () => {
     );
   }, [aiAssistantSidebarData?.aiAssistantData]);
 
-  const generateMessageCard: GenerateMessageCard = useCallback((role, children) => {
-    return (
-      <MessageCard
-        role={role}
-      >
-        {children}
-      </MessageCard>
-    );
-  }, []);
-
   const [dropdownOpen, setDropdownOpen] = useState(false);
 
   const toggleDropdown = useCallback(() => {
@@ -244,7 +229,7 @@ export const useKnowledgeAssistant: UseKnowledgeAssistant = () => {
 
     // Views
     initialView,
-    generateMessageCard,
+    // generateMessageCard,
     generateModeSwitchesDropdown,
     headerIcon,
     headerText,
@@ -328,5 +313,5 @@ export const useFetchAndSetMessageDataEffect = (
     };
 
     fetchAndSetLogs();
-  }, [threadId, mutateMessageData, setMessageLogs]); // Dependencies
+  }, [threadId, mutateMessageData, setMessageLogs, aiAssistantSidebarData?.isEditorAssistant]); // Dependencies
 };