Skip to content

Commit

Permalink
feat(emoji): add emoji picker functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
ph1p committed May 24, 2020
1 parent 03342fd commit 084f513
Show file tree
Hide file tree
Showing 13 changed files with 131 additions and 54 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,8 @@ jobs:
- name: npm install
run: npm install

- name: npm run lint
run: npm run lint

- name: npm run build
run: npm run build
2 changes: 1 addition & 1 deletion src/Ui.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ import SettingsView from './views/Settings';
import UserListView from './views/UserList';

import { reaction } from 'mobx';
import { useStore, StoreProvider } from './store';
import theme from './shared/theme';
import { useStore, StoreProvider } from './store';

onmessage = (message) => {
if (message.data.pluginMessage) {
Expand Down
19 changes: 9 additions & 10 deletions src/assets/icons/Emoji.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,17 @@ import React, { FunctionComponent } from 'react';

const EmojiIcon: FunctionComponent = () => {
return (
<svg
width="10"
height="10"
viewBox="0 0 10 10"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="none">
<path
d="M2.38396 1.47595H8.55196M8.55196 1.47595V7.64395M8.55196 1.47595L1.35596 8.67195"
stroke="white"
strokeWidth="1.028"
fill="#A2ADC0"
d="M9.153 13.958a5.532 5.532 0 01-5.122-3.41.394.394 0 01.585-.482.396.396 0 01.146.178 4.741 4.741 0 004.391 2.923c.625 0 1.244-.124 1.82-.365a4.72 4.72 0 002.558-2.558.396.396 0 01.73.305 5.51 5.51 0 01-2.984 2.983 5.499 5.499 0 01-2.124.426z"
/>
<path
fill="#A2ADC0"
d="M9 18c-4.963 0-9-4.037-9-9s4.037-9 9-9 9 4.037 9 9-4.037 9-9 9zM9 .75C4.451.75.75 4.451.75 9c0 4.549 3.701 8.25 8.25 8.25 4.549 0 8.25-3.701 8.25-8.25C17.25 4.451 13.549.75 9 .75z"
/>
<circle cx="11.646" cy="6" r="1" fill="#A2ADC0" />
<circle cx="6.646" cy="6" r="1" fill="#A2ADC0" />
</svg>
);
};
Expand Down
21 changes: 21 additions & 0 deletions src/assets/icons/SendArrow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React, { FunctionComponent } from 'react';

const SendArrowIcon: FunctionComponent = () => {
return (
<svg
width="10"
height="10"
viewBox="0 0 10 10"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M2.38396 1.47595H8.55196M8.55196 1.47595V7.64395M8.55196 1.47595L1.35596 8.67195"
stroke="white"
strokeWidth="1.028"
/>
</svg>
);
};

export default SendArrowIcon;
2 changes: 1 addition & 1 deletion src/code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ main().then(({ roomName, secret, history, instanceId }) => {
sendNotifications = isMinimized;

// resize window
figma.ui.resize(message.payload ? 180 : 333, message.payload ? 1 : 490);
figma.ui.resize(message.payload ? 180 : 333, message.payload ? 108 : 490);
break;
case 'focus':
if (!isMinimized) {
Expand Down
39 changes: 17 additions & 22 deletions src/components/Chatbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import { observer } from 'mobx-react';
import React, { useEffect, useRef, useState, FunctionComponent } from 'react';
import { useRouteMatch } from 'react-router-dom';
import styled, { css } from 'styled-components';
import EmojiIcon from '../assets/icons/Emoji';
import SendArrowIcon from '../assets/icons/SendArrow';
import { ConnectionEnum } from '../shared/interfaces';
import { useStore } from '../store';
import Tooltip from './Tooltip';
import EmojiIcon from '../assets/icons/Emoji';

interface ChatProps {
sendMessage: (event: any) => void;
Expand All @@ -19,6 +20,7 @@ interface ChatProps {
const ChatBar: FunctionComponent<ChatProps> = (props) => {
const store = useStore();
const isSettings = useRouteMatch('/settings');
const emojiPickerRef = useRef(null);
const [hasSelection, setHasSelection] = useState(false);
const [isFailed, setIsFailed] = useState(
store.status === ConnectionEnum.ERROR
Expand All @@ -39,8 +41,8 @@ const ChatBar: FunctionComponent<ChatProps> = (props) => {
[]
);

const sendMessage = (message: string) => {
props.sendMessage(message);
const sendMessage = (e) => {
props.sendMessage(e);
chatTextInput.current.value = '';
};

Expand Down Expand Up @@ -74,37 +76,30 @@ const ChatBar: FunctionComponent<ChatProps> = (props) => {
/>

<Tooltip
ref={emojiPickerRef}
handler={React.forwardRef((p, ref) => (
<EmojiPickerStyled {...p} ref={ref}>
<svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
fill="none"
>
<path
fill="#A2ADC0"
d="M9.153 13.958a5.532 5.532 0 01-5.122-3.41.394.394 0 01.585-.482.396.396 0 01.146.178 4.741 4.741 0 004.391 2.923c.625 0 1.244-.124 1.82-.365a4.72 4.72 0 002.558-2.558.396.396 0 01.73.305 5.51 5.51 0 01-2.984 2.983 5.499 5.499 0 01-2.124.426z"
/>
<path
fill="#A2ADC0"
d="M9 18c-4.963 0-9-4.037-9-9s4.037-9 9-9 9 4.037 9 9-4.037 9-9 9zM9 .75C4.451.75.75 4.451.75 9c0 4.549 3.701 8.25 8.25 8.25 4.549 0 8.25-3.701 8.25-8.25C17.25 4.451 13.549.75 9 .75z"
/>
<circle cx="11.646" cy="6" r="1" fill="#A2ADC0" />
<circle cx="6.646" cy="6" r="1" fill="#A2ADC0" />
</svg>
<EmojiIcon />
</EmojiPickerStyled>
))}
>
<EmojiList>
{['😂', '😊', '👍', '🙈', '🔥', '🤔', '💩'].map((emoji) => (
<span key={emoji} data-emoji={emoji} />
<span
key={emoji}
data-emoji={emoji}
onClick={(e) => {
props.setTextMessage(emoji);
sendMessage(e);
emojiPickerRef.current.hide();
}}
/>
))}
</EmojiList>
</Tooltip>

<SendButton color={store.settings.color} onClick={sendMessage}>
<EmojiIcon />
<SendArrowIcon />
</SendButton>
</ChatInput>
</ChatInputWrapper>
Expand Down
45 changes: 38 additions & 7 deletions src/components/Message.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Linkify from 'linkifyjs/react';
import { toJS } from 'mobx';
import React, { FunctionComponent } from 'react';
import Linkify from 'linkifyjs/react';
import TimeAgo from 'react-timeago';
import buildFormatter from 'react-timeago/lib/formatters/buildFormatter';
import nowStrings from 'react-timeago/lib/language-strings/en';
Expand All @@ -10,6 +10,15 @@ import { sendMainMessage } from '../shared/utils';

const formatter = buildFormatter(nowStrings);

function isOnlyEmoji(str: string) {
return (
str.replace(
/(?:[\u2700-\u27bf]|(?:\ud83c[\udde6-\uddff]){2}|[\ud800-\udbff][\udc00-\udfff]|[\u0023-\u0039]\ufe0f?\u20e3|\u3299|\u3297|\u303d|\u3030|\u24c2|\ud83c[\udd70-\udd71]|\ud83c[\udd7e-\udd7f]|\ud83c\udd8e|\ud83c[\udd91-\udd9a]|\ud83c[\udde6-\uddff]|\ud83c[\ude01-\ude02]|\ud83c\ude1a|\ud83c\ude2f|\ud83c[\ude32-\ude3a]|\ud83c[\ude50-\ude51]|\u203c|\u2049|[\u25aa-\u25ab]|\u25b6|\u25c0|[\u25fb-\u25fe]|\u00a9|\u00ae|\u2122|\u2139|\ud83c\udc04|[\u2600-\u26FF]|\u2b05|\u2b06|\u2b07|\u2b1b|\u2b1c|\u2b50|\u2b55|\u231a|\u231b|\u2328|\u23cf|[\u23e9-\u23f3]|[\u23f8-\u23fa]|\ud83c\udccf|\u2934|\u2935|[\u2190-\u21ff])/g,
''
) === ''
);
}

interface Props {
data: any;
instanceId: any;
Expand All @@ -25,10 +34,17 @@ const Message: FunctionComponent<Props> = ({ data, instanceId }) => {

const selectionCount = selection?.length || selection?.nodes?.length || 0;

const text = data.message.text;
const isSingleEmoji = !selectionCount && isOnlyEmoji(text);

return (
<MessageFlex isSelf={isSelf}>
<MessageWrapper className="message" isSelf={isSelf}>
<MessageContainer className={`${isSelf ? 'me' : colorClass}`}>
<MessageContainer
className={`${isSelf ? 'me' : colorClass} ${
isSingleEmoji && 'emoji'
}`}
>
{!isSelf && username && (
<MessageHeader>
<div className="user">
Expand Down Expand Up @@ -57,14 +73,14 @@ const Message: FunctionComponent<Props> = ({ data, instanceId }) => {
sendMainMessage('focus-nodes', selectionData);
}}
>
{data.message.text && (
{text && (
<Linkify
tagName="div"
options={{
defaultProtocol: 'https',
}}
>
{data.message.text}
{text}
</Linkify>
)}
<button className="selection button button--secondary">
Expand All @@ -75,12 +91,12 @@ const Message: FunctionComponent<Props> = ({ data, instanceId }) => {
</span>
) : (
<Linkify
tagName="span"
tagName="div"
options={{
defaultProtocol: 'https',
}}
>
{data.message.text}
{text}
</Linkify>
)}
</MessageContainer>
Expand Down Expand Up @@ -135,7 +151,7 @@ const MessageContainer = styled.div`
font-weight: 500;
font-size: 12px;
line-height: 16px;
padding: 11px 16px 11px;
padding: 11px 16px;
word-break: break-word;
margin-bottom: 4px;
max-width: 240px;
Expand Down Expand Up @@ -170,6 +186,10 @@ const MessageContainer = styled.div`
opacity: 1;
}
}
&.emoji {
text-align: right;
}
}
.selection {
Expand Down Expand Up @@ -200,6 +220,17 @@ const MessageContainer = styled.div`
}
`
)}
&.emoji {
padding: 11px 0;
background-color: transparent;
font-size: 30px;
text-align: left;
header {
margin-bottom: 10px;
color: ${(p) => p.theme.fontColor};
}
}
`;

export default Message;
12 changes: 9 additions & 3 deletions src/components/Tooltip.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useRef, useState, FunctionComponent } from 'react';
import React, { useEffect, useImperativeHandle, useRef, useState } from 'react';
import { usePopper } from 'react-popper';
import styled from 'styled-components';

Expand All @@ -9,7 +9,7 @@ interface Props {
placement?: 'top' | 'bottom';
}

const TooltipComponent: FunctionComponent<Props> = (props) => {
const TooltipComponent = React.forwardRef<any, Props>((props, ref) => {
const [isOpen, setIsOpen] = useState(false);
const { handler: HandlerComp } = props;

Expand Down Expand Up @@ -43,6 +43,12 @@ const TooltipComponent: FunctionComponent<Props> = (props) => {
],
});

useImperativeHandle(ref, () => ({
hide() {
setIsOpen(false);
},
}));

useEffect(() => {
if (!props.hover) {
function handleClick(event) {
Expand Down Expand Up @@ -96,7 +102,7 @@ const TooltipComponent: FunctionComponent<Props> = (props) => {
)}
</div>
);
};
});

const TooltipContent = styled.div`
padding: 11px 17px;
Expand Down
26 changes: 21 additions & 5 deletions src/views/Minimized.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import React, { FunctionComponent } from 'react';
import { Redirect } from 'react-router-dom';
import styled from 'styled-components';
import Header from '../components/Header';
import Tooltip from '../components/Tooltip';
import { ConnectionEnum } from '../shared/interfaces';
import { useStore } from '../store';

Expand All @@ -20,9 +21,24 @@ const MinimizedView: FunctionComponent = () => {
)}
<Users>
{store.online.map((user) => (
<User key={user.id} className="user" color={user.color || '#000'}>
{user.avatar || user.name.substr(0, 2)}
</User>
<Tooltip
hover
key={user.id}
handler={observer(
React.forwardRef((_, ref) => (
<User
key={user.id}
ref={ref}
className="user"
color={user.color || '#000'}
>
{user.avatar || user.name.substr(0, 2)}
</User>
))
)}
>
{user.name}
</Tooltip>
))}
</Users>
</Minimized>
Expand All @@ -33,7 +49,7 @@ const MinimizedView: FunctionComponent = () => {
const Minimized = styled.div`
display: grid;
text-align: center;
min-height: calc(100vh - 33px);
min-height: calc(100vh - 37px);
max-width: 100vw;
font-size: 14px;
div {
Expand All @@ -49,7 +65,7 @@ const Users = styled.div`
display: grid;
grid-gap: 10px;
grid-template-columns: repeat(3, 1fr);
padding: 10px;
padding: 14px;
`;

const User = styled.div`
Expand Down
5 changes: 4 additions & 1 deletion src/views/Settings/components/AvatarPicker.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// store
import { observer } from 'mobx-react';
import React, { FunctionComponent } from 'react';
import React, { useRef, FunctionComponent } from 'react';
import styled from 'styled-components';
// components
import Tooltip from '../../../components/Tooltip';
Expand All @@ -14,9 +14,11 @@ interface SettingsProps {

const AvatarPickerComponent: FunctionComponent<SettingsProps> = (props) => {
const store = useStore();
const pickerRef = useRef(null);

return (
<Tooltip
ref={pickerRef}
placement="bottom"
handler={observer(
React.forwardRef((p, ref) => (
Expand All @@ -32,6 +34,7 @@ const AvatarPickerComponent: FunctionComponent<SettingsProps> = (props) => {
<div
key={emoji}
onClick={() => {
pickerRef.current.hide();
store.persistSettings(
{
avatar: emoji,
Expand Down
Loading

0 comments on commit 084f513

Please sign in to comment.