diff --git a/app/soapbox/actions/chats.js b/app/soapbox/actions/chats.js index 6f8d1c788..5529da6f6 100644 --- a/app/soapbox/actions/chats.js +++ b/app/soapbox/actions/chats.js @@ -165,3 +165,19 @@ export function deleteChatMessage(chatId, messageId) { }); }; } + +/** Start a chat and launch it in the UI */ +export function launchChat(account, router) { + const isMobile = width => width <= 1190; + + return (dispatch, getState) => { + // TODO: make this faster + return dispatch(startChat(account.get('id'))).then(chat => { + if (isMobile(window.innerWidth)) { + router.push(`/chats/${chat.id}`); + } else { + dispatch(openChat(chat.id)); + } + }); + }; +} diff --git a/app/soapbox/components/status.js b/app/soapbox/components/status.js index 5a9de370d..544c9320f 100644 --- a/app/soapbox/components/status.js +++ b/app/soapbox/components/status.js @@ -70,6 +70,7 @@ class Status extends ImmutablePureComponent { onReblog: PropTypes.func, onDelete: PropTypes.func, onDirect: PropTypes.func, + onChat: PropTypes.func, onMention: PropTypes.func, onPin: PropTypes.func, onOpenMedia: PropTypes.func, diff --git a/app/soapbox/components/status_action_bar.js b/app/soapbox/components/status_action_bar.js index 4ae7a8539..0b7c5e0a5 100644 --- a/app/soapbox/components/status_action_bar.js +++ b/app/soapbox/components/status_action_bar.js @@ -20,6 +20,7 @@ const messages = defineMessages({ delete: { id: 'status.delete', defaultMessage: 'Delete' }, redraft: { id: 'status.redraft', defaultMessage: 'Delete & re-draft' }, direct: { id: 'status.direct', defaultMessage: 'Direct message @{name}' }, + chat: { id: 'status.chat', defaultMessage: 'Chat with @{name}' }, mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' }, mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' }, block: { id: 'account.block', defaultMessage: 'Block @{name}' }, @@ -74,6 +75,7 @@ class StatusActionBar extends ImmutablePureComponent { onReblog: PropTypes.func, onDelete: PropTypes.func, onDirect: PropTypes.func, + onChat: PropTypes.func, onMention: PropTypes.func, onMute: PropTypes.func, onBlock: PropTypes.func, @@ -218,6 +220,10 @@ class StatusActionBar extends ImmutablePureComponent { this.props.onDirect(this.props.status.get('account'), this.context.router.history); } + handleChatClick = () => { + this.props.onChat(this.props.status.get('account'), this.context.router.history); + } + handleMuteClick = () => { this.props.onMute(this.props.status.get('account')); } @@ -331,7 +337,13 @@ class StatusActionBar extends ImmutablePureComponent { menu.push({ text: intl.formatMessage(messages.redraft), action: this.handleRedraftClick }); } else { menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick }); - menu.push({ text: intl.formatMessage(messages.direct, { name: status.getIn(['account', 'username']) }), action: this.handleDirectClick }); + + if (status.getIn(['account', 'pleroma', 'accepts_chat_messages'], false) === true) { + menu.push({ text: intl.formatMessage(messages.chat, { name: status.getIn(['account', 'username']) }), action: this.handleChatClick }); + } else { + menu.push({ text: intl.formatMessage(messages.direct, { name: status.getIn(['account', 'username']) }), action: this.handleDirectClick }); + } + menu.push(null); menu.push({ text: intl.formatMessage(messages.mute, { name: status.getIn(['account', 'username']) }), action: this.handleMuteClick }); menu.push({ text: intl.formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick }); diff --git a/app/soapbox/containers/status_container.js b/app/soapbox/containers/status_container.js index 5d0490c60..0df68e5f9 100644 --- a/app/soapbox/containers/status_container.js +++ b/app/soapbox/containers/status_container.js @@ -37,6 +37,7 @@ import { import { getSettings } from '../actions/settings'; import { getSoapboxConfig } from 'soapbox/actions/soapbox'; import { deactivateUserModal, deleteUserModal, deleteStatusModal, toggleStatusSensitivityModal } from 'soapbox/actions/moderation'; +import { launchChat } from 'soapbox/actions/chats'; const messages = defineMessages({ deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' }, @@ -151,6 +152,10 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ dispatch(directCompose(account, router)); }, + onChat(account, router) { + dispatch(launchChat(account, router)); + }, + onMention(account, router) { dispatch(mentionCompose(account, router)); }, diff --git a/app/soapbox/features/account/components/header.js b/app/soapbox/features/account/components/header.js index a8c173e94..6a603fe20 100644 --- a/app/soapbox/features/account/components/header.js +++ b/app/soapbox/features/account/components/header.js @@ -300,7 +300,7 @@ class Header extends ImmutablePureComponent { } renderMessageButton() { - const { account, me } = this.props; + const { intl, account, me } = this.props; if (!me || !account || account.get('id') === me) { return null; @@ -309,9 +309,21 @@ class Header extends ImmutablePureComponent { const canChat = account.getIn(['pleroma', 'accepts_chat_messages'], false) === true; if (canChat) { - return ; + return ( + + ); } else { - return ; + return ( + + ); } } diff --git a/app/soapbox/features/account_timeline/components/header.js b/app/soapbox/features/account_timeline/components/header.js index b322125fd..aba9af207 100644 --- a/app/soapbox/features/account_timeline/components/header.js +++ b/app/soapbox/features/account_timeline/components/header.js @@ -14,6 +14,7 @@ export default class Header extends ImmutablePureComponent { onBlock: PropTypes.func.isRequired, onMention: PropTypes.func.isRequired, onDirect: PropTypes.func.isRequired, + onChat: PropTypes.func, onReblogToggle: PropTypes.func.isRequired, onReport: PropTypes.func.isRequired, onMute: PropTypes.func.isRequired, diff --git a/app/soapbox/features/account_timeline/containers/header_container.js b/app/soapbox/features/account_timeline/containers/header_container.js index ddcf6a3ce..6cf505e56 100644 --- a/app/soapbox/features/account_timeline/containers/header_container.js +++ b/app/soapbox/features/account_timeline/containers/header_container.js @@ -24,7 +24,7 @@ import { blockDomain, unblockDomain } from '../../../actions/domain_blocks'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { List as ImmutableList } from 'immutable'; import { getSettings } from 'soapbox/actions/settings'; -import { startChat, openChat } from 'soapbox/actions/chats'; +import { launchChat } from 'soapbox/actions/chats'; import { deactivateUserModal, deleteUserModal } from 'soapbox/actions/moderation'; import { verifyUser, @@ -50,8 +50,6 @@ const messages = defineMessages({ }); -const isMobile = width => width <= 1190; - const makeMapStateToProps = () => { const getAccount = makeGetAccount(); @@ -164,14 +162,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ }, onChat(account, router) { - // TODO make this faster - dispatch(startChat(account.get('id'))).then(chat => { - if (isMobile(window.innerWidth)) { - router.push(`/chats/${chat.id}`); - } else { - dispatch(openChat(chat.id)); - } - }).catch(() => {}); + dispatch(launchChat(account, router)); }, onDeactivateUser(account) { diff --git a/app/soapbox/features/status/components/action_bar.js b/app/soapbox/features/status/components/action_bar.js index a998e641a..9dbc73923 100644 --- a/app/soapbox/features/status/components/action_bar.js +++ b/app/soapbox/features/status/components/action_bar.js @@ -17,6 +17,7 @@ const messages = defineMessages({ delete: { id: 'status.delete', defaultMessage: 'Delete' }, redraft: { id: 'status.redraft', defaultMessage: 'Delete & re-draft' }, direct: { id: 'status.direct', defaultMessage: 'Direct message @{name}' }, + chat: { id: 'status.chat', defaultMessage: 'Chat with @{name}' }, mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' }, reply: { id: 'status.reply', defaultMessage: 'Reply' }, reblog: { id: 'status.reblog', defaultMessage: 'Repost' }, @@ -86,6 +87,7 @@ class ActionBar extends React.PureComponent { onDelete: PropTypes.func.isRequired, onBookmark: PropTypes.func, onDirect: PropTypes.func.isRequired, + onChat: PropTypes.func, onMention: PropTypes.func.isRequired, onMute: PropTypes.func, onMuteConversation: PropTypes.func, @@ -210,6 +212,10 @@ class ActionBar extends React.PureComponent { this.props.onDirect(this.props.status.get('account'), this.context.router.history); } + handleChatClick = () => { + this.props.onChat(this.props.status.get('account'), this.context.router.history); + } + handleMentionClick = () => { this.props.onMention(this.props.status.get('account'), this.context.router.history); } @@ -337,7 +343,13 @@ class ActionBar extends React.PureComponent { menu.push({ text: intl.formatMessage(messages.redraft), action: this.handleRedraftClick }); } else { menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick }); - //menu.push({ text: intl.formatMessage(messages.direct, { name: status.getIn(['account', 'username']) }), action: this.handleDirectClick }); + + if (status.getIn(['account', 'pleroma', 'accepts_chat_messages'], false) === true) { + menu.push({ text: intl.formatMessage(messages.chat, { name: status.getIn(['account', 'username']) }), action: this.handleChatClick }); + } else { + menu.push({ text: intl.formatMessage(messages.direct, { name: status.getIn(['account', 'username']) }), action: this.handleDirectClick }); + } + menu.push(null); menu.push({ text: intl.formatMessage(messages.mute, { name: status.getIn(['account', 'username']) }), action: this.handleMuteClick }); menu.push({ text: intl.formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick }); diff --git a/app/soapbox/features/status/containers/detailed_status_container.js b/app/soapbox/features/status/containers/detailed_status_container.js index cbe1d8485..1b2d64a2a 100644 --- a/app/soapbox/features/status/containers/detailed_status_container.js +++ b/app/soapbox/features/status/containers/detailed_status_container.js @@ -32,6 +32,7 @@ import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { showAlertForError } from '../../../actions/alerts'; import { getSettings } from 'soapbox/actions/settings'; import { deactivateUserModal, deleteUserModal, deleteStatusModal, toggleStatusSensitivityModal } from 'soapbox/actions/moderation'; +import { launchChat } from 'soapbox/actions/chats'; const messages = defineMessages({ deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' }, @@ -141,6 +142,10 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ dispatch(directCompose(account, router)); }, + onChat(account, router) { + dispatch(launchChat(account, router)); + }, + onMention(account, router) { dispatch(mentionCompose(account, router)); }, diff --git a/app/soapbox/features/status/index.js b/app/soapbox/features/status/index.js index ef0eaede0..8b42ad279 100644 --- a/app/soapbox/features/status/index.js +++ b/app/soapbox/features/status/index.js @@ -51,6 +51,7 @@ import { deactivateUserModal, deleteUserModal, deleteStatusModal, toggleStatusSe import ThreadStatus from './components/thread_status'; import PendingStatus from 'soapbox/features/ui/components/pending_status'; import SubNavigation from 'soapbox/components/sub_navigation'; +import { launchChat } from 'soapbox/actions/chats'; const messages = defineMessages({ title: { id: 'status.title', defaultMessage: 'Post' }, @@ -254,6 +255,10 @@ class Status extends ImmutablePureComponent { this.props.dispatch(directCompose(account, router)); } + handleChatClick = (account, router) => { + this.props.dispatch(launchChat(account, router)); + } + handleMentionClick = (account, router) => { this.props.dispatch(mentionCompose(account, router)); } @@ -643,6 +648,7 @@ class Status extends ImmutablePureComponent { onReblog={this.handleReblogClick} onDelete={this.handleDeleteClick} onDirect={this.handleDirectClick} + onChat={this.handleChatClick} onMention={this.handleMentionClick} onMute={this.handleMuteClick} onMuteConversation={this.handleConversationMuteClick}