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

Emoji Picker feature #55

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
"homepage": "https://pusher.github.io/react-slack-clone",
"dependencies": {
"@pusher/chatkit": "^0.7.9",
"emoji-js": "^3.4.0",
"emoji-picker-react": "^2.0.4",
"object-path-immutable": "^1.0.1",
"react": "^16.2.0",
"react-dom": "^16.2.0",
Expand Down
139 changes: 106 additions & 33 deletions src/components/CreateMessageForm/index.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,109 @@
import React from 'react'
import React, { Component } from 'react'
import style from './index.module.css'
import { FileInput } from '../FileInput'
import EmojiPicker from '../EmojiPicker'

export const CreateMessageForm = ({
state: { user = {}, room = {}, message = '' },
actions: { runCommand },
}) =>
room.id ? (
<form
className={style.component}
onSubmit={e => {
e.preventDefault()
const message = e.target[0].value
e.target[0].value = ''
message.startsWith('/')
? runCommand(message.slice(1))
: message.length > 0 &&
user.sendMessage({
text: message,
roomId: room.id,
})
}}
>
<input
placeholder="Type a Message.."
onInput={e => user.isTypingIn({ roomId: room.id })}
/>
<FileInput state={{ user, room, message }} />
<button type="submit">
<svg>
<use xlinkHref="index.svg#send" />
</svg>
</button>
</form>
) : null
export class CreateMessageForm extends Component {
constructor(props) {
super(props)

this.state = {
message: '',
cursorPostion: 0
}
}

render() {
const { runCommand, toggleEmojiPicker } = this.props.actions
const { user, room, message, isPickerShowing } = this.props.state

return room.id ? (
<form
className={style.component}
onSubmit={e => {
e.preventDefault()
const message = this.state.message
message.startsWith('/')
? runCommand(message.slice(1))
: message.length > 0 &&
user.sendMessage({
text: message,
roomId: room.id
})

this.setState({
message: ''
})
}}
>
<input
ref={input => {
this.messageInput = input
}}
placeholder="Type a Message.."
onInput={this.handleInput.bind(this)}
onClick={this.updateCursorPostion.bind(this)}
onKeyUp={e => {
if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') {
this.updateCursorPostion(e)
}
}}
value={this.state.message}
/>
<EmojiPicker
state={{ isPickerShowing }}
actions={{
toggleEmojiPicker,
handleEmojiSelection: this.addEmojiToMessage.bind(this)
}}
/>
<FileInput state={{ user, room, message }} />
<button type="submit">
<svg>
<use xlinkHref="index.svg#send" />
</svg>
</button>
</form>
) : null
}

handleInput(e) {
const { user, room } = this.props.state
user.isTypingIn({ roomId: room.id })

this.setState({
message: e.target.value,
cursorPostion: e.target.selectionStart
})
}

updateCursorPostion(e) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this name is missing the letter i

Should be updateCursorPosition

this.setState({
cursorPostion: e.target.selectionStart
})
}

addEmojiToMessage(emoji) {
let message = this.state.message
let newMessage = ''
let cursor = this.state.cursorPostion
let beforeCursorStr = ''
let afterCursorStr = ''

beforeCursorStr = message.substring(0, cursor)
afterCursorStr = message.substr(cursor)
newMessage = `${beforeCursorStr}${emoji}${afterCursorStr}`

// reset the cursor to be after the emoji
this.messageInput.setSelectionRange(
this.state.cursorPostion + 2,
this.state.cursorPostion + 2,
0
)

this.setState({
cursorPostion: this.state.cursorPostion + 2,
message: newMessage
})
}
}
42 changes: 42 additions & 0 deletions src/components/EmojiPicker/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import EmojiPicker from 'emoji-picker-react'
import style from './index.module.css'

import React from 'react'
import JSEMOJI from 'emoji-js'

let jsemoji = new JSEMOJI()

const Picker = ({
state: { isPickerShowing },
actions: { toggleEmojiPicker, handleEmojiSelection },
}) => (
<div
onClick={e => {
e.nativeEvent.stopImmediatePropagation()
}}
className={style.component}
>
<div
className={`${style.overlayWrapper} ${isPickerShowing ? style.show : ''}`}
>
<EmojiPicker
onEmojiClick={(emojiCode, emojiObj, e) => {
e.nativeEvent.stopImmediatePropagation()
const emoji = jsemoji.replace_colons(`:${emojiObj.name}:`)
handleEmojiSelection(emoji)
toggleEmojiPicker(!isPickerShowing)
}}
/>
</div>
<input
type={'button'}
className={style.button}
onClick={e => {
e.preventDefault()
toggleEmojiPicker(!isPickerShowing)
}}
/>
</div>
)

export default Picker
34 changes: 34 additions & 0 deletions src/components/EmojiPicker/index.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
.component {
width: 2rem;
height: 2rem;
position: relative;
}

.overlayWrapper {
height: 0;
overflow: hidden;
position: absolute;
bottom: 32px;
right: 0;
-webkit-transition: height .25s ease-in-out; /* Safari */
transition: height .25s ease-in-out;
}

.button {
width: 32px;
height: 32px;
position: absolute;
bottom: 0;
right: 0;
background-image: url("https://cdn.jsdelivr.net/emojione/assets/3.0/png/32/1f600.png");
background-position: center center;
background-repeat: no-repeat;
background-size: contain;
border: none;
cursor: pointer;
}

.show {
/* find a better way to set this height */
height: 333px;
}
22 changes: 16 additions & 6 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class View extends React.Component {
messages: {},
typing: {},
sidebarOpen: false,
isPickerShowing: false,
userListOpen: window.innerWidth > 1000,
}

Expand All @@ -38,6 +39,7 @@ class View extends React.Component {

setSidebar: sidebarOpen => this.setState({ sidebarOpen }),
setUserList: userListOpen => this.setState({ userListOpen }),
toggleEmojiPicker: isPickerShowing => this.setState({ isPickerShowing }),

// --------------------------------------
// User
Expand Down Expand Up @@ -66,12 +68,13 @@ class View extends React.Component {
)
},

subscribeToRoom: room =>
subscribeToRoom: room => {
!this.state.user.roomSubscriptions[room.id] &&
this.state.user.subscribeToRoom({
roomId: room.id,
hooks: { onNewMessage: this.actions.addMessage },
}),
this.state.user.subscribeToRoom({
roomId: room.id,
hooks: { onNewMessage: this.actions.addMessage },
})
},

createRoom: options =>
this.state.user.createRoom(options).then(this.actions.joinRoom),
Expand Down Expand Up @@ -172,7 +175,6 @@ class View extends React.Component {
// --------------------------------------
// Notifications
// --------------------------------------

showNotification: message => {
if (
'Notification' in window &&
Expand Down Expand Up @@ -210,6 +212,14 @@ class View extends React.Component {
window.history.replaceState(null, null, window.location.pathname)
ChatManager(this, user)
})

document.addEventListener(
'click',
e => {
this.actions.toggleEmojiPicker(false)
},
false
)
}

render() {
Expand Down
25 changes: 23 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1063,7 +1063,7 @@
lodash "^4.2.0"
to-fast-properties "^2.0.0"

"@pusher/chatkit@^0.7.3":
"@pusher/chatkit@^0.7.9":
version "0.7.9"
resolved "https://registry.yarnpkg.com/@pusher/chatkit/-/chatkit-0.7.9.tgz#34587299107baf55b36b3fe1c9870ea99e90a62a"
dependencies:
Expand Down Expand Up @@ -1611,7 +1611,7 @@ babel-register@^6.26.0:
mkdirp "^0.5.1"
source-map-support "^0.4.15"

babel-runtime@^6.22.0, babel-runtime@^6.26.0:
babel-runtime@^6.22.0, babel-runtime@^6.25.0, babel-runtime@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
dependencies:
Expand Down Expand Up @@ -2964,6 +2964,23 @@ elliptic@^6.0.0:
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.0"

[email protected]:
version "4.0.0"
resolved "https://registry.yarnpkg.com/emoji-datasource/-/emoji-datasource-4.0.0.tgz#3fc9c0c2f4fb321d9291138819f6d100603d3e2f"

emoji-js@^3.4.0:
version "3.4.0"
resolved "https://registry.yarnpkg.com/emoji-js/-/emoji-js-3.4.0.tgz#dabdeda60c92d1948a5177e51ba9421d2029b052"
dependencies:
emoji-datasource "4.0.0"

emoji-picker-react@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/emoji-picker-react/-/emoji-picker-react-2.0.4.tgz#a58edc3412d39726dbcd64a7811806ccc90d9b46"
dependencies:
babel-runtime "^6.25.0"
throttle-debounce "^1.0.1"

emoji-regex@^6.1.0:
version "6.5.1"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-6.5.1.tgz#9baea929b155565c11ea41c6626eaa65cef992c2"
Expand Down Expand Up @@ -7955,6 +7972,10 @@ throat@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a"

throttle-debounce@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-1.0.1.tgz#dad0fe130f9daf3719fdea33dc36a8e6ba7f30b5"

through2@^2.0.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be"
Expand Down