diff --git a/app/soapbox/features/chats/chat_room.js b/app/soapbox/features/chats/chat_room.js index e6df1953d..4a8c480ea 100644 --- a/app/soapbox/features/chats/chat_room.js +++ b/app/soapbox/features/chats/chat_room.js @@ -6,18 +6,20 @@ import { injectIntl } from 'react-intl'; import ImmutablePureComponent from 'react-immutable-pure-component'; import Avatar from 'soapbox/components/avatar'; import { acctFull } from 'soapbox/utils/accounts'; -import { fetchChat } from 'soapbox/actions/chats'; +import { fetchChat, markChatRead } from 'soapbox/actions/chats'; import ChatBox from './components/chat_box'; import Column from 'soapbox/components/column'; import ColumnBackButton from 'soapbox/components/column_back_button'; +import { Map as ImmutableMap } from 'immutable'; import { makeGetChat } from 'soapbox/selectors'; const mapStateToProps = (state, { params }) => { const getChat = makeGetChat(); + const chat = state.getIn(['chats', params.chatId], ImmutableMap()).toJS(); return { me: state.get('me'), - chat: getChat(state, { id: params.chatId }), + chat: getChat(state, chat), }; }; @@ -42,9 +44,26 @@ class ChatRoom extends ImmutablePureComponent { this.inputElem.focus(); } + markRead = () => { + const { dispatch, chat } = this.props; + if (!chat) return; + dispatch(markChatRead(chat.get('id'))); + } + componentDidMount() { const { dispatch, params } = this.props; dispatch(fetchChat(params.chatId)); + this.markRead(); + } + + componentDidUpdate(prevProps) { + const markReadConditions = [ + () => this.props.chat !== undefined, + () => this.props.chat.get('unread') > 0, + ]; + + if (markReadConditions.every(c => c() === true)) + this.markRead(); } render() { diff --git a/app/soapbox/features/chats/components/chat_box.js b/app/soapbox/features/chats/components/chat_box.js index 0f1de7dbb..40123ca25 100644 --- a/app/soapbox/features/chats/components/chat_box.js +++ b/app/soapbox/features/chats/components/chat_box.js @@ -40,11 +40,24 @@ class ChatBox extends ImmutablePureComponent { content: '', } - handleKeyDown = (e) => { + sendMessage = () => { const { chatId } = this.props; - if (e.key === 'Enter') { - this.props.dispatch(sendChatMessage(chatId, this.state)); - this.setState({ content: '' }); + if (this.state.content.length < 1) return; + this.props.dispatch(sendChatMessage(chatId, this.state)); + this.setState({ content: '' }); + } + + insertLine = () => { + const { content } = this.state; + this.setState({ content: content + '\n' }); + } + + handleKeyDown = (e) => { + if (e.key === 'Enter' && e.shiftKey) { + this.insertLine(); + e.preventDefault(); + } else if (e.key === 'Enter') { + this.sendMessage(); e.preventDefault(); } } diff --git a/app/soapbox/features/chats/components/chat_list.js b/app/soapbox/features/chats/components/chat_list.js index 41aae6397..e24d31ab9 100644 --- a/app/soapbox/features/chats/components/chat_list.js +++ b/app/soapbox/features/chats/components/chat_list.js @@ -3,7 +3,6 @@ import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import { injectIntl } from 'react-intl'; import ImmutablePureComponent from 'react-immutable-pure-component'; -import { fetchChats } from 'soapbox/actions/chats'; import Chat from './chat'; import { makeGetChat } from 'soapbox/selectors'; @@ -42,10 +41,6 @@ class ChatList extends ImmutablePureComponent { emptyMessage: PropTypes.node, }; - componentDidMount() { - this.props.dispatch(fetchChats()); - } - render() { const { chats, emptyMessage } = this.props; diff --git a/app/soapbox/features/chats/components/chat_message_list.js b/app/soapbox/features/chats/components/chat_message_list.js index 8e52e3270..e2fe305bd 100644 --- a/app/soapbox/features/chats/components/chat_message_list.js +++ b/app/soapbox/features/chats/components/chat_message_list.js @@ -4,9 +4,14 @@ import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import { injectIntl } from 'react-intl'; import ImmutablePureComponent from 'react-immutable-pure-component'; -import { List as ImmutableList } from 'immutable'; +import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; import emojify from 'soapbox/features/emoji/emoji'; import classNames from 'classnames'; +import { escape } from 'lodash'; + +const makeEmojiMap = record => record.get('emojis', ImmutableList()).reduce((map, emoji) => { + return map.set(`:${emoji.get('shortcode')}:`, emoji); +}, ImmutableMap()); const mapStateToProps = (state, { chatMessageIds }) => ({ me: state.get('me'), @@ -72,6 +77,18 @@ class ChatMessageList extends ImmutablePureComponent { this.scrollToBottom(); } + parsePendingContent = content => { + return escape(content).replace(/(?:\r\n|\r|\n)/g, '
'); + } + + parseContent = chatMessage => { + const content = chatMessage.get('content') || ''; + const pending = chatMessage.get('pending', false); + const formatted = pending ? this.parsePendingContent(content) : content; + const emojiMap = makeEmojiMap(chatMessage); + return emojify(formatted, emojiMap.toJS()); + } + render() { const { chatMessages, me } = this.props; @@ -88,7 +105,7 @@ class ChatMessageList extends ImmutablePureComponent { diff --git a/app/soapbox/features/ui/index.js b/app/soapbox/features/ui/index.js index 59269d577..ff80086ee 100644 --- a/app/soapbox/features/ui/index.js +++ b/app/soapbox/features/ui/index.js @@ -18,6 +18,7 @@ import { expandHomeTimeline } from '../../actions/timelines'; import { expandNotifications } from '../../actions/notifications'; import { fetchReports } from '../../actions/admin'; import { fetchFilters } from '../../actions/filters'; +import { fetchChats } from 'soapbox/actions/chats'; import { clearHeight } from '../../actions/height_cache'; import { openModal } from '../../actions/modal'; import { WrappedRoute } from './util/react_router_helpers'; @@ -433,6 +434,7 @@ class UI extends React.PureComponent { if (account) { this.props.dispatch(expandHomeTimeline()); this.props.dispatch(expandNotifications()); + this.props.dispatch(fetchChats()); // this.props.dispatch(fetchGroups('member')); if (isStaff(account)) this.props.dispatch(fetchReports({ state: 'open' })); diff --git a/app/styles/chats.scss b/app/styles/chats.scss index 7ae51847d..011543ca3 100644 --- a/app/styles/chats.scss +++ b/app/styles/chats.scss @@ -113,6 +113,8 @@ background-color: var(--background-color); overflow: hidden; text-overflow: ellipsis; + overflow-wrap: break-word; + white-space: break-spaces; a { color: var(--brand-color--hicontrast);