Skip to content

Commit fc972a3

Browse files
committed
remove chat list, add hover and minor fixes
1 parent 2d3e650 commit fc972a3

31 files changed

+509
-436
lines changed

services/app/apps/codebattle/.eslintrc.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ extends:
1919
rules:
2020
no-console: 0
2121
react/prop-types: 0
22+
react/no-unused-prop-types: 0
2223
import/no-unresolved: 0
2324
import/no-extraneous-dependencies:
2425
- 2

services/app/apps/codebattle/assets/js/app.js

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ import {
4848
renderUserPage,
4949
renderUsersRating,
5050
} from './widgets';
51-
import renderExtensionPopup from './widgets/components/ExtensionPopup';
5251

5352
if (process.env.NODE_ENV === 'development') {
5453
inspect({
@@ -91,7 +90,6 @@ window.addEventListener('phx:page-loading-stop', _info => NProgress.done());
9190
liveSocket.connect();
9291

9392
const builderWidgetRoot = document.getElementById('builder-widget-root');
94-
const extension = document.getElementById('extension');
9593
const gameWidgetRoot = document.getElementById('game-widget-root');
9694
const heatmapRoot = document.getElementById('heatmap-root');
9795
const onlineRoot = document.getElementById('online-root');
@@ -107,10 +105,6 @@ const adminTournamentRoot = document.getElementById('tournament-admin-root');
107105
const eventWidgetRoot = document.getElementById('event-widget');
108106
const userPageRoot = document.getElementById('user-page-root');
109107

110-
if (extension) {
111-
renderExtensionPopup(extension);
112-
}
113-
114108
if (gameWidgetRoot) {
115109
renderGameWidget(gameWidgetRoot);
116110
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import React from 'react';
2+
3+
import useHover from '../utils/useHover';
4+
5+
import UserInfo from './UserInfo';
6+
7+
function ChatUserInfo({ user, displayMenu, className = '' }) {
8+
const [ref, hovered] = useHover();
9+
10+
return (
11+
<div
12+
ref={ref}
13+
role="button"
14+
tabIndex={0}
15+
className={className}
16+
title={user.name}
17+
key={user.id}
18+
data-user-id={user.id}
19+
data-user-name={user.name}
20+
onContextMenu={displayMenu}
21+
onClick={displayMenu}
22+
onKeyPress={displayMenu}
23+
>
24+
<UserInfo user={user} hovered={hovered} hideInfo hideOnlineIndicator />
25+
</div>
26+
);
27+
}
28+
29+
export default ChatUserInfo;

services/app/apps/codebattle/assets/js/widgets/components/Editor.jsx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, { memo } from 'react';
22

33
import MonacoEditor, { loader } from '@monaco-editor/react';
4+
import PropTypes from 'prop-types';
45

56
import haskellProvider from '../config/editor/haskell';
67
import sassProvider from '../config/editor/sass';
@@ -64,4 +65,33 @@ function Editor(props) {
6465
);
6566
}
6667

68+
Editor.propTypes = {
69+
value: PropTypes.string.isRequired,
70+
syntax: PropTypes.string,
71+
onChange: PropTypes.func.isRequired,
72+
theme: PropTypes.string.isRequired,
73+
loading: PropTypes.bool,
74+
wordWrap: PropTypes.string,
75+
lineNumbers: PropTypes.string,
76+
fontSize: PropTypes.number,
77+
editable: PropTypes.bool,
78+
gameMode: PropTypes.string.isRequired,
79+
checkResult: PropTypes.func.isRequired,
80+
toggleMuteSound: PropTypes.func.isRequired,
81+
mute: PropTypes.bool.isRequired,
82+
userType: PropTypes.string.isRequired,
83+
userId: PropTypes.string.isRequired,
84+
onChangeCursorSelection: PropTypes.func.isRequired,
85+
onChangeCursorPosition: PropTypes.func.isRequired,
86+
};
87+
88+
Editor.defaultProps = {
89+
wordWrap: 'off',
90+
lineNumbers: 'on',
91+
syntax: 'js',
92+
fontSize: 16,
93+
editable: false,
94+
loading: false,
95+
};
96+
6797
export default memo(Editor);

services/app/apps/codebattle/assets/js/widgets/components/ExtensionPopup.jsx

Lines changed: 0 additions & 80 deletions
This file was deleted.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import React from 'react';
2+
3+
import moment from 'moment';
4+
5+
function InfoMessage({ text, time }) {
6+
return (
7+
<div className="d-flex align-items-baseline flex-wrap">
8+
<small className="text-muted text-small">{text}</small>
9+
<small className="text-muted text-small ml-auto">
10+
{time ? moment.unix(time).format('HH:mm:ss') : ''}
11+
</small>
12+
</div>
13+
);
14+
}
15+
16+
export default InfoMessage;

services/app/apps/codebattle/assets/js/widgets/components/Loading.jsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ const getSize = ({ small = false, large = false, adaptive = false }) => {
1111
}
1212
};
1313

14-
const Loading = params => {
15-
const size = getSize(params);
14+
function Loading(props) {
15+
const size = getSize(props);
1616

1717
return (
1818
<div className="d-flex my-0 py-1 justify-content-center">
1919
<ReactLoading type="spin" color="#6c757d" height={size} width={size} />
2020
</div>
2121
);
22-
};
22+
}
2323

2424
export default Loading;
Lines changed: 57 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,108 +1,112 @@
11
import React from 'react';
22

33
import cn from 'classnames';
4-
import moment from 'moment';
54

5+
import useHover from '../utils/useHover';
6+
7+
import InfoMessage from './InfoMessage';
68
import MessageTag from './MessageTag';
79
import MessageTimestamp from './MessageTimestamp';
10+
import SystemMessage from './SystemMessage';
11+
12+
function MessageHeader({ name, time, hovered }) {
13+
const playerClassName = cn(
14+
'd-inline-block text-truncate align-top text-nowrap cb-username-max-length mr-1',
15+
{ 'text-primary': hovered },
16+
);
17+
18+
return (
19+
<>
20+
<span className="font-weight-bold">
21+
<span className={playerClassName}>
22+
{name}
23+
</span>
24+
</span>
25+
<MessageTimestamp time={time} />
26+
</>
27+
);
28+
}
29+
30+
function MessagePart({ part, index, name }) {
31+
if (part.slice(1) === name) {
32+
return (
33+
<span key={index} className="font-weight-bold bg-warning">
34+
{part}
35+
</span>
36+
);
37+
}
838

9-
const MessageHeader = ({ name, time }) => (
10-
<>
11-
<span className="font-weight-bold">
12-
<span className="d-inline-block text-truncate align-top text-nowrap cb-username-max-length mr-1">
13-
{name}
39+
if (part.startsWith('@')) {
40+
return (
41+
<span key={index} className="font-weight-bold text-primary">
42+
{part}
1443
</span>
15-
</span>
16-
<MessageTimestamp time={time} />
17-
</>
18-
);
44+
);
45+
}
46+
47+
return part;
48+
}
1949

20-
const Message = ({
50+
function Message({
2151
text = '',
2252
name = '',
2353
userId,
2454
type,
2555
time,
2656
meta,
2757
displayMenu,
28-
}) => {
58+
}) {
59+
const [chatHeaderRef, hoveredChatHeader] = useHover();
60+
2961
if (!text) {
3062
return null;
3163
}
3264

3365
if (type === 'system') {
34-
const statusClassName = cn('text-small', {
35-
'text-danger': ['error', 'failure'].includes(meta?.status),
36-
'text-success': meta?.status === 'success',
37-
'text-muted': meta?.status === 'event',
38-
});
39-
40-
return (
41-
<div className="d-flex align-items-baseline flex-wrap">
42-
<small className={statusClassName}>{text}</small>
43-
</div>
44-
);
66+
return <SystemMessage text={text} meta={meta} />;
4567
}
4668

4769
if (type === 'info') {
48-
return (
49-
<div className="d-flex align-items-baseline flex-wrap">
50-
<small className="text-muted text-small">{text}</small>
51-
<small className="text-muted text-small ml-auto">
52-
{time ? moment.unix(time).format('HH:mm:ss') : ''}
53-
</small>
54-
</div>
55-
);
70+
return <InfoMessage text={text} time={time} />;
5671
}
5772

5873
const parts = text.split(/(@+[-a-zA-Z0-9_]+\b)/g);
5974

60-
const renderMessagePart = (part, i) => {
61-
if (part.slice(1) === name) {
62-
return (
63-
<span key={i} className="font-weight-bold bg-warning">
64-
{part}
65-
</span>
66-
);
67-
}
68-
if (part.startsWith('@')) {
69-
return (
70-
<span key={i} className="font-weight-bold text-primary">
71-
{part}
72-
</span>
73-
);
74-
}
75-
return part;
76-
};
77-
7875
const textPartsClassNames = cn('text-break', {
7976
'cb-private-text': meta?.type === 'private',
8077
});
8178

8279
return (
8380
<div className="d-flex align-items-baseline flex-wrap mb-1">
84-
<span className="d-flex flex-column">
81+
<span className="d-flex flex-column w-100">
8582
<span
83+
ref={chatHeaderRef}
8684
role="button"
8785
tabIndex={0}
8886
title={`Message (${name})`}
87+
className="d-flex justify-content-between"
8988
data-user-id={userId}
9089
data-user-name={name}
9190
onContextMenu={displayMenu}
9291
onClick={displayMenu}
9392
onKeyPress={displayMenu}
9493
>
95-
<MessageHeader name={name} time={time} />
94+
<MessageHeader name={name} time={time} hovered={hoveredChatHeader} />
9695
</span>
9796
<span>
9897
<MessageTag messageType={meta?.type} />
9998
<span className={textPartsClassNames}>
100-
{parts.map((part, i) => renderMessagePart(part, i))}
99+
{parts.map(
100+
(part, i) => (
101+
/* eslint-disable react/no-array-index-key */
102+
<MessagePart key={i} part={part} index={i} name={name} />
103+
),
104+
)}
101105
</span>
102106
</span>
103107
</span>
104108
</div>
105109
);
106-
};
110+
}
107111

108112
export default Message;

0 commit comments

Comments
 (0)