Skip to content

Commit

Permalink
release 0.0.8 (#30)
Browse files Browse the repository at this point in the history
* feat: web socket (#29)
  • Loading branch information
SchneiderNicolas authored Oct 9, 2023
1 parent eff0786 commit a26d0aa
Show file tree
Hide file tree
Showing 11 changed files with 441 additions and 36 deletions.
80 changes: 80 additions & 0 deletions front/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions front/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"react-responsive": "^9.0.2",
"react-router-dom": "^6.16.0",
"react-scripts": "^5.0.1",
"socket.io-client": "^4.7.2",
"swr": "^2.2.4",
"tailwindcss": "^3.3.3",
"typescript": "^4.9.5",
Expand Down
49 changes: 26 additions & 23 deletions front/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,35 +11,38 @@ import { DiscussionProvider } from './contexts/DiscussionContext';
import { useResponsive } from './hooks/useResponsive';
import Discussion from './components/discussion/Discussion';
import NewDiscussion from './components/discussion/new/NewDiscussion';
import { SocketProvider } from './contexts/SocketContext';

const App = () => {
const { isMobile } = useResponsive();
return (
<BrowserRouter>
<DiscussionProvider>
<div className="flex">
<ConditionalSidebar />
<div className="flex-grow">
<Routes>
<Route path="/signin" element={<SignIn />} />
<Route path="/signup" element={<SignUp />} />
<Route path="/register" element={<AcceptInvitationPage />} />
<Route path="/" element={<ProtectedRoute />}>
<Route index element={<Home />} />
<Route
path="/discussion/:discussionId"
element={isMobile ? <Discussion /> : <Home />}
/>
<Route
path="/new"
element={isMobile ? <NewDiscussion /> : <Home />}
/>
</Route>
<Route path="/*" element={<NotFound />} />
</Routes>
<SocketProvider>
<DiscussionProvider>
<div className="flex">
<ConditionalSidebar />
<div className="flex-grow">
<Routes>
<Route path="/signin" element={<SignIn />} />
<Route path="/signup" element={<SignUp />} />
<Route path="/register" element={<AcceptInvitationPage />} />
<Route path="/" element={<ProtectedRoute />}>
<Route index element={<Home />} />
<Route
path="/discussion/:discussionId"
element={isMobile ? <Discussion /> : <Home />}
/>
<Route
path="/new"
element={isMobile ? <NewDiscussion /> : <Home />}
/>
</Route>
<Route path="/*" element={<NotFound />} />
</Routes>
</div>
</div>
</div>
</DiscussionProvider>
</DiscussionProvider>
</SocketProvider>
</BrowserRouter>
);
};
Expand Down
18 changes: 18 additions & 0 deletions front/src/components/discussion/Discussion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Discussion as DiscussionType } from '../../types/discussionTypes';
import MessageBubble from './MessageBubble';
import DiscussionTopBar from './DiscussionTopBar';
import { useDiscussionContext } from '../../contexts/DiscussionContext';
import { useSocket } from '../../contexts/SocketContext';

const Discussion = () => {
const [messageInput, setMessageInput] = useState('');
Expand Down Expand Up @@ -35,6 +36,23 @@ const Discussion = () => {
fetcher(url, cookies.accessToken) as Promise<DiscussionType>,
);
const messagesEndRef = useRef<null | HTMLDivElement>(null);
const socket = useSocket();

useEffect(() => {
if (socket && discussionId) {
socket.emit('join', discussionId);

const handleNewMessage = () => {
mutate();
};

socket.on('new-message', handleNewMessage);
return () => {
socket.emit('leave', discussionId);
socket.off('new-message', handleNewMessage);
};
}
}, [socket, discussionId, mutate]);

useEffect(() => {
if (messagesEndRef.current) {
Expand Down
35 changes: 30 additions & 5 deletions front/src/components/discussion/list/DiscussionCards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,23 @@ import fetcher from '../../../utils/fetcher';
import { Discussion } from '../../../types/discussionTypes';
import { useNavigate } from 'react-router-dom';
import { useDiscussionContext } from '../../../contexts/DiscussionContext';
import { useSocket } from '../../../contexts/SocketContext';

const DiscussionCards = () => {
const [cookies] = useCookies(['accessToken']);
const [cookies] = useCookies(['accessToken', 'userId']);
const navigate = useNavigate();
const { showDiscussion, viewState } = useDiscussionContext();
const { forceUpdate } = useDiscussionContext();
const [localDiscussions, setLocalDiscussions] = useState<Discussion[] | null>(
null,
);
const socket = useSocket();

const { data: discussions, error } = useSWR<Discussion[]>(
const {
data: discussions,
error,
mutate,
} = useSWR<Discussion[]>(
[`${config.API_BASE_URL}/discussions`, forceUpdate],
([url]) => fetcher(url, cookies.accessToken),
);
Expand All @@ -25,6 +31,23 @@ const DiscussionCards = () => {
setLocalDiscussions(discussions || null);
}, [discussions]);

useEffect(() => {
if (socket && cookies.userId) {
socket.emit('join', cookies.userId.toString());

const handleNewMessageNotification = () => {
mutate();
};

socket.on('new-message-notification', handleNewMessageNotification);

return () => {
socket.emit('leave', cookies.userId.toString());
socket.off('new-message-notification', handleNewMessageNotification);
};
}
}, [socket, cookies.userId, mutate]);

const handleDiscussionClick = (id: number) => {
if (localDiscussions) {
const updatedDiscussions = localDiscussions.map((discussion) =>
Expand All @@ -51,20 +74,22 @@ const DiscussionCards = () => {
}`}
onClick={() => handleDiscussionClick(discussion.id)}
>
{discussion.isNew && (
{discussion.isNew && discussion.id !== viewState.discussionId && (
<span className="block absolute top-0 right-0 w-3 h-3 mt-2 mr-2 bg-violet-500 rounded-full" />
)}
<h2
className={`text-stone-800 ${
discussion.isNew ? 'font-bold' : 'font-medium'
discussion.isNew && discussion.id !== viewState.discussionId
? 'font-bold'
: 'font-medium'
}`}
>
{discussion.title}
</h2>
<div className="flex justify-between items-center mt-1 text-sm">
<p
className={`truncate flex-grow ${
discussion.isNew
discussion.isNew && discussion.id !== viewState.discussionId
? 'font-semibold text-stone-800'
: 'text-zinc-500'
}`}
Expand Down
36 changes: 36 additions & 0 deletions front/src/contexts/SocketContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {
createContext,
ReactNode,
useContext,
useEffect,
useState,
} from 'react';
import { io, Socket } from 'socket.io-client';
import config from '../config/config';

const SocketContext = createContext<Socket | null>(null);

type SocketProviderProps = {
children: ReactNode;
};

export const SocketProvider = ({ children }: SocketProviderProps) => {
const [socket, setSocket] = useState<Socket | null>(null);

useEffect(() => {
const socketIo = io(config.API_BASE_URL);
setSocket(socketIo);

return () => {
socketIo.disconnect();
};
}, []);

return (
<SocketContext.Provider value={socket}>{children}</SocketContext.Provider>
);
};

export const useSocket = () => {
return useContext(SocketContext);
};
Loading

0 comments on commit a26d0aa

Please sign in to comment.