Skip to content

Commit

Permalink
Feature: Improved Message Flow (#11)
Browse files Browse the repository at this point in the history
* Message flow with local persistance. Sync deleted nodes from interep
  • Loading branch information
njofce authored Apr 7, 2022
1 parent 8d8d0ad commit 4f0d4f4
Show file tree
Hide file tree
Showing 73 changed files with 2,613 additions and 774 deletions.
24 changes: 24 additions & 0 deletions app/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# dependencies
node_modules

# testing
/coverage

# production
build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
5 changes: 5 additions & 0 deletions app/.prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"semi": false,
"arrowParens": "always",
"trailingComma": "none"
}
12 changes: 9 additions & 3 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,18 @@
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build"
"build": "react-scripts build",
"prettier": "prettier -c src",
"prettier:fix": "prettier -w src"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
],
"rules": {
"react-hooks/exhaustive-deps": 0
}
},
"browserslist": {
"production": [
Expand Down Expand Up @@ -79,6 +84,7 @@
"@types/react-redux": "^7.1.20",
"@types/redux": "^3.6.0",
"@types/redux-thunk": "^2.1.0",
"@types/styled-components": "^5.1.15"
"@types/styled-components": "^5.1.15",
"prettier": "^2.6.2"
}
}
6 changes: 3 additions & 3 deletions app/src/App.scss
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@
}
}

.modal-body {
.modal-body {
padding: 40px;
}

.modal-header {
color: #475C7A;
.modal-header {
color: #475c7a;
}
16 changes: 8 additions & 8 deletions app/src/App.test.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import App from './App';
import React from "react"
import { render, screen } from "@testing-library/react"
import App from "./App"

test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});
test("renders learn react link", () => {
render(<App />)
const linkElement = screen.getByText(/learn react/i)
expect(linkElement).toBeInTheDocument()
})
10 changes: 5 additions & 5 deletions app/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import "./App.scss";
import { BrowserRouter } from "react-router-dom";
import AppWrapper from "./components/AppWrapper";
import "./App.scss"
import { BrowserRouter } from "react-router-dom"
import AppWrapper from "./components/AppWrapper"

function App() {
return (
Expand All @@ -11,7 +11,7 @@ function App() {
</div>
</div>
</BrowserRouter>
);
)
}

export default App;
export default App
125 changes: 80 additions & 45 deletions app/src/components/AppWrapper/index.tsx
Original file line number Diff line number Diff line change
@@ -1,69 +1,104 @@
import React from "react";
import { Routes, Route, Navigate } from "react-router-dom";
import { useLocation, useNavigate } from "react-router";
import RegisterOrRecover from "../RegisterOrRecover";
import Dashboard from "../Dashboard";
import { init, receive_message } from "rln-client-lib";
import { useEffect } from "react";
import { useAppDispatch } from "../../redux/hooks/useAppDispatch";
import React from "react"
import PublicRoomInvitedScreen from "../PublicRoomInvitedScreen"
import RegisterOrRecover from "../RegisterOrRecover"
import Dashboard from "../Dashboard"
import SyncSpinner from "../Spinner"
import { Routes, Route, Navigate } from "react-router-dom"
import { useLocation, useNavigate } from "react-router"
import { get_rooms, init, receive_message } from "rln-client-lib"
import { useEffect } from "react"
import { useAppDispatch } from "../../redux/hooks/useAppDispatch"
import {
addMessageToRoomAction,
getChatHistoryAction,
getRoomsAction
} from "../../redux/actions/actionCreator";
import PublicRoomInvitedScreen from "../PublicRoomInvitedScreen";
import { roomTypes, serverUrl, socketUrl } from "../../constants/constants";
import { ToastContainer } from 'react-toastify';
getRoomsAction,
loadMessagesForRooms,
runSyncMessageHistory
} from "../../redux/actions/actionCreator"
import { roomTypes, serverUrl, socketUrl } from "../../constants/constants"
import { ToastContainer } from "react-toastify"

import 'react-toastify/dist/ReactToastify.css';
import { generateProof } from "../../util/util";
import "react-toastify/dist/ReactToastify.css"
import { IMessage } from "rln-client-lib/dist/src/chat/interfaces"
import { IRooms } from "rln-client-lib/dist/src/profile/interfaces"
import { useAppSelector } from "../../redux/hooks/useAppSelector"

const AppWrapper = () => {
const navigate = useNavigate();
const dispatch = useAppDispatch();
const location = useLocation();
const navigate = useNavigate()
const dispatch = useAppDispatch()
const location = useLocation()

const isChatHistorySyncing = useAppSelector(
(state) => state.ChatReducer.chatHistorySyncing
)

useEffect(() => {
initializeApp();
initializeApp()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
}, [])

const initializeApp = async () => {
try {
await init({
serverUrl: serverUrl,
socketUrl: socketUrl
},
generateProof)
.then(() => {
if (!location.pathname.includes(roomTypes.public))
navigate("/dashboard");
dispatch(getRoomsAction());
dispatch(getChatHistoryAction());
})
.then(async () => {
await receive_message(receiveMessageCallback);
});
}, generateProof).then(() => {
if (!location.pathname.includes(roomTypes.public))
navigate("/dashboard")
dispatch(getRoomsAction())
dispatch(
runSyncMessageHistory({
onSuccess: () => {
loadMessagesFromDb()
}
})
)
})
} catch (error) {
navigate("/r-procedure");
navigate("/r-procedure")
}
};
}

const loadMessagesFromDb = async () => {
const allRooms: IRooms = await get_rooms()
const roomIds: string[] = [
...allRooms.direct.map((d) => d.id),
...allRooms.private.map((d) => d.id),
...allRooms.public.map((d) => d.id)
]

const nowTimestamp: number = new Date().getTime()
dispatch(loadMessagesForRooms(roomIds, nowTimestamp))

await receive_message(receiveMessageCallback)
}

const receiveMessageCallback = (message: any, roomId: string) => {
dispatch(addMessageToRoomAction(message, roomId));
};
const receiveMessageCallback = (message: IMessage, roomId: string) => {
dispatch(addMessageToRoomAction(message, roomId))
}

return (
<div className="w-100 vh-100 container-fluid">
<Routes>
<Route path="/r-procedure" element={<RegisterOrRecover />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/public/:roomId" element={<PublicRoomInvitedScreen />} />
<Route path="/" element={<Navigate replace to="/r-procedure" />} />
</Routes>
<ToastContainer />
{isChatHistorySyncing ? (
<SyncSpinner />
) : (
<>
{" "}
<Routes>
<Route path="/r-procedure" element={<RegisterOrRecover />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route
path="/public/:roomId"
element={<PublicRoomInvitedScreen />}
/>
<Route path="/" element={<Navigate replace to="/r-procedure" />} />
</Routes>
<ToastContainer />
</>
)}
</div>
);
};
)
}

export default AppWrapper;
export default AppWrapper
116 changes: 116 additions & 0 deletions app/src/components/ChatMessages/ChatMessagesWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import React, { useEffect, useRef } from "react"
import { Spinner } from "reactstrap"
import styled from "styled-components"
import * as Colors from "../../constants/colors"
import { loadMessagesForRoom } from "../../redux/actions/actionCreator"
import { useAppDispatch } from "../../redux/hooks/useAppDispatch"
import { useAppSelector } from "../../redux/hooks/useAppSelector"

const StyledSingleMessage = styled.div`
font-size: 16px;
color: black;
border-radius: 10px;
padding: 8px 12px;
margin-bottom: 16px;
width: fit-content;
text-align: left;
`
const StyledMessagesWrapper = styled.div`
overflow-y: scroll;
padding: 20px 40px;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
height: 85%;
div:nth-child(odd) {
background: ${Colors.ANATRACITE};
color: white;
}
div:nth-child(even) {
background: #f0f2f5;
}
`

const StyledSpinnerWrapper = styled.div`
span {
color: ${Colors.ANATRACITE};
}
height: 85%;
align-items: center;
display: flex;
flex-direction: column;
justify-content: center;
`

interface ChatMessagesProps {
chatHistory: any[]
chatRef: React.RefObject<HTMLDivElement>
currentActiveRoom: any
}

const ChatMessagesWrapper = ({
chatHistory,
chatRef,
currentActiveRoom
}: ChatMessagesProps) => {
const dispatch = useAppDispatch()
const sortedHistory = chatHistory.sort((a, b) => a.epoch - b.epoch)
const chatHistoryRef = useRef(sortedHistory)
const loadingMessages = useAppSelector((state) => state.ChatReducer.loading)

const handleScroll = () => {
// @ts-ignore
if (chatRef.current.scrollTop === 0) {
fetchNewMessages()
}
}

const fetchNewMessages = () => {
const lastMessage = chatHistoryRef.current[0]

dispatch(
loadMessagesForRoom(
currentActiveRoom.id,
new Date(lastMessage.epoch).getTime(),
false,
{
onSuccess: (res: any) => {
chatHistoryRef.current = res.concat(sortedHistory)
}
}
)
)
}

useEffect(() => {
chatRef.current?.addEventListener("scroll", handleScroll)
return () => {
chatRef.current?.removeEventListener("scroll", handleScroll)
}
}, [])

return loadingMessages ? (
<StyledSpinnerWrapper>
<>
<Spinner
animation="grow"
role="status"
className="spinner-grow text-secondary"
/>{" "}
<span> Loading messages </span>
</>
</StyledSpinnerWrapper>
) : (
<StyledMessagesWrapper ref={chatRef}>
{sortedHistory.length > 0 &&
sortedHistory.map((messageObj) => (
<StyledSingleMessage key={messageObj.uuid}>
{messageObj.message_content}
</StyledSingleMessage>
))}
</StyledMessagesWrapper>
)
}

export default ChatMessagesWrapper
Loading

0 comments on commit 4f0d4f4

Please sign in to comment.