From db6831603123dfdaf763bdf2f0fae85f044b99a9 Mon Sep 17 00:00:00 2001
From: Jeremie Pardou-Piquemal <571533+jrmi@users.noreply.github.com>
Date: Wed, 13 May 2020 22:06:51 +0200
Subject: [PATCH] Rework Home component phase 2
---
client/src/components/Home/Activity.js | 227 ++++++++----------
client/src/components/Home/ActivityList.js | 136 +++++------
.../src/components/Home/ActivityList.test.js | 95 +++-----
client/src/components/Home/Home.js | 20 +-
client/src/components/Home/index.js | 3 -
5 files changed, 207 insertions(+), 274 deletions(-)
diff --git a/client/src/components/Home/Activity.js b/client/src/components/Home/Activity.js
index a531c51..61cd116 100644
--- a/client/src/components/Home/Activity.js
+++ b/client/src/components/Home/Activity.js
@@ -1,4 +1,4 @@
-import React, { Component } from 'react'
+import React from 'react'
import PropTypes from 'prop-types'
import Message from 'components/Message'
import Username from 'components/Username'
@@ -8,143 +8,128 @@ import { getObjectUrl } from 'utils/file'
import T from 'components/T'
-class Activity extends Component {
- constructor(props) {
- super(props)
+const FileDisplay = ({ activity: { fileType, encodedFile, fileName, username }, scrollToBottom }) => {
+ const zoomableImage = React.useRef(null)
- this.state = {
- zoomableImages: [],
- }
+ const handleImageDisplay = () => {
+ Zoom(zoomableImage.current)
+ scrollToBottom()
}
- getFileDisplay(activity) {
- const type = activity.fileType
- if (type.match('image.*')) {
+ if (fileType.match('image.*')) {
+ return (
+
+ )
+ }
+ return null
+}
+
+const Activity = ({ activity, scrollToBottom }) => {
+ switch (activity.type) {
+ case 'TEXT_MESSAGE':
return (
-
this._zoomableImage = c}
- className="image-transfer zoomable"
- src={`data:${activity.fileType};base64,${activity.encodedFile}`}
- alt={`${activity.fileName} from ${activity.username}`}
- onLoad={this.handleImageDisplay.bind(this)}
+
)
- }
- return null
- }
-
- handleImageDisplay() {
- Zoom(this._zoomableImage)
- this.props.scrollToBottom()
- }
-
- getActivityComponent(activity) {
- switch (activity.type) {
- case 'TEXT_MESSAGE':
- return (
-
- )
- case 'USER_ENTER':
- return (
-
-
-
- }} path='userJoined'/>
-
-
- )
- case 'USER_EXIT':
- return (
-
-
-
- }} path='userLeft'/>
-
-
- )
- case 'TOGGLE_LOCK_ROOM':
- if (activity.locked) {
- return (
-
-
- }} path='lockedRoom'/>
-
- )
- } else {
- return (
-
-
- }} path='unlockedRoom'/>
-
- )
- }
- case 'NOTICE':
- return (
-
- {activity.message}
-
- )
- case 'CHANGE_USERNAME':
+ case 'USER_ENTER':
+ return (
+
+
+
+ }} path='userJoined'/>
+
+
+ )
+ case 'USER_EXIT':
+ return (
+
+
+
+ }} path='userLeft'/>
+
+
+ )
+ case 'TOGGLE_LOCK_ROOM':
+ if (activity.locked) {
return (
,
- newUsername:
- }} path='nameChange'/>
-
+ username:
+ }} path='lockedRoom'/>
)
- case 'USER_ACTION':
+ } else {
return (
- * {activity.action}
+
+ }} path='unlockedRoom'/>
)
- case 'RECEIVE_FILE':
- const downloadUrl = getObjectUrl(activity.encodedFile, activity.fileType)
- return (
+ }
+ case 'NOTICE':
+ return (
+
+ {activity.message}
+
+ )
+ case 'CHANGE_USERNAME':
+ return (
+
+ ,
+ newUsername:
+ }} path='nameChange'/>
+
+
+ )
+ case 'USER_ACTION':
+ return (
+
+ * {activity.action}
+
+ )
+ case 'RECEIVE_FILE':
+ const downloadUrl = getObjectUrl(activity.encodedFile, activity.fileType)
+ return (
+
-
this.messageStream = el} data-testid="main-div">
-
this.activitiesList = el}>
-
- {this.props.activities.map((activity, index) => (
+ return (
+
+
+
+
+ {activities.map((activity, index) => (
-
-
+
- ))}
-
-
-
-
-
+ ))}
+
- )
- }
+
+
+
+
+ )
}
ActivityList.propTypes = {
activities: PropTypes.array.isRequired,
openModal: PropTypes.func.isRequired,
- setScrolledToBottom: PropTypes.func.isRequired,
- scrolledToBottom: PropTypes.bool.isRequired,
}
-export default ActivityList;
+export default ActivityList
diff --git a/client/src/components/Home/ActivityList.test.js b/client/src/components/Home/ActivityList.test.js
index af1c769..ebe234c 100644
--- a/client/src/components/Home/ActivityList.test.js
+++ b/client/src/components/Home/ActivityList.test.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { render, fireEvent, waitFor } from '@testing-library/react';
+import { render, fireEvent } from '@testing-library/react';
import ActivityList from './ActivityList';
import { Provider } from 'react-redux';
import configureStore from 'store';
@@ -12,7 +12,7 @@ describe('ActivityList component', () => {
it('should display', () => {
const { asFragment } = render(
-
+
,
);
@@ -39,7 +39,7 @@ describe('ActivityList component', () => {
];
const { asFragment } = render(
-
+
,
);
@@ -51,7 +51,7 @@ describe('ActivityList component', () => {
const { getByText } = render(
-
+
,
);
@@ -59,79 +59,33 @@ describe('ActivityList component', () => {
jest.runAllTimers();
expect(mockOpenModal.mock.calls[0][0]).toBe('About');
+ jest.runAllTimers();
});
it('should focus chat', () => {
const { getByTestId } = render(
-
+
,
);
fireEvent.click(getByTestId('main-div'));
jest.runAllTimers();
});
- it('should handle scroll', () => {
- const mockSetScrollToBottom = jest.fn();
- jest
- .spyOn(Element.prototype, 'clientHeight', 'get')
- .mockReturnValueOnce(400)
- .mockReturnValueOnce(200)
- .mockReturnValueOnce(400)
- .mockReturnValueOnce(200);
+ it('should scroll to bottom on new message if not scrolled', () => {
+ jest.spyOn(Element.prototype, 'clientHeight', 'get').mockReturnValueOnce(400).mockReturnValueOnce(200);
Element.prototype.getBoundingClientRect = jest
.fn()
.mockReturnValueOnce({ top: 0 })
- .mockReturnValueOnce({ top: 60 })
- .mockReturnValueOnce({ top: 0 })
.mockReturnValueOnce({ top: 261 });
- const { getByTestId, rerender } = render(
-
-
- ,
- );
- fireEvent.scroll(getByTestId('main-div'));
-
- expect(mockSetScrollToBottom).toHaveBeenLastCalledWith(true);
-
- rerender(
-
-
- ,
- );
-
- fireEvent.scroll(getByTestId('main-div'));
-
- expect(mockSetScrollToBottom).toHaveBeenCalledTimes(2);
- expect(mockSetScrollToBottom).toHaveBeenLastCalledWith(false);
- });
-
- it('should scroll to bottom on new message', () => {
- const mockSetScrollToBottom = jest.fn();
-
jest.spyOn(Element.prototype, 'scrollHeight', 'get').mockReturnValue(42);
const mockScrollTop = jest.spyOn(Element.prototype, 'scrollTop', 'set');
- const { rerender } = render(
+ const { rerender, getByTestId } = render(
-
+
,
);
@@ -147,14 +101,39 @@ describe('ActivityList component', () => {
text: 'Hi!',
},
]}
- setScrolledToBottom={mockSetScrollToBottom}
- scrolledToBottom={true}
/>
,
);
jest.runAllTimers();
+ expect(mockScrollTop).toHaveBeenCalledTimes(2);
expect(mockScrollTop).toHaveBeenLastCalledWith(42);
+
+ fireEvent.scroll(getByTestId('main-div'));
+
+ rerender(
+
+
+ ,
+ );
+
+ expect(mockScrollTop).toHaveBeenCalledTimes(2);
});
});
diff --git a/client/src/components/Home/Home.js b/client/src/components/Home/Home.js
index ef2761b..a96fb65 100644
--- a/client/src/components/Home/Home.js
+++ b/client/src/components/Home/Home.js
@@ -11,7 +11,6 @@ import Settings from 'components/Settings'
import Welcome from 'components/Welcome'
import RoomLocked from 'components/RoomLocked'
import { X, AlertCircle } from 'react-feather'
-import { defer } from 'lodash'
import Tinycon from 'tinycon'
import beepFile from 'audio/beep.mp3'
import classNames from 'classnames'
@@ -24,13 +23,6 @@ const crypto = new Crypto()
Modal.setAppElement('#root');
class Home extends Component {
- constructor(props) {
- super(props)
-
- this.state = {
- zoomableImages: []
- }
- }
async componentWillMount() {
const roomId = encodeURI(this.props.match.params.roomId)
@@ -145,7 +137,6 @@ class Home extends Component {
}
bindEvents() {
-
window.onfocus = () => {
this.props.toggleWindowFocus(true)
}
@@ -175,11 +166,6 @@ class Home extends Component {
})
}
- handleChatClick() {
- this.setState({ focusChat: true })
- defer(() => this.setState({ focusChat: false }))
- }
-
render() {
const modalOpts = this.getModal()
return (
@@ -201,11 +187,9 @@ class Home extends Component {
translations={this.props.translations}
/>
-