Skip to content
This repository has been archived by the owner on Sep 7, 2020. It is now read-only.

Commit

Permalink
feat: first version of UI
Browse files Browse the repository at this point in the history
  • Loading branch information
coderbyheart committed Jan 9, 2020
1 parent a670acf commit 4452ca2
Show file tree
Hide file tree
Showing 9 changed files with 667 additions and 57 deletions.
315 changes: 283 additions & 32 deletions package-lock.json

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"@commitlint/config-angular": "^8.3.4",
"@types/react": "^16.9.17",
"@types/react-dom": "^16.9.4",
"@types/uuid": "^3.4.6",
"husky": "^4.0.3",
"source-map-loader": "^0.2.4",
"ts-loader": "^6.2.1",
Expand All @@ -45,8 +46,12 @@
]
},
"dependencies": {
"@types/styled-components": "^4.4.2",
"date-fns": "^2.9.0",
"react": "^16.12.0",
"react-dom": "^16.12.0"
"react-dom": "^16.12.0",
"styled-components": "^4.4.1",
"uuid": "^3.3.3"
},
"release": {
"branch": "saga",
Expand Down
243 changes: 243 additions & 0 deletions src/Chat/Chat.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
import * as React from 'react'
import { useState } from 'react'
import styled from 'styled-components'
import { v4 } from 'uuid'
import { MessageItem } from './MessageItem'

const ChatWidget = styled.div`
@import url('https://rsms.me/inter/inter.css');
position: absolute;
z-index: 9999;
right: 1rem;
bottom: 1rem;
font-family: 'Inter', sans-serif;
max-width: 350px;
`

const Header = styled.div`
background-color: #3543ec;
color: #ffffff;
display: flex;
justify-content: space-between;
font-weight: 300;
font-family: 'Inter', sans-serif;
align-items: center;
width: 100%;
`

const Title = styled.div`
margin: 0.5rem 0.5rem 0.5rem 1rem;
`

const Button = styled.button`
font-family: 'Inter', sans-serif;
background-color: transparent;
border: 1px solid;
height: 30px;
margin: 0.5rem;
`

const MinimizeButton = styled(Button)`
width: 30px;
border-color: #fff;
${Header} & {
color: inherit;
}
`

const Footer = styled(Header)`
background-color: #d8d8d8;
`

const SendButton = styled(Button)`
background-color: #fff;
margin-left: 0.5rem;
`

const MessageInput = styled.input`
flex-grow: 1;
font-family: 'Inter', sans-serif;
background-color: #fff;
border: 1px solid;
height: 28px;
padding: 0 0.5rem;
margin-left: 0.5rem;
`

const MessageListContainer = styled.div`
width: 100%;
`

export type Message = {
from: string
message: string
id: string
sent: boolean
createdAt: Date
fromUser?: true
}

const MessageList = styled.div`
min-height: 200px;
max-height: 500px;
border-right: 1px solid #ccc;
border-left: 1px solid #ccc;
overflow-y: scroll;
`

export const Chat = ({ context }: { context: string }) => {
const storageKey = `DAChat:minimized:${context}`
const [isMinimized, minimize] = useState<boolean>(
window.localStorage.getItem(storageKey) === '1',
)
const memoMinimized = (state: boolean) => {
window.localStorage.setItem(storageKey, state ? '1' : '0')
minimize(state)
}
const [message, setMessage] = useState<string>('')
const [messages, updateMessages] = useState<Message[]>([
{
id: v4(),
sent: false,
message: 'This is a sample message, which should span multiple lines.',
createdAt: new Date(),
from: 'John',
},
{
id: v4(),
sent: false,
message:
'This is another sample message, which should span multiple lines. This is another sample message, which should span multiple lines.',
createdAt: new Date(),
from: 'Jane',
},

{
id: v4(),
sent: false,
message: 'This is a sample message from you.',
createdAt: new Date(),
from: 'You',
fromUser: true,
},
{
id: v4(),
sent: false,
message:
'This is another sample message, which should span multiple lines. This is another sample message, which should span multiple lines.',
createdAt: new Date(),
from: 'Jane',
},
{
id: v4(),
sent: false,
message:
'This is another sample message, which should span multiple lines. This is another sample message, which should span multiple lines.',
createdAt: new Date(),
from: 'Jane',
},
{
id: v4(),
sent: false,
message:
'This is another sample message, which should span multiple lines. This is another sample message, which should span multiple lines.',
createdAt: new Date(),
from: 'Jane',
},
{
id: v4(),
sent: false,
message:
'This is another sample message, which should span multiple lines. This is another sample message, which should span multiple lines.',
createdAt: new Date(),
from: 'Jane',
},
{
id: v4(),
sent: false,
message:
'This is another sample message, which should span multiple lines. This is another sample message, which should span multiple lines.',
createdAt: new Date(),
from: 'Jane',
},
])

const messageListRef = React.createRef<HTMLDivElement>()
let currentMessageListRef = messageListRef.current

const sendMessage = () => {
updateMessages([
...messages,
{
id: v4(),
sent: false,
message,
createdAt: new Date(),
from: 'You',
fromUser: true,
},
])
setMessage('')
currentMessageListRef = messageListRef.current
setTimeout(() => {
if (currentMessageListRef) {
currentMessageListRef.scrollTop = currentMessageListRef.scrollHeight
}
}, 250)
}
return (
<ChatWidget>
<Header>
<Title>
Chat context: <code>{context}</code>
</Title>
{!isMinimized && (
<MinimizeButton
onClick={() => {
memoMinimized(true)
}}
>
_
</MinimizeButton>
)}
{isMinimized && (
<MinimizeButton
onClick={() => {
memoMinimized(false)
}}
>
+
</MinimizeButton>
)}
</Header>
{!isMinimized && (
<>
<MessageListContainer>
<MessageList ref={messageListRef}>
{messages.map(m => (
<MessageItem key={m.id} message={m} />
))}
</MessageList>
</MessageListContainer>
<Footer>
<MessageInput
placeholder="Write something here..."
value={message}
onChange={({ target: { value } }) => {
setMessage(value)
}}
onKeyUp={({ keyCode }) => {
if (keyCode === 13 && message.length > 0) {
sendMessage()
}
}}
/>
<SendButton disabled={!(message.length > 0)} onClick={sendMessage}>
send
</SendButton>
</Footer>
</>
)}
</ChatWidget>
)
}
76 changes: 76 additions & 0 deletions src/Chat/MessageItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import * as React from 'react'
import styled from 'styled-components'
import { Message } from './Chat'
import { TimeAgo } from './TimeAgo'

const MessageView = styled.div`
border-radius: 10px;
background-color: #dbedff;
margin: 1rem 4rem 1rem 1rem;
`

const UserMessageView = styled(MessageView)`
margin: 1rem 1rem 1rem 4rem;
background-color: #dbfff7;
`

const Text = styled.div`
font-size: 95%;
padding: 0.5rem 0.5rem 0 0.5rem;
`

const Meta = styled.div`
display: flex;
justify-content: flex-end;
align-items: center;
font-size: 80%;
font-weight: 300;
margin: 0.5rem 0.5rem 0.5rem 0.5rem;
padding-bottom: 0.5rem;
opacity: 0.85;
`

const From = styled.div`
font-size: 80%;
font-weight: 300;
padding: 0.5rem 0.5rem 0 0.5rem;
opacity: 0.85;
`

const GreenIndicator = styled.span`
:after {
content: '';
}
border-radius: 100%;
border: 0;
width: 10px;
height: 10px;
display: inline-block;
background-color: #0f0;
margin-left: 0.25rem;
`

const RedIndicator = styled(GreenIndicator)`
background-color: #f00;
`

const Status = ({ sent }: { sent: boolean }) =>
sent ? <GreenIndicator /> : <RedIndicator />

export const MessageItem = ({
message: { from, sent, message, createdAt, fromUser },
}: {
message: Message
}) => {
const V = fromUser ? UserMessageView : MessageView
return (
<V>
<From>{from}</From>
<Text>{message}</Text>
<Meta>
<TimeAgo from={createdAt} />
{fromUser && <Status sent={sent} />}
</Meta>
</V>
)
}
25 changes: 25 additions & 0 deletions src/Chat/TimeAgo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import * as React from 'react'
import { useState, useEffect } from 'react'
import { formatDistance } from 'date-fns'
import { enGB } from 'date-fns/locale'

const d = (from: Date) => formatDistance(from, new Date(), { locale: enGB })

export const TimeAgo = ({ from, ...props }: { from: Date }) => {
const [ago, setTimeAgo] = useState<string>(d(from))

useEffect(() => {
const i = setInterval(() => {
setTimeAgo(d(from))
}, 60000)
return () => {
clearInterval(i)
}
})

return (
<time dateTime={from.toISOString()} {...props}>
{ago}
</time>
)
}
13 changes: 0 additions & 13 deletions src/components/Hello.tsx

This file was deleted.

Loading

0 comments on commit 4452ca2

Please sign in to comment.