Просмотр исходного кода

Use SWR to manage sidebar open/close

Shun Miyazawa 1 год назад
Родитель
Сommit
1cb4d0a3e9

+ 41 - 40
apps/app/src/features/openai/client/components/AiAssistant/AiAssistantChatSidebar/AiAssistantChatSidebar.tsx

@@ -5,6 +5,8 @@ import {
 
 import SimpleBar from 'simplebar-react';
 
+import { useAiAssistantChatSidebar } from '../../../stores/ai-assistant';
+
 const RIGHT_SIDEBAR_WIDTH = 500;
 
 // Assistant Info Component
@@ -17,49 +19,52 @@ const AssistantInfo: FC = () => {
   );
 };
 
-// Right Sidebar Tab
-type RightSidebarTabProps = {
-  isOpen: boolean;
-  onClick: () => void;
-};
+// // Right Sidebar Tab
+// type RightSidebarTabProps = {
+//   isOpen: boolean;
+//   onClick: () => void;
+// };
 
-const RightSidebarTab: FC<RightSidebarTabProps> = memo(({ isOpen, onClick }) => {
-  return (
-    <button
-      type="button"
-      className="position-fixed top-50 end-0 bg-white border-start border-top border-bottom px-2 py-3 translate-middle-y"
-      style={{
-        transform: 'translateX(-100%) translateY(-50%) rotate(-90deg)',
-        transformOrigin: '100% 50%',
-        borderRadius: '4px 4px 0 0',
-        zIndex: 1030,
-      }}
-      onClick={onClick}
-    >
-      <span className="d-flex align-items-center">
-        <span className="material-symbols-outlined me-2" style={{ transform: 'rotate(90deg)' }}>
-          {isOpen ? 'close' : 'robot'}
-        </span>
-        GROWI AI について
-      </span>
-    </button>
-  );
-});
+// const RightSidebarTab: FC<RightSidebarTabProps> = memo(({ isOpen, onClick }) => {
+//   return (
+//     <button
+//       type="button"
+//       className="position-fixed top-50 end-0 bg-white border-start border-top border-bottom px-2 py-3 translate-middle-y"
+//       style={{
+//         transform: 'translateX(-100%) translateY(-50%) rotate(-90deg)',
+//         transformOrigin: '100% 50%',
+//         borderRadius: '4px 4px 0 0',
+//         zIndex: 1030,
+//       }}
+//       onClick={onClick}
+//     >
+//       <span className="d-flex align-items-center">
+//         <span className="material-symbols-outlined me-2" style={{ transform: 'rotate(90deg)' }}>
+//           {isOpen ? 'close' : 'robot'}
+//         </span>
+//         GROWI AI について
+//       </span>
+//     </button>
+//   );
+// });
 
 
 export const AiAssistantChatSidebar: FC = memo((): JSX.Element => {
-  const [isOpen, setIsOpen] = useState(false);
   const sidebarRef = useRef<HTMLDivElement>(null);
   const sidebarScrollerRef = useRef<HTMLDivElement>(null);
 
-  const handleToggle = useCallback(() => {
-    setIsOpen(prev => !prev);
-  }, []);
+  const { data: aiAssistantChatSidebarData, close: closeAiAssistantChatSidebar } = useAiAssistantChatSidebar();
+  const isOpened = aiAssistantChatSidebarData?.isOpened ?? false;
+
+
+  // const handleToggle = useCallback(() => {
+  //   setIsOpen(prev => !prev);
+  // }, []);
 
   useEffect(() => {
     const handleClickOutside = (event: MouseEvent) => {
-      if (isOpen && sidebarRef.current && !sidebarRef.current.contains(event.target as Node)) {
-        setIsOpen(false);
+      if (isOpened && sidebarRef.current && !sidebarRef.current.contains(event.target as Node)) {
+        closeAiAssistantChatSidebar();
       }
     };
 
@@ -67,15 +72,11 @@ export const AiAssistantChatSidebar: FC = memo((): JSX.Element => {
     return () => {
       document.removeEventListener('mousedown', handleClickOutside);
     };
-  }, [isOpen]);
+  }, [closeAiAssistantChatSidebar, isOpened]);
 
   return (
     <>
-      <RightSidebarTab
-        isOpen={isOpen}
-        onClick={handleToggle}
-      />
-      {isOpen && (
+      {isOpened && (
         <div
           ref={sidebarRef}
           className="position-fixed top-0 end-0 h-100 border-start bg-white shadow-sm"
@@ -88,7 +89,7 @@ export const AiAssistantChatSidebar: FC = memo((): JSX.Element => {
             <button
               type="button"
               className="btn btn-link p-0 border-0"
-              onClick={handleToggle}
+              onClick={closeAiAssistantChatSidebar}
             >
               <span className="material-symbols-outlined">close</span>
             </button>

+ 8 - 2
apps/app/src/features/openai/client/components/AiAssistant/Sidebar/AiAssistantTree.tsx

@@ -8,6 +8,7 @@ import { useCurrentUser } from '~/stores-universal/context';
 import type { AiAssistantAccessScope } from '../../../../interfaces/ai-assistant';
 import { AiAssistantShareScope, type AiAssistantHasId } from '../../../../interfaces/ai-assistant';
 import { deleteAiAssistant } from '../../../services/ai-assistant';
+import { useAiAssistantChatSidebar } from '../../../stores/ai-assistant';
 
 import styles from './AiAssistantTree.module.scss';
 
@@ -84,6 +85,7 @@ type AiAssistantItemProps = {
   currentUserId?: string;
   aiAssistant: AiAssistantHasId;
   threads: Thread[];
+  onItemClicked?: () => void;
   onDeleted?: () => void;
 };
 
@@ -91,13 +93,14 @@ const AiAssistantItem: React.FC<AiAssistantItemProps> = ({
   currentUserId,
   aiAssistant,
   threads,
+  onItemClicked,
   onDeleted,
 }) => {
   const [isThreadsOpened, setIsThreadsOpened] = useState(false);
 
   const openChatHandler = useCallback(() => {
-    // TODO: https://redmine.weseek.co.jp/issues/159530
-  }, []);
+    onItemClicked?.();
+  }, [onItemClicked]);
 
   const openThreadsHandler = useCallback(() => {
     setIsThreadsOpened(toggle => !toggle);
@@ -183,6 +186,8 @@ type AiAssistantTreeProps = {
 
 export const AiAssistantTree: React.FC<AiAssistantTreeProps> = ({ aiAssistants, onDeleted }) => {
   const { data: currentUser } = useCurrentUser();
+  const { open: openAiAssistantChatSidebar } = useAiAssistantChatSidebar();
+
   return (
     <ul className={`list-group ${moduleClass}`}>
       {aiAssistants.map(assistant => (
@@ -191,6 +196,7 @@ export const AiAssistantTree: React.FC<AiAssistantTreeProps> = ({ aiAssistants,
           currentUserId={currentUser?._id}
           aiAssistant={assistant}
           threads={dummyThreads}
+          onItemClicked={openAiAssistantChatSidebar}
           onDeleted={onDeleted}
         />
       ))}

+ 23 - 0
apps/app/src/features/openai/client/stores/ai-assistant.tsx

@@ -51,3 +51,26 @@ export const useSWRxAiAssistants = (): SWRResponse<AccessibleAiAssistantsHasId,
     ([endpoint]) => apiv3Get(endpoint).then(response => response.data.accessibleAiAssistants),
   );
 };
+
+
+type AiAssistantChatSidebarStatus = {
+  isOpened: boolean,
+}
+
+type AiAssistantChatSidebarUtils = {
+  open(): void
+  close(): void
+}
+
+export const useAiAssistantChatSidebar = (
+    status?: AiAssistantChatSidebarStatus,
+): SWRResponse<AiAssistantChatSidebarStatus, Error> & AiAssistantChatSidebarUtils => {
+  const initialStatus = { isOpened: false };
+  const swrResponse = useSWRStatic<AiAssistantChatSidebarStatus, Error>('AiAssistantChatSidebar', status, { fallbackData: initialStatus });
+
+  return {
+    ...swrResponse,
+    open: useCallback(() => { swrResponse.mutate({ isOpened: true }) }, [swrResponse]),
+    close: useCallback(() => swrResponse.mutate({ isOpened: false }), [swrResponse]),
+  };
+};