Skip to content

Commit

Permalink
feat(channel-web): convos view arrow navigation
Browse files Browse the repository at this point in the history
  • Loading branch information
EFF committed Mar 21, 2019
1 parent 80994c2 commit b6b9a4b
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 62 deletions.
20 changes: 20 additions & 0 deletions modules/channel-web/src/views/lite/icons/Add.jsx
@@ -0,0 +1,20 @@
import React from 'react'

export default ({ height, width }) => (
<i>
<svg
xmlns="http://www.w3.org/2000/svg"
version="1.1"
x="0px"
y="0px"
width={width || 20}
height={height || 20}
viewBox="0 0 357 357"
space="preserve"
>
<g id="add">
<path d="M357,204H204v153h-51V204H0v-51h153V0h51v153h153V204z" />
</g>
</svg>
</i>
)
97 changes: 97 additions & 0 deletions modules/channel-web/src/views/lite/side/ConversationList.jsx
@@ -0,0 +1,97 @@
import React from 'react'
import style from './style.scss'

import distanceInWordsToNow from 'date-fns/distance_in_words_to_now'
import Add from '../icons/Add'

const ConversationListItem = ({ conversation, onClick, hasFocus }) => {
const title = conversation.title || conversation.message_author || 'Untitled Conversation'
const date = distanceInWordsToNow(new Date(conversation.message_sent_on || conversation.created_on))
const message = conversation.message_text || '...'

return (
<div className={`bp-item ${style.item} ${hasFocus && style.focus}`} onClick={onClick}>
<div className={style.right}>
<div className={'bp-title ' + style.title}>
<div className={style.name}>{title}</div>
<div className={style.date}>
<span>{date}</span>
</div>
</div>
<div className={'bp-preview ' + style.text}>{message}</div>
</div>
</div>
)
}

class ConversationList extends React.Component {
state = {
focusIdx: null
}

changeFocus = step => {
let focusIdx = this.state.focusIdx || 0
focusIdx += step

if (focusIdx > this.props.conversations.length) {
focusIdx = 0
} else if (focusIdx < 0) {
focusIdx = this.props.conversations.length
}

this.setState({ focusIdx })
}

componentDidMount() {
this.main.focus()
}

componentDidUpdate(_, prevState) {
if (this.state.focusIdx === this.props.conversations.length) {
this.btn.focus()
} else if (prevState.focusIdx === this.props.conversations.length) {
this.main.focus()
}
}

handleKeyDown = e => {
if (e.key === 'ArrowDown' || e.key === 'ArrowLeft') {
this.changeFocus(1)
} else if (e.key == 'ArrowUp' || e.key == 'ArrowRight') {
this.changeFocus(-1)
} else if (e.key == 'Enter' && this.state.focusIdx && this.state.focusIdx < this.props.conversations.length) {
const convoId = this.props.conversations[this.state.focusIdx].id
this.props.onConversationClicked(convoId)
}
}

render() {
const { conversations, createConversation, onConversationClicked } = this.props
return (
<div
tabindex="0"
ref={el => (this.main = el)}
className={`bp-list-convo ${style.list}`}
onKeyDown={this.handleKeyDown}
>
{conversations.map((convo, idx) => (
<ConversationListItem
key={convo.id}
hasFocus={this.state.focusIdx == idx}
conversation={convo}
onClick={onConversationClicked.bind(this, convo.id)}
/>
))}
<button
ref={el => (this.btn = el)}
className={'bp-new-convo-btn ' + style.addConvoBtn}
onClick={createConversation}
>
<Add width={15} height={15} />
</button>
</div>
)
}
}

export default ConversationList
76 changes: 22 additions & 54 deletions modules/channel-web/src/views/lite/side/index.jsx
@@ -1,8 +1,6 @@
import React from 'react'
import classnames from 'classnames'

import distanceInWordsToNow from 'date-fns/distance_in_words_to_now'

import Send from '../send'
import MessageList from '../messages'
import Input from '../input'
Expand All @@ -12,6 +10,7 @@ import BotInfo from '../bot-info'

import style from './style.scss'
import { getOverridedComponent } from '../messages/misc'
import ConversationList from './ConversationList'

export default class Side extends React.Component {
state = {
Expand Down Expand Up @@ -63,6 +62,11 @@ export default class Side extends React.Component {
})
}

handleConvoClicked = convoId => {
this.props.onSwitchConvo && this.props.onSwitchConvo(convoId)
this.handleToggleShowConvos()
}

renderAvatar() {
const name = this.props.botInfo.name || this.props.config.botName
const avatarUrl =
Expand Down Expand Up @@ -275,67 +279,31 @@ export default class Side extends React.Component {
)
}

renderItemConvos = (convo, key) => {
const title = convo.title || convo.message_author || 'Untitled Conversation'
const date = distanceInWordsToNow(new Date(convo.message_sent_on || convo.created_on))
const message = convo.message_text || '...'

const onClick = () => {
this.props.onSwitchConvo && this.props.onSwitchConvo(convo.id)

this.setState({
showConvos: false
})
}

return (
<div className={'bp-item ' + style.item} key={key} onClick={onClick}>
<div className={style.left}>{this.renderAvatar()}</div>
<div className={style.right}>
<div className={'bp-title ' + style.title}>
<div className={style.name}>{title}</div>
<div className={style.date}>
<span>{date}</span>
</div>
</div>
<div className={'bp-preview ' + style.text}>{message}</div>
</div>
</div>
)
}

renderListOfConvos() {
const btnColor = this.props.config && this.props.config.textColorOnBackground
renderBotInfoPage = () => {
// TODO move this logic in botInfo component
const isConvoStarted = this.props.currentConversation && !!this.props.currentConversation.messages.length
const onDismiss = isConvoStarted ? this.toggleBotInfo : this.props.startConversation.bind(this, this.toggleBotInfo)
return (
<div className={'bp-list-convo ' + style.list}>
{this.props.conversations.map(this.renderItemConvos)}
<button
className={'bp-new-convo-btn ' + style.addConvoBtn}
style={{ color: btnColor, borderColor: btnColor }}
onClick={this.props.createConversation}
>
+
</button>
</div>
<BotInfo
botInfo={this.props.botInfo}
webchatConfig={this.props.config}
dismissLabel={isConvoStarted ? 'Back to Conversation' : 'Start Conversation'}
onDismiss={onDismiss}
/>
)
}

renderBody() {
if (this.state.showConvos) {
return this.renderListOfConvos()
} else if (this.state.showBotInfo) {
const isConvoStarted = this.props.currentConversation && !!this.props.currentConversation.messages.length
const onDismiss = isConvoStarted
? this.toggleBotInfo
: this.props.startConversation.bind(this, this.toggleBotInfo)
return (
<BotInfo
botInfo={this.props.botInfo}
webchatConfig={this.props.config}
dismissLabel={isConvoStarted ? 'Back to Conversation' : 'Start Conversation'}
onDismiss={onDismiss}
<ConversationList
conversations={this.props.conversations}
createConversation={this.props.createConversation}
onConversationClicked={this.handleConvoClicked}
/>
)
} else if (this.state.showBotInfo) {
return this.renderBotInfoPage()
} else {
return this.renderConversation()
}
Expand Down
18 changes: 10 additions & 8 deletions modules/channel-web/src/views/lite/side/style.scss
Expand Up @@ -171,18 +171,18 @@
.addConvoBtn {
background: transparent;
border: 2px solid;
font-size: 36px;
border-radius: 50%;
float: left;
margin-left: 15px;
float: right;
margin-right: 15px;
margin-top: 15px;
margin-bottom: 15px;
width: 52px;
height: 52px;
width: 40px;
height: 40px;
padding: 0;
cursor: pointer;
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.4);

&:hover {
&:hover,
&:focus {
background-color: rgba(0, 0, 0, 0.03);
}
}
Expand Down Expand Up @@ -304,6 +304,7 @@
overflow-x: hidden;

.item {
margin-left: 15px;
height: 4.7rem;
border-bottom: 1px solid #eee;
cursor: pointer;
Expand All @@ -314,7 +315,8 @@
display: -ms-flexbox;
display: flex;

&:hover {
&:hover,
&.focus {
background-color: #fafafa;
}

Expand Down

0 comments on commit b6b9a4b

Please sign in to comment.