Skip to content

Commit 4abee19

Browse files
committed
feat(ui): update animations, remove full size connection view
1 parent 977b877 commit 4abee19

File tree

8 files changed

+299
-227
lines changed

8 files changed

+299
-227
lines changed

src/Ui.tsx

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import { state, view } from './shared/state';
2121
import { sendMainMessage } from './shared/utils';
2222
// views
2323
import ChatView from './views/Chat';
24-
import ConnectionView from './views/Connection';
2524
import MinimizedView from './views/Minimized';
2625
import UserListView from './views/UserList';
2726

@@ -43,17 +42,28 @@ onmessage = (message) => {
4342
const GlobalStyle = createGlobalStyle`
4443
body {
4544
overflow: hidden;
45+
text-shadow: 1px 1px 1px rgba(0,0,0,0.004);
46+
text-rendering: optimizeLegibility !important;
47+
-webkit-font-smoothing: antialiased !important;
4648
}
4749
`;
4850

4951
const AppWrapper = styled.div`
5052
overflow: hidden;
5153
`;
5254

55+
let socket: SocketIOClient.Socket;
56+
5357
const init = (serverUrl) => {
5458
state.url = serverUrl;
59+
state.status = ConnectionEnum.NONE;
60+
61+
if(socket) {
62+
socket.removeAllListeners();
63+
socket.disconnect();
64+
}
5565

56-
const socket: SocketIOClient.Socket = io(serverUrl, {
66+
socket = io(serverUrl, {
5767
reconnectionAttempts: 3,
5868
forceNew: true,
5969
transports: ['websocket'],
@@ -121,17 +131,11 @@ const init = (serverUrl) => {
121131
<Route exact path="/minimized">
122132
<MinimizedView />
123133
</Route>
124-
<Route exact path="/connecting">
125-
<ConnectionView retry={init} text="connecting..." />
126-
</Route>
127-
<Route exact path="/connection-error">
128-
<ConnectionView retry={init} text="connection error :( " />
129-
</Route>
130134
<Route path="/user-list">
131135
<UserListView />
132136
</Route>
133137
<Route path="/">
134-
<ChatView retry={init} />
138+
<ChatView init={init} />
135139
</Route>
136140
</Switch>
137141
</Router>

src/components/Chatbar.tsx

Lines changed: 97 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
import React, { FunctionComponent, useState, useEffect, useRef } from 'react';
22
import { useRouteMatch } from 'react-router-dom';
33
import styled from 'styled-components';
4-
import { state } from '../shared/state';
4+
import { state, view } from '../shared/state';
55
import { sendMainMessage } from '../shared/utils';
6-
import { SharedIcon } from '../shared/style';
6+
import ColorPicker from './ColorPicker';
7+
import { ConnectionEnum } from '../shared/interfaces';
78

89
interface ChatProps {
10+
socket: SocketIOClient.Socket;
911
sendMessage: (event: any) => void;
1012
setTextMessage: (text: string) => void;
1113
textMessage: string;
1214
setSelectionIsChecked: (event: any) => void;
1315
selectionIsChecked: boolean;
16+
init?: (url: string) => void;
1417
}
1518

1619
const ChatBar: FunctionComponent<ChatProps> = (props) => {
@@ -20,6 +23,9 @@ const ChatBar: FunctionComponent<ChatProps> = (props) => {
2023
const [show, setShow] = useState(hasSelection);
2124
const chatTextInput = useRef(null);
2225

26+
const isFailed = state.status === ConnectionEnum.ERROR;
27+
const isConnected = state.status === ConnectionEnum.CONNECTED;
28+
2329
useEffect(() => {
2430
if (hasSelection) {
2531
setShow(true);
@@ -40,7 +46,19 @@ const ChatBar: FunctionComponent<ChatProps> = (props) => {
4046
chatTextInput.current.value = '';
4147
}}
4248
>
43-
{false ? (
49+
{!isConnected && (
50+
<ConnectionInfo>
51+
{isFailed ? (
52+
<>
53+
connection failed{' '}
54+
<span onClick={() => props.init(state.url)}>retry</span>
55+
</>
56+
) : (
57+
'connecting...'
58+
)}
59+
</ConnectionInfo>
60+
)}
61+
{/* {false ? (
4462
<SelectionInfo
4563
hasSelection={hasSelection}
4664
onAnimationEnd={onAnimationEnd}
@@ -74,54 +92,96 @@ const ChatBar: FunctionComponent<ChatProps> = (props) => {
7492
</SelectionInfo>
7593
) : (
7694
''
77-
)}
78-
<ChatInput hasSelection={hasSelection}>
79-
<BellIcon>
80-
<svg
81-
xmlns="http://www.w3.org/2000/svg"
82-
fill="none"
83-
viewBox="0 0 15 16"
84-
>
85-
<path
86-
fill="#5751FF"
87-
fillRule="evenodd"
88-
d="M11 5v4l1 1H2l1-1V5a4 4 0 018 0zm1 4a2 2 0 002 2H0a2 2 0 002-2V5a5 5 0 0110 0v4zm-5 5l-2-2H4a3 3 0 106 0H9l-2 2z"
89-
clipRule="evenodd"
95+
)} */}
96+
{isConnected && (
97+
<>
98+
<ChatInput hasSelection={hasSelection}>
99+
<BellIcon
100+
onClick={() =>
101+
(state.settings.enableNotificationSound = !state.settings
102+
.enableNotificationSound)
103+
}
104+
>
105+
<svg
106+
xmlns="http://www.w3.org/2000/svg"
107+
fill="none"
108+
viewBox="0 0 15 16"
109+
>
110+
{state.settings.enableNotificationSound ? (
111+
<path
112+
fill={state.settings.color}
113+
fillRule="evenodd"
114+
d="M11 5v4l1 1H2l1-1V5a4 4 0 018 0zm1 4a2 2 0 002 2H0a2 2 0 002-2V5a5 5 0 0110 0v4zm-5 5l-2-2H4a3 3 0 106 0H9l-2 2z"
115+
clipRule="evenodd"
116+
/>
117+
) : (
118+
<path
119+
fill={state.settings.color}
120+
fillRule="evenodd"
121+
d="M4.998 11.472h9.475v-.882a1.76 1.76 0 01-1.764-1.765v-3.53a5.31 5.31 0 00-.134-1.188l-.88.854c.01.11.014.222.014.334v3.53c0 .617.202 1.187.544 1.647H6.027l-1.029 1zm5.718-8.924a4.295 4.295 0 00-7.597 2.747v3.53c0 .604-.194 1.162-.522 1.617l-1.06 1.03H.354v-.882a1.76 1.76 0 001.765-1.765v-3.53a5.295 5.295 0 019.315-3.445l-.718.698zm-5.009 9.807a1.706 1.706 0 103.413 0h1a2.706 2.706 0 11-5.413 0h1zM0 14.146l14-14 .707.708-14 14L0 14.146z"
122+
clipRule="evenodd"
123+
/>
124+
)}
125+
</svg>
126+
</BellIcon>
127+
<input
128+
ref={chatTextInput}
129+
type="input"
130+
onChange={({ target }: any) =>
131+
props.setTextMessage(target.value.substr(0, 1000))
132+
}
133+
placeholder={`Write something ... ${
134+
props.selectionIsChecked ? '(optional)' : ''
135+
}`}
90136
/>
91-
</svg>
92-
</BellIcon>
93-
<input
94-
ref={chatTextInput}
95-
type="input"
96-
onChange={({ target }: any) =>
97-
props.setTextMessage(target.value.substr(0, 1000))
98-
}
99-
placeholder={`Write something ... ${
100-
props.selectionIsChecked ? '(optional)' : ''
101-
}`}
102-
/>
103-
</ChatInput>
104-
<SelectionCheckbox hasSelection={hasSelection}>lol</SelectionCheckbox>
137+
<ColorPicker socket={props.socket} />
138+
</ChatInput>
139+
<SelectionCheckbox hasSelection={hasSelection}>lol</SelectionCheckbox>
140+
</>
141+
)}
105142
</ChatBarForm>
106143
);
107144
};
108145

146+
const ConnectionInfo = styled.div`
147+
display: flex;
148+
justify-content: center;
149+
align-items: center;
150+
position: absolute;
151+
left: 0;
152+
top: 0;
153+
width: 100%;
154+
z-index: 6;
155+
bottom: -5px;
156+
text-align: center;
157+
color: #fff;
158+
font-weight: bold;
159+
span {
160+
text-decoration: underline;
161+
cursor: pointer;
162+
margin-left: 5px;
163+
}
164+
`;
165+
109166
const ChatBarForm = styled.form`
110167
position: absolute;
168+
z-index: 3;
169+
height: 37px;
170+
padding-top: 7px;
111171
right: 14px;
112172
left: 14px;
113173
bottom: 7px;
114174
margin: 0;
115-
transition: transform 0.3s;
175+
transition: transform 0.5s;
116176
transform: translateY(${({ isSettings }) => (isSettings ? 50 : 0)}px);
117177
`;
118178

119179
const SelectionCheckbox = styled.div`
120180
position: absolute;
121181
right: 0;
122182
top: 0;
123-
animation-delay: 0.4s;
124-
transition: all 0.4s;
183+
animation-delay: 0.5s;
184+
transition: all 0.5s;
125185
opacity: ${(p) => (p.hasSelection ? 1 : 0)};
126186
color: #fff;
127187
margin: 7px 14px;
@@ -133,7 +193,7 @@ const BellIcon = styled.div`
133193
position: absolute;
134194
z-index: 3;
135195
left: 10px;
136-
top: 15px;
196+
top: 8px;
137197
svg {
138198
width: 15px;
139199
height: 16px;
@@ -187,18 +247,17 @@ const ChatInput = styled.div`
187247
margin: 0;
188248
position: relative;
189249
z-index: 3;
250+
transition: width 0.3s;
251+
width: ${(p) => (p.hasSelection ? '200px' : '100%')};
190252
input {
191253
position: relative;
192254
z-index: 2;
193255
border-radius: 6px;
194256
width: 100%;
195257
border: 0;
196258
padding: 10px 14px 10px 30px;
197-
margin: 7px 0 0;
198259
height: 30px;
199260
outline: none;
200-
transition: width 0.4s;
201-
width: ${(p) => (p.hasSelection ? '200px' : '100%')};
202261
}
203262
button {
204263
border: 0;
@@ -217,4 +276,4 @@ const ChatInput = styled.div`
217276
}
218277
`;
219278

220-
export default ChatBar;
279+
export default view(ChatBar);

src/components/ColorPicker.tsx

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import React, { useState, useEffect, useRef, FunctionComponent } from 'react';
2+
import styled from 'styled-components';
3+
4+
import { state, view } from '../shared/state';
5+
import { colors } from '../shared/constants';
6+
7+
interface Props {
8+
socket?: SocketIOClient.Socket;
9+
}
10+
11+
const ColorPicker: FunctionComponent<Props> = view((props) => {
12+
const [isOpen, setIsOpen] = useState(false);
13+
const wrapperRef = useRef(null);
14+
15+
useEffect(() => {
16+
function handleClick(event) {
17+
if (wrapperRef.current && !wrapperRef.current.contains(event.target)) {
18+
setIsOpen(false);
19+
}
20+
}
21+
22+
document.addEventListener('mousedown', handleClick);
23+
return () => document.removeEventListener('mousedown', handleClick);
24+
}, [wrapperRef]);
25+
26+
return (
27+
<Wrapper ref={wrapperRef}>
28+
<CurrentColor
29+
color={state.settings.color}
30+
onClick={() => setIsOpen(!isOpen)}
31+
/>
32+
33+
<Picker isOpen={isOpen}>
34+
{Object.keys(colors).map((color) => (
35+
<div
36+
key={color}
37+
onClick={() =>
38+
state.persistSettings(
39+
{
40+
color,
41+
},
42+
props.socket
43+
)
44+
}
45+
className={`color ${state.settings.color === color && ' active'}`}
46+
style={{ backgroundColor: color }}
47+
/>
48+
))}
49+
</Picker>
50+
</Wrapper>
51+
);
52+
});
53+
54+
const Wrapper = styled.div`
55+
position: absolute;
56+
right: 20px;
57+
top: 10px;
58+
z-index: 4;
59+
`;
60+
61+
const CurrentColor = styled.div`
62+
position: absolute;
63+
width: 12px;
64+
height: 12px;
65+
border-radius: 100%;
66+
background-color: ${({ color }) => color};
67+
cursor: pointer;
68+
`;
69+
70+
const Picker = styled.div`
71+
position: absolute;
72+
pointer-events: ${({ isOpen }) => (isOpen ? 'all' : 'none')};
73+
opacity: ${({ isOpen }) => (isOpen ? 1 : 0)};
74+
background-color: #000;
75+
left: -240px;
76+
top: -51px;
77+
border-radius: 8px;
78+
display: flex;
79+
padding: 10px;
80+
width: 265px;
81+
justify-content: space-between;
82+
&::after {
83+
top: 100%;
84+
right: 11px;
85+
border: solid transparent;
86+
content: ' ';
87+
height: 0;
88+
width: 0;
89+
position: absolute;
90+
pointer-events: none;
91+
border-color: transparent;
92+
border-top-color: #000;
93+
border-width: 8px;
94+
margin-left: -8px;
95+
}
96+
.color {
97+
position: relative;
98+
width: 18px;
99+
height: 18px;
100+
border-radius: 100%;
101+
cursor: pointer;
102+
&:hover::after {
103+
background-color: #fff;
104+
}
105+
&.active::after {
106+
background-color: #fff;
107+
}
108+
&::after {
109+
content: '';
110+
position: absolute;
111+
width: 7px;
112+
height: 7px;
113+
top: 50%;
114+
left: 50%;
115+
background-color: transparent;
116+
border-radius: 100%;
117+
transform: translate(-50%, -50%);
118+
}
119+
}
120+
`;
121+
122+
export default ColorPicker;

0 commit comments

Comments
 (0)