Skip to content

Commit

Permalink
refactor(web): add react-spring and fix bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
ph1p committed Jul 25, 2021
1 parent 4f340a2 commit 5d749b7
Show file tree
Hide file tree
Showing 10 changed files with 223 additions and 108 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"dependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-spring": "^9.2.4",
"styled-components": "^5.3.0",
"tsconfig-paths-webpack-plugin": "^3.5.1"
}
Expand Down
10 changes: 6 additions & 4 deletions packages/plugin/src/components/Message.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ import { sendMainMessage } from '../shared/utils';
const formatter = buildFormatter(nowStrings);

const isOnlyEmoji = (str: string) =>
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,
''
) === '';
str
? 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;
Expand Down
7 changes: 7 additions & 0 deletions packages/shared/src/utils/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const isOnlyEmoji = (str: string) =>
str
? 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,
''
) === ''
: '';
1 change: 0 additions & 1 deletion packages/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.3",
"react-timeago": "^6.2.1",
"react-transition-group": "^4.4.2",
"simple-encryptor": "^4.0.0",
"socket.io-client": "^4.1.3",
"web-vitals": "^2.1.0"
Expand Down
31 changes: 24 additions & 7 deletions packages/web/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,32 @@
import { Chat } from '@web/views/Chat';
import React from 'react';
import { Route, BrowserRouter as Router, Switch } from 'react-router-dom';
import styled from 'styled-components';

const Wrapper = styled.div`
height: 500px;
max-height: 100%;
max-width: 500px;
border-radius: 8px;
display: grid;
position: relative;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
box-shadow: 0 0 100px #eee;
background-color: ${(p) => p.theme.backgroundColor};
`;

export const App = () => {
return (
<Router>
<Switch>
<Route path="/">
<Chat />
</Route>
</Switch>
</Router>
<Wrapper>
<Router>
<Switch>
<Route path="/">
<Chat />
</Route>
</Switch>
</Router>
</Wrapper>
);
};
7 changes: 1 addition & 6 deletions packages/web/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,7 @@ import reportWebVitals from './reportWebVitals';
import { StoreProvider, trunk, useStore } from './store/RootStore';

const GlobalStyle = createGlobalStyle`
body {
background-color: ${(p) => p.theme.backgroundColor};
height: 100%;
}
#root, html {
#root, body, html {
height: 100%;
}
Expand Down
1 change: 0 additions & 1 deletion packages/web/src/store/RootStore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
} from '@shared/utils/interfaces';
import { darkTheme, lightTheme } from '@shared/utils/theme';


class RootStore {
url = 'https://figma-chat.ph1p.dev';
room = '1627070280662-EOq7RFhIyf7SIjv';
Expand Down
38 changes: 15 additions & 23 deletions packages/web/src/views/Chat/components/ChatView.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { observer } from 'mobx-react-lite';
import React, { FunctionComponent, useState } from 'react';
import { CSSTransition, TransitionGroup } from 'react-transition-group';
import styled from 'styled-components';

import { MAX_MESSAGES } from '@shared/utils/constants';
Expand All @@ -18,7 +17,7 @@ interface Props {

const ChatView: FunctionComponent<Props> = (props) => {
const store = useStore();
const nodeRef = React.useRef(null);
// const nodeRef = React.useRef(null);
const [containerIsHidden, setContainerIsHidden] = useState(false);

const showMessageSeperator = (messageIndex = 0) => {
Expand All @@ -35,29 +34,20 @@ const ChatView: FunctionComponent<Props> = (props) => {
isBottom={props.isBottom}
onWheel={props.onWheel}
>
<TransitionGroup>
<div>
{props.chatState.filteredMessages.map((m: MessageData, i: number) => (
<CSSTransition
key={m.message?.date || i}
nodeRef={nodeRef}
timeout={400}
classNames={`message-${
m.id === store.instanceId ? 'self' : 'other'
}`}
>
<>
<Message data={m} ref={nodeRef} instanceId={store.instanceId} />
{showMessageSeperator(i) ? (
<MessageSeperator>
<span>older messages</span>
</MessageSeperator>
) : (
''
)}
</>
</CSSTransition>
<React.Fragment key={i}>
<Message data={m} />
{showMessageSeperator(i) ? (
<MessageSeperator>
<span>older messages</span>
</MessageSeperator>
) : (
''
)}
</React.Fragment>
))}
</TransitionGroup>
</div>
</Messages>
);
};
Expand Down Expand Up @@ -89,6 +79,8 @@ const Messages = styled.div<{
overflow-x: hidden;
overflow-y: auto;
display: grid;
height: calc(500px - 60px);
max-height: 100%;
&::after {
content: '';
transition: opacity 0.3s;
Expand Down
138 changes: 72 additions & 66 deletions packages/web/src/views/Chat/components/Message.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { useStore } from '@web/store/RootStore';
import Linkify from 'linkifyjs/react';
import { toJS } from 'mobx';
import React, { FunctionComponent } from 'react';
import { observer } from 'mobx-react-lite';
import React, { FunctionComponent, useEffect, useState } from 'react';
import { animated, useSpring } from 'react-spring';
import TimeAgo from 'react-timeago';
// @ts-ignore
import buildFormatter from 'react-timeago/lib/formatters/buildFormatter';
Expand All @@ -10,32 +13,22 @@ import styled, { css } from 'styled-components';

import HashIcon from '@shared/assets/icons/HashIcon';
import { EColors } from '@shared/utils/constants';
import { isOnlyEmoji } from '@shared/utils/helpers';
import { MessageData } from '@shared/utils/interfaces';


const formatter = buildFormatter(nowStrings);

const isOnlyEmoji = (str: string) =>
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: MessageData;
instanceId: any;
ref: any;
}

const Message: FunctionComponent<Props> = React.forwardRef<
HTMLDivElement,
Props
>(({ data, instanceId }, ref) => {
const Message: FunctionComponent<Props> = observer(({ data }) => {
const store = useStore();
const username = data.user.name || '';
const avatar = data.user.avatar || '';
const colorClass = EColors[data.user.color] || 'blue';
const selection = toJS(data?.message?.selection);
const isSelf = data.id === instanceId;
const isSelf = data.id === store.instanceId;
const pageName = selection?.page?.name || '';

const selectionCount = (selection?.nodes && selection?.nodes?.length) || 0;
Expand All @@ -44,57 +37,70 @@ const Message: FunctionComponent<Props> = React.forwardRef<
const date = data?.message?.date || null;
const isSingleEmoji = !selectionCount && isOnlyEmoji(text);

const [toggle, setToggle] = useState(false);

useEffect(() => {
setToggle(true);
}, []);

const styles = useSpring({
translateX: toggle ? 0 : isSelf ? 60 : -60,
opacity: toggle ? 1 : 0,
});

return (
<MessageFlex isSelf={isSelf} ref={ref}>
<MessageWrapper className="message" isSelf={isSelf}>
<MessageContainer
className={`${isSelf ? 'me' : colorClass} ${
isSingleEmoji && 'emoji'
}`}
>
{!isSelf && username && (
<MessageHeader>
<div className="user">
{avatar && avatar + ' '}
{username}
</div>
</MessageHeader>
)}
{selection ? (
<span onClick={() => {}}>
{text && (
<Linkify
tagName="div"
options={{
defaultProtocol: 'https',
}}
>
{text}
</Linkify>
)}
<SelectionButton isSelf={isSelf}>
<HashIcon />
{pageName ? pageName + ' - ' : ''}
focus {selectionCount} element
{selectionCount > 1 ? 's' : ''}
</SelectionButton>
</span>
) : (
<Linkify
tagName="div"
options={{
defaultProtocol: 'https',
}}
>
{text}
</Linkify>
)}
</MessageContainer>
<MessageDate>
{date && <TimeAgo date={date} formatter={formatter} />}
</MessageDate>
</MessageWrapper>
</MessageFlex>
<animated.div style={styles}>
<MessageFlex style={styles} isSelf={isSelf}>
<MessageWrapper className="message" isSelf={isSelf}>
<MessageContainer
className={`${isSelf ? 'me' : colorClass} ${
isSingleEmoji && 'emoji'
}`}
>
{!isSelf && username && (
<MessageHeader>
<div className="user">
{avatar && avatar + ' '}
{username}
</div>
</MessageHeader>
)}
{selection ? (
<span onClick={() => {}}>
{text && (
<Linkify
tagName="div"
options={{
defaultProtocol: 'https',
}}
>
{text}
</Linkify>
)}
<SelectionButton isSelf={isSelf}>
<HashIcon />
{pageName ? pageName + ' - ' : ''}
focus {selectionCount} element
{selectionCount > 1 ? 's' : ''}
</SelectionButton>
</span>
) : (
<Linkify
tagName="div"
options={{
defaultProtocol: 'https',
}}
>
{text}
</Linkify>
)}
</MessageContainer>
<MessageDate>
{date && <TimeAgo date={date} formatter={formatter} />}
</MessageDate>
</MessageWrapper>
</MessageFlex>
</animated.div>
);
});

Expand All @@ -116,7 +122,7 @@ const MessageDate = styled.div`
font-weight: 600;
`;

const MessageFlex = styled.div<{ isSelf: boolean }>`
const MessageFlex = styled.div<{ isSelf: boolean; style: any }>`
display: flex;
justify-content: flex-end;
flex-direction: ${({ isSelf }) => (isSelf ? 'row' : 'row-reverse')};
Expand Down
Loading

0 comments on commit 5d749b7

Please sign in to comment.