1
1
import React , { useState , useEffect , useRef } from 'react' ;
2
- import styled from 'styled-components' ;
2
+ import styled , { css as c } from 'styled-components' ;
3
3
import { sortBy , takeRight } from 'lodash-es' ;
4
4
5
- import AutosizeTextArea from '@codesandbox/common/lib/components/AutosizeTextArea' ;
6
5
import { ENTER } from '@codesandbox/common/lib/utils/keycodes' ;
7
6
import { useOvermind } from 'app/overmind' ;
7
+ import css from '@styled-system/css' ;
8
+ import {
9
+ Collapsible ,
10
+ Text ,
11
+ Stack ,
12
+ Textarea ,
13
+ Element ,
14
+ } from '@codesandbox/components' ;
8
15
9
- const Container = styled . div `
16
+ const Container = styled ( Stack ) `
10
17
min-height: 200px;
11
18
max-height: 300px;
12
- padding: 0 1rem;
13
- color: white;
14
- font-size: 0.875rem;
15
- display: flex;
16
- flex-direction: column;
19
+ padding: 0 ${ props => props . theme . sizes [ 2 ] } px;
17
20
overflow-y: auto;
18
21
` ;
19
22
@@ -22,9 +25,17 @@ const Messages = styled.div`
22
25
flex: 1;
23
26
` ;
24
27
28
+ const Avatar = styled . img `
29
+ ${ ( { theme, color } ) => c `
30
+ border-radius: ${ theme . radii . medium } px;
31
+ border: 1px solid ${ color } ;
32
+ width: ${ theme . sizes [ 8 ] } px;
33
+ height: ${ theme . sizes [ 8 ] } px;
34
+ ` }
35
+ ` ;
36
+
25
37
export const Chat : React . FC = ( ) => {
26
38
const [ value , setValue ] = useState ( '' ) ;
27
- const [ height , setHeight ] = useState < number > ( null ) ;
28
39
const { state, actions } = useOvermind ( ) ;
29
40
const messagesRef = useRef ( null ) ;
30
41
const scrollDown = ( ) => {
@@ -35,7 +46,7 @@ export const Chat: React.FC = () => {
35
46
useEffect ( scrollDown ) ;
36
47
37
48
const handleKeyDown = ( e : React . KeyboardEvent ) => {
38
- if ( e . keyCode === ENTER && ! e . shiftKey ) {
49
+ if ( e . keyCode === ENTER && ! e . shiftKey && value . trim ( ) !== '' ) {
39
50
e . preventDefault ( ) ;
40
51
e . stopPropagation ( ) ;
41
52
// Enter
@@ -54,76 +65,96 @@ export const Chat: React.FC = () => {
54
65
} ;
55
66
56
67
const { messages, users } = state . live . roomInfo . chat ;
57
- const currentUserId = state . live . liveUserId ;
58
68
const roomInfoUsers = state . live . roomInfo . users ;
59
69
70
+ const orderedMessages : ( m : any ) => any [ ] = m =>
71
+ sortBy ( takeRight ( m , 100 ) , 'date' ) ;
72
+
73
+ const messageData = message => {
74
+ const metadata = roomInfoUsers . find ( u => u . id === message . userId ) ;
75
+ return {
76
+ metadata,
77
+ color : metadata
78
+ ? `rgb(${ metadata . color [ 0 ] } , ${ metadata . color [ 1 ] } , ${ metadata . color [ 2 ] } )`
79
+ : '#636363' ,
80
+ name : users [ message . userId ] ,
81
+ } ;
82
+ } ;
83
+
84
+ const isNotSameUser = ( message , i ) =>
85
+ i === 0 || messages [ i - 1 ] . userId !== message . userId ;
86
+
87
+ const isLight = theme => theme . vscodeTheme . type === 'light' ;
88
+
60
89
return (
61
- < Container ref = { messagesRef } >
62
- < Messages >
63
- { messages . length > 0 ? (
64
- sortBy ( takeRight ( messages , 100 ) , 'date' ) . map ( ( message , i ) => {
65
- const metadata = roomInfoUsers . find ( u => u . id === message . userId ) ;
66
- const color = metadata
67
- ? `rgb(${ metadata . color [ 0 ] } , ${ metadata . color [ 1 ] } , ${ metadata . color [ 2 ] } )`
68
- : '#636363' ;
69
- const name = users [ message . userId ] ;
70
- return (
71
- < div key = { message . date } >
72
- { ( i === 0 || messages [ i - 1 ] . userId !== message . userId ) && (
73
- < div
90
+ < Collapsible
91
+ css = { css ( theme => ( {
92
+ borderTop : '1px solid' ,
93
+ borderColor : 'sideBar.border' ,
94
+ boxShadow : isLight ( theme )
95
+ ? '0px -8px 8px rgba(255,255,255,0.24), 0px -4px 8px rgba(255,255,255,0.4)'
96
+ : '0px -8px 8px rgba(0,0,0,0.24), 0px -4px 8px rgba(0,0,0,0.4)' ,
97
+ } ) ) }
98
+ defaultOpen
99
+ title = "Chat"
100
+ >
101
+ < Container direction = "vertical" ref = { messagesRef } >
102
+ < Messages >
103
+ { messages . length > 0 ? (
104
+ orderedMessages ( messages ) . map ( ( message , i ) => {
105
+ const { color, name, metadata } = messageData ( message ) ;
106
+ return (
107
+ < Element key = { message . date } >
108
+ { isNotSameUser ( message , i ) && (
109
+ < Stack
110
+ paddingTop = { 2 }
111
+ marginBottom = { 2 }
112
+ align = "center"
113
+ gap = { 2 }
114
+ >
115
+ < Avatar
116
+ color = { color }
117
+ alt = { metadata . username }
118
+ src = { metadata . avatarUrl }
119
+ />
120
+ < Text block weight = "bold" >
121
+ { name }
122
+ </ Text >
123
+ </ Stack >
124
+ ) }
125
+ < Text
126
+ block
74
127
style = { {
75
- color,
76
- fontWeight : 600 ,
77
- marginBottom : '0.25rem' ,
78
- marginTop : '0.5rem' ,
128
+ wordBreak : 'break-word' ,
79
129
} }
130
+ marginBottom = { 2 }
80
131
>
81
- { name }
82
- { currentUserId === message . userId && ' (you)' }
83
- { ! metadata && ' (left)' }
84
- </ div >
85
- ) }
86
- < div
87
- style = { {
88
- color : 'rgba(255, 255, 255, 0.7)' ,
89
- fontWeight : 400 ,
90
- marginBottom : '.25rem' ,
91
- } }
92
- >
93
- { message . message . split ( '\n' ) . map ( m => (
94
- < span key = { m } >
95
- { m }
96
- < br />
97
- </ span >
98
- ) ) }
99
- </ div >
100
- </ div >
101
- ) ;
102
- } )
103
- ) : (
104
- < div
105
- style = { {
106
- fontStyle : 'italic' ,
107
- color : 'rgba(255, 255, 255, 0.5)' ,
108
- } }
109
- >
110
- No messages, start sending some!
111
- </ div >
112
- ) }
113
- </ Messages >
114
- < AutosizeTextArea
115
- useCacheForDOMMeasurements
116
- value = { value }
117
- onChange = { handleChange }
118
- placeholder = "Send a message..."
119
- style = { {
120
- width : '100%' ,
121
- minHeight : height ,
122
- marginTop : '0.5rem' ,
123
- } }
124
- onKeyDown = { handleKeyDown }
125
- onHeightChange = { setHeight }
126
- />
127
- </ Container >
132
+ { message . message . split ( '\n' ) . map ( m => (
133
+ < span key = { m } >
134
+ { m }
135
+ < br />
136
+ </ span >
137
+ ) ) }
138
+ </ Text >
139
+ </ Element >
140
+ ) ;
141
+ } )
142
+ ) : (
143
+ < Text variant = "muted" > No messages, start sending some!</ Text >
144
+ ) }
145
+ </ Messages >
146
+ < Element marginTop = { 4 } >
147
+ < Textarea
148
+ autosize
149
+ style = { { height : 'auto' , minHeight : 'auto' } }
150
+ rows = { 1 }
151
+ value = { value }
152
+ onChange = { handleChange }
153
+ placeholder = "Send a message..."
154
+ onKeyDown = { handleKeyDown }
155
+ />
156
+ </ Element >
157
+ </ Container >
158
+ </ Collapsible >
128
159
) ;
129
160
} ;
0 commit comments