diff --git a/app/soapbox/features/chats/chat_room.js b/app/soapbox/features/chats/chat_room.js
new file mode 100644
index 000000000..fc17f9c2e
--- /dev/null
+++ b/app/soapbox/features/chats/chat_room.js
@@ -0,0 +1,109 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import PropTypes from 'prop-types';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import { injectIntl } from 'react-intl';
+import ImmutablePureComponent from 'react-immutable-pure-component';
+import {
+ fetchChatMessages,
+ sendChatMessage,
+ markChatRead,
+} from 'soapbox/actions/chats';
+import { List as ImmutableList, OrderedSet as ImmutableOrderedSet } from 'immutable';
+import ChatMessageList from './components/chat_message_list';
+import Column from 'soapbox/features/ui/components/column';
+
+const mapStateToProps = (state, { params }) => ({
+ me: state.get('me'),
+ chat: state.getIn(['chats', params.chatId]),
+ chatMessageIds: state.getIn(['chat_message_lists', params.chatId], ImmutableOrderedSet()),
+});
+
+export default @connect(mapStateToProps)
+@injectIntl
+class ChatWindow extends ImmutablePureComponent {
+
+ static propTypes = {
+ dispatch: PropTypes.func.isRequired,
+ intl: PropTypes.object.isRequired,
+ chatMessageIds: ImmutablePropTypes.orderedSet,
+ chat: ImmutablePropTypes.map,
+ me: PropTypes.node,
+ }
+
+ static defaultProps = {
+ chatMessages: ImmutableList(),
+ }
+
+ state = {
+ content: '',
+ }
+
+ handleKeyDown = (chatId) => {
+ return (e) => {
+ if (e.key === 'Enter') {
+ this.props.dispatch(sendChatMessage(chatId, this.state));
+ this.setState({ content: '' });
+ e.preventDefault();
+ }
+ };
+ }
+
+ handleContentChange = (e) => {
+ this.setState({ content: e.target.value });
+ }
+
+ handleReadChat = (e) => {
+ const { dispatch, chat } = this.props;
+ dispatch(markChatRead(chat.get('id')));
+ }
+
+ focusInput = () => {
+ if (!this.inputElem) return;
+ this.inputElem.focus();
+ }
+
+ setInputRef = (el) => {
+ this.inputElem = el;
+ this.focusInput();
+ };
+
+ componentDidMount() {
+ const { dispatch, chatMessages, params } = this.props;
+ if (chatMessages && chatMessages.count() < 1)
+ dispatch(fetchChatMessages(params.chatId));
+ }
+
+ componentDidUpdate(prevProps) {
+ const markReadConditions = [
+ () => this.props.chat !== undefined,
+ () => document.activeElement === this.inputElem,
+ () => this.props.chat.get('unread') > 0,
+ ];
+
+ if (markReadConditions.every(c => c() === true))
+ this.handleReadChat();
+ }
+
+ render() {
+ const { chatMessageIds, chat } = this.props;
+ if (!chat) return null;
+
+ return (
+
+
+
+
+
+
+ );
+ }
+
+}
diff --git a/app/soapbox/features/ui/index.js b/app/soapbox/features/ui/index.js
index 4af1ab29c..084c8a0cf 100644
--- a/app/soapbox/features/ui/index.js
+++ b/app/soapbox/features/ui/index.js
@@ -80,6 +80,7 @@ import {
SecurityForm,
MfaForm,
ChatIndex,
+ ChatRoom,
} from './util/async-components';
// Dummy import, to make sure that ends up in the application bundle.
@@ -239,7 +240,7 @@ class SwitchingColumnsArea extends React.PureComponent {
- {/* */}
+
diff --git a/app/soapbox/features/ui/util/async-components.js b/app/soapbox/features/ui/util/async-components.js
index 9d39c099f..73a8a5540 100644
--- a/app/soapbox/features/ui/util/async-components.js
+++ b/app/soapbox/features/ui/util/async-components.js
@@ -201,3 +201,7 @@ export function MfaForm() {
export function ChatIndex() {
return import(/* webpackChunkName: "features/chats" */'../../chats');
}
+
+export function ChatRoom() {
+ return import(/* webpackChunkName: "features/chats/chat_room" */'../../chats/chat_room');
+}