diff --git a/app/soapbox/actions/emoji_reacts.js b/app/soapbox/actions/emoji_reacts.js index c3e6b63f4..8ed43b8ed 100644 --- a/app/soapbox/actions/emoji_reacts.js +++ b/app/soapbox/actions/emoji_reacts.js @@ -2,6 +2,7 @@ import api from '../api'; import { importFetchedAccounts, importFetchedStatus } from './importer'; import { favourite, unfavourite } from './interactions'; import { isLoggedIn } from 'soapbox/utils/auth'; +import { List as ImmutableList } from 'immutable'; export const EMOJI_REACT_REQUEST = 'EMOJI_REACT_REQUEST'; export const EMOJI_REACT_SUCCESS = 'EMOJI_REACT_SUCCESS'; @@ -19,7 +20,7 @@ const noOp = () => () => new Promise(f => f()); export const simpleEmojiReact = (status, emoji) => { return (dispatch, getState) => { - const emojiReacts = status.getIn(['pleroma', 'emoji_reactions']); + const emojiReacts = status.getIn(['pleroma', 'emoji_reactions'], ImmutableList()); if (emoji === '👍' && status.get('favourited')) return dispatch(unfavourite(status)); diff --git a/app/soapbox/components/status_action_bar.js b/app/soapbox/components/status_action_bar.js index fa4fd993e..b7ee34aae 100644 --- a/app/soapbox/components/status_action_bar.js +++ b/app/soapbox/components/status_action_bar.js @@ -14,6 +14,7 @@ import EmojiSelector from 'soapbox/components/emoji_selector'; import { getReactForStatus, reduceEmoji } from 'soapbox/utils/emoji_reacts'; import { simpleEmojiReact } from 'soapbox/actions/emoji_reacts'; import { List as ImmutableList } from 'immutable'; +import { getFeatures } from 'soapbox/utils/features'; const messages = defineMessages({ delete: { id: 'status.delete', defaultMessage: 'Delete' }, @@ -93,6 +94,7 @@ class StatusActionBar extends ImmutablePureComponent { allowedEmoji: ImmutablePropTypes.list, emojiSelectorFocused: PropTypes.bool, handleEmojiSelectorUnfocus: PropTypes.func.isRequired, + features: PropTypes.object.isRequired, }; static defaultProps = { @@ -132,16 +134,26 @@ class StatusActionBar extends ImmutablePureComponent { isMobile = () => window.matchMedia('only screen and (max-width: 895px)').matches; handleLikeButtonHover = e => { - if (!this.isMobile()) this.setState({ emojiSelectorVisible: true }); + const { features } = this.props; + + if (features.emojiReacts && !this.isMobile()) { + this.setState({ emojiSelectorVisible: true }); + } } handleLikeButtonLeave = e => { - if (!this.isMobile()) this.setState({ emojiSelectorVisible: false }); + const { features } = this.props; + + if (features.emojiReacts && !this.isMobile()) { + this.setState({ emojiSelectorVisible: false }); + } } handleLikeButtonClick = e => { + const { features } = this.props; const meEmojiReact = getReactForStatus(this.props.status, this.props.allowedEmoji) || '👍'; - if (this.isMobile()) { + + if (features.emojiReacts && this.isMobile()) { if (this.state.emojiSelectorVisible) { this.handleReactClick(meEmojiReact)(); } else { @@ -362,7 +374,7 @@ class StatusActionBar extends ImmutablePureComponent { } render() { - const { status, intl, allowedEmoji, emojiSelectorFocused, handleEmojiSelectorUnfocus } = this.props; + const { status, intl, allowedEmoji, emojiSelectorFocused, handleEmojiSelectorUnfocus, features } = this.props; const { emojiSelectorVisible } = this.state; const publicStatus = ['public', 'unlisted'].includes(status.get('visibility')); @@ -427,7 +439,7 @@ class StatusActionBar extends ImmutablePureComponent { > @@ -456,11 +468,13 @@ class StatusActionBar extends ImmutablePureComponent { const mapStateToProps = state => { const me = state.get('me'); const account = state.getIn(['accounts', me]); + const instance = state.get('instance'); return { me, isStaff: account ? isStaff(account) : false, isAdmin: account ? isAdmin(account) : false, + features: getFeatures(instance), }; }; diff --git a/app/soapbox/features/status/components/action_bar.js b/app/soapbox/features/status/components/action_bar.js index b435c77a4..332108133 100644 --- a/app/soapbox/features/status/components/action_bar.js +++ b/app/soapbox/features/status/components/action_bar.js @@ -11,6 +11,7 @@ import { isStaff, isAdmin } from 'soapbox/utils/accounts'; import { isUserTouching } from 'soapbox/is_mobile'; import EmojiSelector from 'soapbox/components/emoji_selector'; import { getReactForStatus } from 'soapbox/utils/emoji_reacts'; +import { getFeatures } from 'soapbox/utils/features'; const messages = defineMessages({ delete: { id: 'status.delete', defaultMessage: 'Delete' }, @@ -54,11 +55,13 @@ const messages = defineMessages({ const mapStateToProps = state => { const me = state.get('me'); const account = state.getIn(['accounts', me]); + const instance = state.get('instance'); return { me, isStaff: account ? isStaff(account) : false, isAdmin: account ? isAdmin(account) : false, + features: getFeatures(instance), }; }; @@ -103,6 +106,7 @@ class ActionBar extends React.PureComponent { emojiSelectorFocused: PropTypes.bool, handleEmojiSelectorExpand: PropTypes.func.isRequired, handleEmojiSelectorUnfocus: PropTypes.func.isRequired, + features: PropTypes.object.isRequired, }; static defaultProps = { @@ -146,16 +150,26 @@ class ActionBar extends React.PureComponent { } handleLikeButtonHover = e => { - if (!isUserTouching()) this.setState({ emojiSelectorVisible: true }); + const { features } = this.props; + + if (features.emojiReacts && !isUserTouching()) { + this.setState({ emojiSelectorVisible: true }); + } } handleLikeButtonLeave = e => { - if (!isUserTouching()) this.setState({ emojiSelectorVisible: false }); + const { features } = this.props; + + if (features.emojiReacts && !isUserTouching()) { + this.setState({ emojiSelectorVisible: false }); + } } handleLikeButtonClick = e => { + const { features } = this.props; const meEmojiReact = getReactForStatus(this.props.status, this.props.allowedEmoji) || '👍'; - if (isUserTouching()) { + + if (features.emojiReacts && isUserTouching()) { if (this.state.emojiSelectorVisible) { this.handleReactClick(meEmojiReact)(); } else { @@ -278,7 +292,7 @@ class ActionBar extends React.PureComponent { } render() { - const { status, intl, me, isStaff, isAdmin, allowedEmoji, emojiSelectorFocused, handleEmojiSelectorExpand, handleEmojiSelectorUnfocus } = this.props; + const { status, intl, me, isStaff, isAdmin, allowedEmoji, emojiSelectorFocused, handleEmojiSelectorExpand, handleEmojiSelectorUnfocus, features } = this.props; const { emojiSelectorVisible } = this.state; const ownAccount = status.getIn(['account', 'id']) === me; @@ -391,7 +405,7 @@ class ActionBar extends React.PureComponent { > diff --git a/app/soapbox/features/ui/components/tabs_bar.js b/app/soapbox/features/ui/components/tabs_bar.js index fda0e19cc..9bb512057 100644 --- a/app/soapbox/features/ui/components/tabs_bar.js +++ b/app/soapbox/features/ui/components/tabs_bar.js @@ -15,6 +15,7 @@ import Icon from '../../../components/icon'; import ThemeToggle from '../../ui/components/theme_toggle_container'; import { getSoapboxConfig } from 'soapbox/actions/soapbox'; import { isStaff } from 'soapbox/utils/accounts'; +import { getFeatures } from 'soapbox/utils/features'; const messages = defineMessages({ post: { id: 'tabs_bar.post', defaultMessage: 'Post' }, @@ -32,6 +33,7 @@ class TabsBar extends React.PureComponent { dashboardCount: PropTypes.number, notificationCount: PropTypes.number, chatsCount: PropTypes.number, + features: PropTypes.object.isRequired, } state = { @@ -52,7 +54,7 @@ class TabsBar extends React.PureComponent { } getNavLinks() { - const { intl: { formatMessage }, logo, account, dashboardCount, notificationCount, chatsCount } = this.props; + const { intl: { formatMessage }, logo, account, dashboardCount, notificationCount, chatsCount, features } = this.props; const links = []; if (logo) { links.push( @@ -73,7 +75,7 @@ class TabsBar extends React.PureComponent { ); } - if (account) { + if (features.chats && account) { links.push( @@ -155,12 +157,15 @@ const mapStateToProps = state => { const me = state.get('me'); const reportsCount = state.getIn(['admin', 'openReports']).count(); const approvalCount = state.getIn(['admin', 'awaitingApproval']).count(); + const instance = state.get('instance'); + return { account: state.getIn(['accounts', me]), logo: getSoapboxConfig(state).get('logo'), notificationCount: state.getIn(['notifications', 'unread']), chatsCount: state.get('chats').reduce((acc, curr) => acc + Math.min(curr.get('unread', 0), 1), 0), dashboardCount: reportsCount + approvalCount, + features: getFeatures(instance), }; }; diff --git a/app/soapbox/features/ui/index.js b/app/soapbox/features/ui/index.js index bc963e028..45d77a466 100644 --- a/app/soapbox/features/ui/index.js +++ b/app/soapbox/features/ui/index.js @@ -42,6 +42,7 @@ import Icon from 'soapbox/components/icon'; import { isStaff, isAdmin } from 'soapbox/utils/accounts'; import ProfileHoverCard from 'soapbox/components/profile_hover_card'; import { getAccessToken } from 'soapbox/utils/auth'; +import { getFeatures } from 'soapbox/utils/features'; import { Status, @@ -115,6 +116,7 @@ const messages = defineMessages({ const mapStateToProps = state => { const me = state.get('me'); const account = state.getIn(['accounts', me]); + const instance = state.get('instance'); return { dropdownMenuIsOpen: state.getIn(['dropdown_menu', 'openId']) !== null, @@ -122,6 +124,7 @@ const mapStateToProps = state => { streamingUrl: state.getIn(['instance', 'urls', 'streaming_api']), me, account, + features: getFeatures(instance), }; }; @@ -302,6 +305,7 @@ class UI extends React.PureComponent { me: SoapboxPropTypes.me, streamingUrl: PropTypes.string, account: PropTypes.object, + features: PropTypes.object.isRequired, }; state = { @@ -411,7 +415,7 @@ class UI extends React.PureComponent { }); componentDidMount() { - const { account } = this.props; + const { account, features } = this.props; if (!account) return; window.addEventListener('resize', this.handleResize, { passive: true }); @@ -432,7 +436,10 @@ class UI extends React.PureComponent { if (account) { this.props.dispatch(expandHomeTimeline()); this.props.dispatch(expandNotifications()); - this.props.dispatch(fetchChats()); + + if (features.chats) { + this.props.dispatch(fetchChats()); + } if (isStaff(account)) { this.props.dispatch(fetchReports({ state: 'open' })); @@ -577,7 +584,7 @@ class UI extends React.PureComponent { } render() { - const { streamingUrl } = this.props; + const { streamingUrl, features } = this.props; const { draggingOver, mobile } = this.state; const { intl, children, location, dropdownMenuIsOpen, me } = this.props; @@ -635,7 +642,7 @@ class UI extends React.PureComponent { {me && } - {me && !mobile && ( + {me && features.chats && !mobile && ( {Component => } diff --git a/app/soapbox/utils/features.js b/app/soapbox/utils/features.js index f89652219..9b520c78c 100644 --- a/app/soapbox/utils/features.js +++ b/app/soapbox/utils/features.js @@ -16,6 +16,7 @@ export const getFeatures = createSelector([ focalPoint: v.software === 'Mastodon' && gte(v.compatVersion, '2.3.0'), importMutes: v.software === 'Pleroma' && gte(v.version, '2.2.0'), emailList: f.includes('email_list'), + chats: v.software === 'Pleroma' && gte(v.version, '2.1.0'), }; });