diff --git a/frontend/src/features/people/hooks/usePeopleSocket.ts b/frontend/src/features/people/hooks/usePeopleSocket.ts deleted file mode 100644 index 9014edc..0000000 --- a/frontend/src/features/people/hooks/usePeopleSocket.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { useEffect, useRef } from 'react'; -import { usePeopleStore } from '../stores/usePeopleStore'; - -interface usePeopleSocketProps { - url: string; - isActive?: boolean; -} - -export const usePeopleSocket = ({ url, isActive = false }: usePeopleSocketProps) => { - // Establish WebSocket connection - const socketRef = useRef(null); - - useEffect(() => { - if (!isActive) return; - - // Establish WebSocket connection - socketRef.current = new WebSocket(url); - - // Handle WebSocket open, message, error, and close events here - socketRef.current.onopen = () => { - /* handle open */ - if (socketRef.current == null) return; - console.log('Sending message'); - socketRef.current.send(JSON.stringify({ message: 'testing' })); - }; - socketRef.current.onmessage = e => { - /* handle incoming messages */ - const wsData = JSON.parse(e.data); - console.log(wsData); - }; - socketRef.current.onerror = error => { - /* handle error */ - }; - socketRef.current.onclose = () => { - /* handle close */ - }; - - // Clean up the WebSocket connection when the component unmounts - return () => { - if (socketRef.current) { - socketRef.current.close(); - } - }; - }, [url, isActive]); - - useEffect(() => { - if (!isActive || socketRef.current == null) return; - - const socket = socketRef.current; - - // Subscribe to changes in the store - const unsubscribeData = usePeopleStore.subscribe( - state => state.data, - data => { - // Send message when data changes - //socket.send(JSON.stringify({ type: 'updateData', data })); - console.log('Data changed, sending message'); - socket.send(JSON.stringify({ message: 'testing' })); - }, - ); - - const unsubscribeDefinitions = usePeopleStore.subscribe( - state => state.propertyDefinitions, - definitions => { - // Send message when definitions change - //socket.send(JSON.stringify({ type: 'updateDefinitions', definitions })); - console.log('Definitions changed, sending message'); - socket.send(JSON.stringify({ message: 'testing' })); - }, - ); - - return () => { - // Unsubscribe when the component unmounts - unsubscribeData(); - unsubscribeDefinitions(); - }; - }, [isActive]); -}; diff --git a/frontend/src/features/people/pages/People.tsx b/frontend/src/features/people/pages/People.tsx index 2660534..4ce45ca 100644 --- a/frontend/src/features/people/pages/People.tsx +++ b/frontend/src/features/people/pages/People.tsx @@ -3,22 +3,37 @@ import { SegmentSelector, SelectedRowsBar, Table } from '../components'; import { MdAdd, MdConstruction } from 'react-icons/md'; import { usePeopleStore } from '../stores/usePeopleStore'; import { SoonLabel } from 'components'; -import { usePeopleSocket } from '../hooks/usePeopleSocket'; +import { useEffect, useRef } from 'react'; import { useProjectsStore } from 'features/projects/stores/useProjectsStore'; import { isHasData } from 'api/utils'; const People = () => { - const selectedProject = useProjectsStore(state => state.selectedProject); - const isActive = isHasData(selectedProject) && !!selectedProject.data.api_key; - const socketUrl = isActive - ? `ws://localhost:8001/ws/people/?api_key=${selectedProject.data.api_key}` - : ''; - usePeopleSocket({ url: socketUrl, isActive }); - const addRow = usePeopleStore(state => state.addRow); const selectedRowCount = usePeopleStore( state => Object.values(state.rowSelection || {}).filter(value => value).length, ); + + const destroy = usePeopleStore(state => state.destroy); + const setupSocket = usePeopleStore(state => state.setupSocket); + useEffect(() => { + // We track changes to the select project as we need the api_key in our socket connection + const unsubscribe = useProjectsStore.subscribe( + state => state.selectedProject, + (selectedProjectState, prevSelectedProjectState) => { + if (isHasData(selectedProjectState) && !!selectedProjectState.data.api_key) { + const socketUrl = `ws://localhost:8001/ws/people/?api_key=${selectedProjectState.data.api_key}`; + setupSocket(socketUrl); + } + }, + { fireImmediately: true }, + ); + + return () => { + destroy(); + unsubscribe(); + }; + }, [destroy, setupSocket]); + return (
diff --git a/frontend/src/features/people/stores/usePeopleStore.ts b/frontend/src/features/people/stores/usePeopleStore.ts index 042813e..f74e63c 100644 --- a/frontend/src/features/people/stores/usePeopleStore.ts +++ b/frontend/src/features/people/stores/usePeopleStore.ts @@ -25,73 +25,121 @@ interface Actions { addRow: () => void; setRowSelection: (newRowSelection: RowSelectionState) => void; deleteSelectedRows: () => void; + setupSocket: (url: string) => void; + destroy: () => void; } export const usePeopleStore = create()( - subscribeWithSelector(set => ({ - data: [ - { name: 'Jane Doe', age: 30 }, - { name: 'Jane Doe', age: 32 }, - { name: 'Earl Grey of West Sussex', age: 400000000 }, - // ... more data - ], - updateData: (rowIndex: number, columnId: string, value: unknown) => - set( - produce(draft => { - const row = draft.data[rowIndex] as Record; - if (row) { - row[columnId] = value; - } - }), - ), - addRow: () => - set( - produce(draft => { - draft.data.push({}); - }), - ), - rowSelection: {}, - propertyDefinitions: { - id_1: { accessor: 'name', header: 'Name' }, - id_2: { accessor: 'age', header: 'Age' }, - }, - upsertDefinition: (id, newDefinition) => - set( - produce(draft => { - if (draft.propertyDefinitions[id]) { - // If the definition exists, merge it - Object.assign(draft.propertyDefinitions[id], newDefinition); - } else { - // If the definition does not exist, create a new one - // (you should validate the full object structure here) - draft.propertyDefinitions[id] = newDefinition as Omit; - } - }), - ), - deleteDefinition: ( - id, // Implement the delete function - ) => - set( - produce(draft => { - delete draft.propertyDefinitions[id]; - }), - ), - setRowSelection: newRowSelection => - set( - produce(draft => { - draft.rowSelection = newRowSelection; - }), - ), - deleteSelectedRows: () => - set( - produce(draft => { - if (draft.rowSelection) { - // Filter out the rows that are selected - draft.data = draft.data.filter((_, index) => !draft.rowSelection?.[index]); - // Reset the rowSelection state after deletion - draft.rowSelection = {}; - } - }), - ), - })), + subscribeWithSelector((set, get) => { + return { + setupSocket: url => { + setupSocket(set, url); + }, + destroy: () => { + if (socket) { + socket.close(); + } + }, + data: [ + { name: 'Jane Doe', age: 30 }, + { name: 'Jane Doe', age: 32 }, + { name: 'Earl Grey of West Sussex', age: 400000000 }, + // ... more data + ], + updateData: (rowIndex: number, columnId: string, value: unknown) => { + // Update store + set( + produce(draft => { + const row = draft.data[rowIndex] as Record; + if (row) { + row[columnId] = value; + } + }), + ); + // Send message over socket + socket?.send(JSON.stringify({ message: 'testing' })); + }, + addRow: () => + set( + produce(draft => { + draft.data.push({}); + }), + ), + rowSelection: {}, + propertyDefinitions: { + id_1: { accessor: 'name', header: 'Name' }, + id_2: { accessor: 'age', header: 'Age' }, + }, + upsertDefinition: (id, newDefinition) => + set( + produce(draft => { + if (draft.propertyDefinitions[id]) { + // If the definition exists, merge it + Object.assign(draft.propertyDefinitions[id], newDefinition); + } else { + // If the definition does not exist, create a new one + // (you should validate the full object structure here) + draft.propertyDefinitions[id] = newDefinition as Omit; + } + }), + ), + deleteDefinition: ( + id, // Implement the delete function + ) => + set( + produce(draft => { + delete draft.propertyDefinitions[id]; + }), + ), + setRowSelection: newRowSelection => + set( + produce(draft => { + draft.rowSelection = newRowSelection; + }), + ), + deleteSelectedRows: () => + set( + produce(draft => { + if (draft.rowSelection) { + // Filter out the rows that are selected + draft.data = draft.data.filter((_, index) => !draft.rowSelection?.[index]); + // Reset the rowSelection state after deletion + draft.rowSelection = {}; + } + }), + ), + }; + }), ); + +let socket: WebSocket | null = null; + +const setupSocket = (set: any, url?: string | null) => { + if (socket) { + console.log('closing inside initializeSocket'); + socket.close(); + } + if (url == null) return; + + console.log('settings up socket'); + + socket = new WebSocket(url); + + socket.onopen = () => { + /* handle open */ + if (socket == null) return; + console.log('Sending message'); + socket.send(JSON.stringify({ message: 'testing' })); + }; + socket.onmessage = e => { + /* handle incoming messages */ + const wsData = JSON.parse(e.data); + console.log(wsData); + }; + socket.onerror = error => { + /* handle error */ + }; + socket.onclose = () => { + /* handle close */ + }; +}; \ No newline at end of file