From fc3b9d62a6baac2a902a9e5ebef7c28e945bcca5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Thu, 30 Dec 2021 16:13:45 +0100 Subject: [PATCH] Support account subscriptions on Mastodon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- app/soapbox/actions/accounts.js | 4 ++-- .../features/account/components/header.js | 20 ++++++++++++++++--- .../account_timeline/components/header.js | 5 +++++ .../containers/header_container.js | 12 +++++++++-- .../ui/components/subscription_button.js | 17 +++++++++++++--- app/soapbox/utils/features.js | 4 ++++ 6 files changed, 52 insertions(+), 10 deletions(-) diff --git a/app/soapbox/actions/accounts.js b/app/soapbox/actions/accounts.js index 0aabbb42b..f0db19a5e 100644 --- a/app/soapbox/actions/accounts.js +++ b/app/soapbox/actions/accounts.js @@ -203,7 +203,7 @@ export function fetchAccountFail(id, error) { }; } -export function followAccount(id, reblogs = true) { +export function followAccount(id, options = { reblogs: true }) { return (dispatch, getState) => { if (!isLoggedIn(getState)) return; @@ -212,7 +212,7 @@ export function followAccount(id, reblogs = true) { dispatch(followAccountRequest(id, locked)); - api(getState).post(`/api/v1/accounts/${id}/follow`, { reblogs }).then(response => { + api(getState).post(`/api/v1/accounts/${id}/follow`, options).then(response => { dispatch(followAccountSuccess(response.data, alreadyFollowing)); }).catch(error => { dispatch(followAccountFail(error, locked)); diff --git a/app/soapbox/features/account/components/header.js b/app/soapbox/features/account/components/header.js index a27271888..cc99b2689 100644 --- a/app/soapbox/features/account/components/header.js +++ b/app/soapbox/features/account/components/header.js @@ -256,7 +256,21 @@ class Header extends ImmutablePureComponent { }); } - if (features.accountSubscriptions) { + if (features.accountNotifies) { + if (account.getIn(['relationship', 'notifying'])) { + menu.push({ + text: intl.formatMessage(messages.unsubscribe, { name: account.get('username') }), + action: this.props.onNotifyToggle, + icon: require('@tabler/icons/icons/bell.svg'), + }); + } else { + menu.push({ + text: intl.formatMessage(messages.subscribe, { name: account.get('username') }), + action: this.props.onNotifyToggle, + icon: require('@tabler/icons/icons/bell-off.svg'), + }); + } + } else if (features.accountSubscriptions) { if (account.getIn(['relationship', 'subscribing'])) { menu.push({ text: intl.formatMessage(messages.unsubscribe, { name: account.get('username') }), @@ -550,8 +564,8 @@ class Header extends ImmutablePureComponent { } - {features.accountSubscriptions &&
- + {(features.accountNotifies || features.accountSubscriptions) &&
+
}
diff --git a/app/soapbox/features/account_timeline/components/header.js b/app/soapbox/features/account_timeline/components/header.js index e2b9d3207..c81c31538 100644 --- a/app/soapbox/features/account_timeline/components/header.js +++ b/app/soapbox/features/account_timeline/components/header.js @@ -57,6 +57,10 @@ export default class Header extends ImmutablePureComponent { this.props.onSubscriptionToggle(this.props.account); } + handleNotifyToggle = () => { + this.props.onNotifyToggle(this.props.account); + } + handleMute = () => { this.props.onMute(this.props.account); } @@ -143,6 +147,7 @@ export default class Header extends ImmutablePureComponent { onChat={this.handleChat} onReblogToggle={this.handleReblogToggle} onSubscriptionToggle={this.handleSubscriptionToggle} + onNotifyToggle={this.handleNotifyToggle} onReport={this.handleReport} onMute={this.handleMute} onBlockDomain={this.handleBlockDomain} diff --git a/app/soapbox/features/account_timeline/containers/header_container.js b/app/soapbox/features/account_timeline/containers/header_container.js index 7513232cd..4030822bd 100644 --- a/app/soapbox/features/account_timeline/containers/header_container.js +++ b/app/soapbox/features/account_timeline/containers/header_container.js @@ -112,9 +112,9 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ onReblogToggle(account) { if (account.getIn(['relationship', 'showing_reblogs'])) { - dispatch(followAccount(account.get('id'), false)); + dispatch(followAccount(account.get('id'), { reblogs: false })); } else { - dispatch(followAccount(account.get('id'), true)); + dispatch(followAccount(account.get('id'), { reblogs: true })); } }, @@ -126,6 +126,14 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ } }, + onNotifyToggle(account) { + if (account.getIn(['relationship', 'notifying'])) { + dispatch(followAccount(account.get('id'), { notify: false })); + } else { + dispatch(followAccount(account.get('id'), { notify: true })); + } + }, + // onEndorseToggle(account) { // if (account.getIn(['relationship', 'endorsed'])) { // dispatch(unpinAccount(account.get('id'))); diff --git a/app/soapbox/features/ui/components/subscription_button.js b/app/soapbox/features/ui/components/subscription_button.js index 131f55b9a..137742022 100644 --- a/app/soapbox/features/ui/components/subscription_button.js +++ b/app/soapbox/features/ui/components/subscription_button.js @@ -1,5 +1,6 @@ import React from 'react'; import { connect } from 'react-redux'; +import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import { defineMessages, injectIntl } from 'react-intl'; import classNames from 'classnames'; @@ -7,6 +8,7 @@ import Button from 'soapbox/components/button'; import Icon from 'soapbox/components/icon'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { + followAccount, subscribeAccount, unsubscribeAccount, } from 'soapbox/actions/accounts'; @@ -32,6 +34,13 @@ const mapDispatchToProps = (dispatch) => ({ dispatch(subscribeAccount(account.get('id'))); } }, + onNotifyToggle(account) { + if (account.getIn(['relationship', 'notifying'])) { + dispatch(followAccount(account.get('id'), { notify: false })); + } else { + dispatch(followAccount(account.get('id'), { notify: true })); + } + }, }); export default @connect(mapStateToProps, mapDispatchToProps) @@ -40,15 +49,17 @@ class SubscriptionButton extends ImmutablePureComponent { static propTypes = { account: ImmutablePropTypes.map, + features: PropTypes.object.isRequired, }; handleSubscriptionToggle = () => { - this.props.onSubscriptionToggle(this.props.account); + if (this.props.features.accountNotifies) this.props.onNotifyToggle(this.props.account); + else this.props.onSubscriptionToggle(this.props.account); } render() { - const { account, intl } = this.props; - const subscribing = account.getIn(['relationship', 'subscribing']); + const { account, intl, features } = this.props; + const subscribing = features.accountNotifies ? account.getIn(['relationship', 'notifying']) : account.getIn(['relationship', 'subscribing']); const following = account.getIn(['relationship', 'following']); const requested = account.getIn(['relationship', 'requested']); diff --git a/app/soapbox/utils/features.js b/app/soapbox/utils/features.js index 6447f53f7..3c2e15346 100644 --- a/app/soapbox/utils/features.js +++ b/app/soapbox/utils/features.js @@ -64,6 +64,10 @@ export const getFeatures = createSelector([ resetPasswordAPI: v.software === PLEROMA, exposableReactions: features.includes('exposable_reactions'), accountSubscriptions: v.software === PLEROMA && gte(v.version, '1.0.0'), + accountNotifies: any([ + v.software === MASTODON && gte(v.compatVersion, '3.3.0'), + v.software === PLEROMA && gte(v.version, '2.4.50'), + ]), unrestrictedLists: v.software === PLEROMA, accountByUsername: v.software === PLEROMA, profileDirectory: any([