add messaging
Change-Id: I9f7b5c73e25774751a2c55ea6c2575feca5ebfd5
diff --git a/client/src/App.js b/client/src/App.js
index bce1842..b500052 100644
--- a/client/src/App.js
+++ b/client/src/App.js
@@ -32,10 +32,6 @@
return () => authManager.deinit()
}, []);
- console.log("App render")
- console.log(state)
- console.log(location)
-
if (!state.loaded) {
return <LoadingPage />
} else if (!state.auth.setupComplete) {
diff --git a/client/src/AuthManager.js b/client/src/AuthManager.js
index 46b4466..c256d51 100644
--- a/client/src/AuthManager.js
+++ b/client/src/AuthManager.js
@@ -52,7 +52,6 @@
this.authenticating = false
this.state.initialized = true
console.log("Init ended")
- console.log(response)
if (response.status === 200) {
const jsonData = await response.json()
Object.assign(this.state, {
@@ -71,9 +70,6 @@
} else {
this.state.error = true
}
- console.log("New auth state")
- console.log(this.state)
-
if (this.onAuthChanged)
this.onAuthChanged(this.state)
})
@@ -150,7 +146,6 @@
}
return fetch(url, init)
.then(response => {
- console.log(`Got status ${response.status}`)
if (response.status === 401) {
this.disconnect()
return this.fetch(url, init)
diff --git a/client/src/components/ConversationView.js b/client/src/components/ConversationView.js
index 6217905..5c5a936 100644
--- a/client/src/components/ConversationView.js
+++ b/client/src/components/ConversationView.js
@@ -1,12 +1,14 @@
-import CircularProgress from '@material-ui/core/CircularProgress';
import React, { useEffect, useState } from 'react';
import MessageList from './MessageList';
import SendMessageForm from './SendMessageForm';
import authManager from '../AuthManager'
import Conversation from '../../../model/Conversation';
import LoadingPage from './loading';
+import io from "socket.io-client";
const ConversationView = props => {
+ const [loadingMesages, setLoadingMesages] = useState(false)
+ const [socket, setSocket] = useState(undefined)
const [state, setState] = useState({
loaded: false,
error: false,
@@ -21,7 +23,7 @@
console.log(result)
setState({
loaded: true,
- conversation: Conversation.from(props.accountId, result)// result.map(account => Account.from(account)),
+ conversation: Conversation.from(props.accountId, result)
})
}, error => {
console.log(`get error ${error}`)
@@ -33,6 +35,52 @@
return () => controller.abort()
}, [props.accountId, props.conversationId])
+ useEffect(() => {
+ console.log("io.connect")
+ const socket = io()
+ setSocket(socket)
+ return () => {
+ console.log("io.disconnect")
+ socket.disconnect()
+ setSocket(undefined)
+ }
+ }, [])
+
+ useEffect(() => {
+ if (!state.conversation)
+ return
+ console.log(`io set conversation ${state.conversation.getId()} `+ socket)
+ if (socket)
+ socket.emit('conversation', { accountId: state.conversation.getAccountId(), conversationId: state.conversation.getId() })
+ socket.off('newMessage')
+ socket.on('newMessage', (data) => {
+ console.log("newMessage")
+ console.log(data)
+ setState(state => {
+ if (state.conversation)
+ state.conversation.addMessage(data)
+ return {...state}
+ })
+ })
+ }, [state.conversation ? state.conversation.getId() : "", socket])
+
+ useEffect(() => {
+ if (!loadingMesages || !state.conversation)
+ return
+ console.log(`Load more messages`)
+ const controller = new AbortController()
+ authManager.fetch(`/api/accounts/${state.conversation.getAccountId()}/conversations/${state.conversation.getId()}/messages`, {signal: controller.signal})
+ .then(res => res.json())
+ .then(messages => {
+ console.log(messages)
+ setLoadingMesages(false)
+ if (state.conversation)
+ state.conversation.addLoadedMessages(messages)
+ setState(state)
+ }).catch(e => console.log(e))
+ return () => controller.abort()
+ }, [state, loadingMesages])
+
const sendMessage = (message) => {
authManager.fetch(`/api/accounts/${props.accountId}/conversations/${props.conversationId}`, {
headers: {
@@ -44,24 +92,13 @@
})
}
- const loadMore = () => {
- authManager.fetch(`/api/accounts/${props.accountId}/conversations/${props.conversationId}/messages`)
- .then(res => res.json())
- .then(messages => {
- console.log(messages)
- state.conversation.addLoadedMessages(messages)
- setState(state)
- })
- }
-
- console.log("ConversationView render " + (state.conversation ? state.conversation.getMessages().length : "no conversation"))
if (state.loaded === false) {
return <LoadingPage />
} else if (state.error === true) {
return <div>Error loding {props.conversationId}</div>
} else {
return <div className="messenger">
- <MessageList conversation={state.conversation} loadMore={loadMore} messages={state.conversation.getMessages()} />
+ <MessageList conversation={state.conversation} loading={loadingMesages} loadMore={() => setLoadingMesages(true)} messages={state.conversation.getMessages()} />
<SendMessageForm onSend={sendMessage} />
</div>
}
diff --git a/client/src/components/Message.js b/client/src/components/Message.js
index 9054cd7..f4b4f57 100644
--- a/client/src/components/Message.js
+++ b/client/src/components/Message.js
@@ -1,16 +1,27 @@
import { Typography } from '@material-ui/core'
+import { GroupOutlined } from '@material-ui/icons'
import React from 'react'
+import ConversationAvatar from './ConversationAvatar'
function Message(props) {
- console.log("Message render")
- console.log(props.message)
-
- return (
- <div className="message">
- <div className="message-username">{props.message.author}</div>
- <Typography className="message-text">{props.message.body}</Typography>
- </div>
- )
+ const message = props.message
+ if (message.type == 'text/plain')
+ return (<div className="message">
+ <div className="message-avatar">
+ <ConversationAvatar name="{message.author}" /></div>
+ <Typography className="message-text">{message.body}</Typography>
+ </div>)
+ else if (message.type == 'contact')
+ return (<div className="contact-event">
+ <Typography className="message-text">Contact event</Typography>
+ </div>)
+ else if (message.type == 'initial')
+ return (<div className="conversation-event">
+ <Typography variant="h6" className="message-text" color="textSecondary">
+ <div className="inline-avatar"><GroupOutlined color="action" style={{ fontSize: 32 }} /></div>Conversation created
+ </Typography>
+ </div>)
+ else return ''
}
export default Message
\ No newline at end of file
diff --git a/client/src/components/MessageList.js b/client/src/components/MessageList.js
index b2fca5e..e73af6a 100644
--- a/client/src/components/MessageList.js
+++ b/client/src/components/MessageList.js
@@ -2,15 +2,14 @@
import React, { useEffect } from 'react'
import { Box, Divider, Typography } from '@material-ui/core'
import ConversationAvatar from './ConversationAvatar'
-const reverseMap = (arr, f) => arr.map((_, idx, arr) => f(arr[arr.length - 1 - idx ]));
export default function MessageList(props) {
const displayName = props.conversation.getDisplayName()
const messages = props.conversation.getMessages()
- console.log("MessageList render " + messages.length)
useEffect(() => {
- props.loadMore()
+ if (!props.loading)
+ props.loadMore()
}, [props.conversation.getId()])
return (
@@ -26,9 +25,8 @@
<Divider orientation="horizontal" />
</Box>
<div className="message-list">
- <div className="message-list-inner">
- {reverseMap(messages, (message) => <Message key={message.id} message={message} />)}
- </div>
+ {messages.map((message) => <Message key={message.id} message={message} />)}
+ <div style={{ border: "1px solid transparent" }}/>
</div>
</React.Fragment>
)
diff --git a/client/src/index.scss b/client/src/index.scss
index ab30741..19e2639 100644
--- a/client/src/index.scss
+++ b/client/src/index.scss
@@ -1,17 +1,18 @@
:root {
- --main-color: #1F1F1F;
+ --main-color: #1f1f1f;
--secondary-color: white;
--third-color: #8b8b8b;
--main-text-color: #3e5869;
--secondary-text-color: #b0c7d6;
- --send-message-form: #F5F5F5;
+ --send-message-form: #f5f5f5;
}
-html, body {
+html,
+body {
height: 100%;
margin: 0;
padding: 0;
- font-family: 'Open Sans', sans-serif;
+ font-family: "Open Sans", sans-serif;
font-weight: 200;
color: #3e5869;
}
@@ -26,21 +27,25 @@
grid-template-columns: 320px 1fr;
grid-template-rows: 40px 50px 1fr 1fr 92px;
grid-template-areas:
- "h m"
- "n m"
- "r m"
- "r m"
- "r m";
+ "h m"
+ "n m"
+ "r m"
+ "r m"
+ "r m";
}
.messenger {
grid-area: m;
- display: grid;
- grid-template-rows: 73px 1fr 72px;
- grid-template-areas:
- "h"
- "m"
- "i";
+ display: flex;
+ flex-direction: column;
+}
+
+.conversation-header {
+ grid-area: h;
+}
+
+.send-message-form {
+ margin: 0 16px 16px 8px;
}
.MuiContainer-root {
@@ -51,51 +56,12 @@
background-color: var(--main-color);
}
-.header-section {
- grid-area: h;
-}
-
.main-search {
grid-area: n;
}
-.rooms-list {
- grid-area: r;
-}
-
-.conversation-header {
- grid-area: h;
-}
-
-.message-list {
- grid-area: m;
- position: relative;
-}
-
-.message-list-inner {
- max-height: 100%;
- position: absolute;
- overflow-y: auto;
- width: 100%;
- bottom: 0;
- padding: 24px;
-}
-
-.send-message-form {
- grid-area: i;
-}
-
/* REST OF CSS */
-.header-section {
- background-color: var(--main-color);
-
-}
-
-.simple-menu {
- color: var(--send-message-form);
-}
-
.main-search-input {
padding: 8px;
background-color: var(--secondary-color);
@@ -103,42 +69,67 @@
}
.rooms-list {
+ grid-area: r;
overflow-y: scroll;
-}
-.rooms-list .MuiListItemText-primary,
-.rooms-list .MuiListItemText-secondary
-{
- overflow: hidden;
- text-overflow: ellipsis;
+
+ .MuiListItemText-primary,
+ .MuiListItemText-secondary {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
}
.list-placeholder {
margin: 32px auto;
width: 256px;
text-align: center;
+
+ .subtitle {
+ color: #a0a0a0;
+ }
}
-.list-placeholder .subtitle {
- color:#a0a0a0;
-}
+.message-list {
+ flex: 1;
+ height: 100%;
+ overflow: auto;
+ display: flex;
+ flex-direction: column-reverse;
+ padding: 16px 32px;
-.message {
- margin: 15px 0;
-}
+ .conversation-event {
+ margin: 8px auto;
-.message .message-username {
- font-size: 11px;
- font-weight: bold;
- color: var(--secondary-color);
- opacity: 0.9;
- margin-bottom: 6px;
-}
-.message .message-text {
- background: var(--third-color);
- color: var(--secondary-color);
- display: inline;
- padding: 8px 16px;
- border-radius: 16px;
+ .inline-avatar {
+ display: inline-block;
+ vertical-align: middle;
+ margin: 16px;
+ }
+ }
+
+ .message {
+ margin: 8px 0;
+
+ .message-avatar {
+ display: inline-block;
+ vertical-align: middle;
+ }
+ .message-username {
+ font-size: 11px;
+ font-weight: bold;
+ color: var(--secondary-color);
+ opacity: 0.9;
+ margin-bottom: 6px;
+ }
+ .message-text {
+ background: var(--third-color);
+ color: var(--secondary-color);
+ display: inline;
+ padding: 8px 16px;
+ border-radius: 16px;
+ margin: 8px;
+ }
+ }
}
.send-message-card {
diff --git a/client/src/pages/addContactPage.jsx b/client/src/pages/addContactPage.jsx
index ee57b69..a4c32d7 100644
--- a/client/src/pages/addContactPage.jsx
+++ b/client/src/pages/addContactPage.jsx
@@ -40,7 +40,7 @@
}
return (
- <Container className='message-list'>
+ <Container className='messenger'>
<Card variant='outlined' style={{ borderRadius: 16, maxWidth: 560, margin: "16px auto" }}>
<CardContent>
<Typography variant='h6'>Jami key ID</Typography>
diff --git a/client/src/pages/messenger.jsx b/client/src/pages/messenger.jsx
index 5b40718..9b825f9 100644
--- a/client/src/pages/messenger.jsx
+++ b/client/src/pages/messenger.jsx
@@ -3,9 +3,7 @@
import NewContactForm from '../components/NewContactForm'
//import Sound from 'react-sound';
-import io from "socket.io-client";
import ConversationList from '../components/ConversationList';
-//const socket = io.connect('http://localhost:3000');
import authManager from '../AuthManager'
import Conversation from '../../../model/Conversation'
import Contact from '../../../model/Contact'
@@ -16,6 +14,7 @@
const JamiMessenger = (props) => {
const [conversations, setConversations] = useState(undefined)
+ const [searchQuery, setSearchQuery] = useState('')
const [searchResult, setSearchResults] = useState(undefined)
const params = useParams()
@@ -23,36 +22,6 @@
const conversationId = props.conversationId || params.conversationId
const contactId = props.contactId || params.contactId
- //this.socket = socketIOClient(ENDPOINT);
- /*socket.on('connect', () => {
- console.log("Success !")
- })*/
- //this.socket.on("FromAPI", data => {
- // this.setState({
- // messages: [...this.state.messages, data]
- // })
- //});
- /*socket.on('receivedMessage', (data) => {
- const message = {
- senderId: '65f6674b26e5af6ed0b4e92a13b80ff4bbfdf1e8',
- text: data
- }
- this.setState({
- messages: [...this.state.messages, message],
- sound: true
- })
- });*/
- useEffect(() => {
- console.log("io.connect")
- const socket = io()
- socket.on('receivedMessage', (data) => {
- console.log("receivedMessage")
- console.log(data)
- conversation.addMessage(data)
- })
- return () => socket.disconnect()
- })
-
useEffect(() => {
const controller = new AbortController()
authManager.fetch(`/api/accounts/${accountId}/conversations`, {signal: controller.signal})
@@ -64,8 +33,11 @@
return () => controller.abort()
}, [accountId])
- const handleSearch = (query) => {
- authManager.fetch(`/api/accounts/${accountId}/ns/name/${query}`)
+ useEffect(() => {
+ if (!searchQuery)
+ return
+ const controller = new AbortController()
+ authManager.fetch(`/api/accounts/${accountId}/ns/name/${searchQuery}`, {signal: controller.signal})
.then(response => {
if (response.status === 200) {
return response.json()
@@ -80,18 +52,16 @@
}).catch(e => {
setSearchResults(undefined)
})
- }
-
- console.log("JamiMessenger render " + conversationId)
- console.log(props)
+ return () => controller.abort()
+ }, [accountId, searchQuery])
return (
<div className="app" >
<Header />
+ <NewContactForm onChange={setSearchQuery} />
{conversations ?
<ConversationList search={searchResult} conversations={conversations} accountId={accountId} /> :
<div className="rooms-list"><LoadingPage /></div>}
- <NewContactForm onChange={handleSearch} />
{conversationId && <ConversationView accountId={accountId} conversationId={conversationId} />}
{contactId && <AddContactPage accountId={accountId} contactId={contactId} />}
</div>