From 2277913f3f01d3bdb9a1661f019221b1cb185fbb Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 26 Oct 2022 19:35:55 +0200 Subject: [PATCH] Add closed registrations modal (#19437) --- .../closed_registrations_modal/index.js | 75 +++++++++++++++++++ .../features/interaction_modal/index.js | 33 +++++++- .../features/ui/components/modal_root.js | 2 + .../features/ui/components/sign_in_banner.js | 43 +++++++++-- .../features/ui/util/async-components.js | 4 + app/serializers/rest/instance_serializer.rb | 9 ++- 6 files changed, 155 insertions(+), 11 deletions(-) create mode 100644 app/javascript/mastodon/features/closed_registrations_modal/index.js diff --git a/app/javascript/mastodon/features/closed_registrations_modal/index.js b/app/javascript/mastodon/features/closed_registrations_modal/index.js new file mode 100644 index 00000000000..062045b837e --- /dev/null +++ b/app/javascript/mastodon/features/closed_registrations_modal/index.js @@ -0,0 +1,75 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { FormattedMessage } from 'react-intl'; +import ImmutablePureComponent from 'react-immutable-pure-component'; +import { domain } from 'mastodon/initial_state'; +import { fetchServer } from 'mastodon/actions/server'; + +const mapStateToProps = state => ({ + closed_registrations_message: state.getIn(['server', 'server', 'registrations', 'closed_registrations_message']), +}); + +export default @connect(mapStateToProps) +class ClosedRegistrationsModal extends ImmutablePureComponent { + + componentDidMount () { + const { dispatch } = this.props; + dispatch(fetchServer()); + } + + render () { + let closedRegistrationsMessage; + + if (this.props.closed_registrations_message) { + closedRegistrationsMessage = ( +

+ ); + } else { + closedRegistrationsMessage = ( +

+ {domain} }} + /> +

+ ); + } + + return ( +
+
+

+

+ +

+
+ +
+
+

+ {closedRegistrationsMessage} +
+ +
+

+

+ +

+ +
+
+
+ ); + } + +}; diff --git a/app/javascript/mastodon/features/interaction_modal/index.js b/app/javascript/mastodon/features/interaction_modal/index.js index 6a7aa9c1c72..9f774790336 100644 --- a/app/javascript/mastodon/features/interaction_modal/index.js +++ b/app/javascript/mastodon/features/interaction_modal/index.js @@ -5,11 +5,19 @@ import { registrationsOpen } from 'mastodon/initial_state'; import { connect } from 'react-redux'; import Icon from 'mastodon/components/icon'; import classNames from 'classnames'; +import { openModal, closeModal } from 'mastodon/actions/modal'; const mapStateToProps = (state, { accountId }) => ({ displayNameHtml: state.getIn(['accounts', accountId, 'display_name_html']), }); +const mapDispatchToProps = (dispatch) => ({ + onSignupClick() { + dispatch(closeModal()); + dispatch(openModal('CLOSED_REGISTRATIONS')); + }, +}); + class Copypaste extends React.PureComponent { static propTypes = { @@ -66,15 +74,20 @@ class Copypaste extends React.PureComponent { } -export default @connect(mapStateToProps) +export default @connect(mapStateToProps, mapDispatchToProps) class InteractionModal extends React.PureComponent { static propTypes = { displayNameHtml: PropTypes.string, url: PropTypes.string, type: PropTypes.oneOf(['reply', 'reblog', 'favourite', 'follow']), + onSignupClick: PropTypes.func.isRequired, }; + handleSignupClick = () => { + this.props.onSignupClick(); + } + render () { const { url, type, displayNameHtml } = this.props; @@ -105,6 +118,22 @@ class InteractionModal extends React.PureComponent { break; } + let signupButton; + + if (registrationsOpen) { + signupButton = ( + + + + ); + } else { + signupButton = ( + + ); + } + return (
@@ -116,7 +145,7 @@ class InteractionModal extends React.PureComponent {

- + {signupButton}
diff --git a/app/javascript/mastodon/features/ui/components/modal_root.js b/app/javascript/mastodon/features/ui/components/modal_root.js index 2224a82077b..484ebbd8b63 100644 --- a/app/javascript/mastodon/features/ui/components/modal_root.js +++ b/app/javascript/mastodon/features/ui/components/modal_root.js @@ -23,6 +23,7 @@ import { FilterModal, InteractionModal, SubscribedLanguagesModal, + ClosedRegistrationsModal, } from 'mastodon/features/ui/util/async-components'; import { Helmet } from 'react-helmet'; @@ -44,6 +45,7 @@ const MODAL_COMPONENTS = { 'FILTER': FilterModal, 'SUBSCRIBED_LANGUAGES': SubscribedLanguagesModal, 'INTERACTION': InteractionModal, + 'CLOSED_REGISTRATIONS': ClosedRegistrationsModal, }; export default class ModalRoot extends React.PureComponent { diff --git a/app/javascript/mastodon/features/ui/components/sign_in_banner.js b/app/javascript/mastodon/features/ui/components/sign_in_banner.js index 5ff4ee2a868..8bd32edf907 100644 --- a/app/javascript/mastodon/features/ui/components/sign_in_banner.js +++ b/app/javascript/mastodon/features/ui/components/sign_in_banner.js @@ -1,13 +1,40 @@ -import React from 'react'; +import React, { useCallback } from 'react'; import { FormattedMessage } from 'react-intl'; +import { useDispatch } from 'react-redux'; import { registrationsOpen } from 'mastodon/initial_state'; +import { openModal } from 'mastodon/actions/modal'; -const SignInBanner = () => ( -
-

- - -
-); +const SignInBanner = () => { + const dispatch = useDispatch(); + + const openClosedRegistrationsModal = useCallback( + () => dispatch(openModal('CLOSED_REGISTRATIONS')), + [dispatch], + ); + + let signupButton; + + if (registrationsOpen) { + signupButton = ( + + + + ); + } else { + signupButton = ( + + ); + } + + return ( +
+

+ + {signupButton} +
+ ); +}; export default SignInBanner; diff --git a/app/javascript/mastodon/features/ui/util/async-components.js b/app/javascript/mastodon/features/ui/util/async-components.js index 7686a69ea57..6046578de47 100644 --- a/app/javascript/mastodon/features/ui/util/async-components.js +++ b/app/javascript/mastodon/features/ui/util/async-components.js @@ -174,6 +174,10 @@ export function SubscribedLanguagesModal () { return import(/*webpackChunkName: "modals/subscribed_languages_modal" */'../../subscribed_languages_modal'); } +export function ClosedRegistrationsModal () { + return import(/*webpackChunkName: "modals/closed_registrations_modal" */'../../closed_registrations_modal'); +} + export function About () { return import(/*webpackChunkName: "features/about" */'../../about'); } diff --git a/app/serializers/rest/instance_serializer.rb b/app/serializers/rest/instance_serializer.rb index 606e7d8311e..2a4da8c3b3d 100644 --- a/app/serializers/rest/instance_serializer.rb +++ b/app/serializers/rest/instance_serializer.rb @@ -81,8 +81,15 @@ class REST::InstanceSerializer < ActiveModel::Serializer def registrations { - enabled: Setting.registrations_mode != 'none' && !Rails.configuration.x.single_user_mode, + enabled: registrations_enabled?, approval_required: Setting.registrations_mode == 'approved', + closed_registrations_message: registrations_enabled? ? nil : Setting.closed_registrations_message, } end + + private + + def registrations_enabled? + Setting.registrations_mode != 'none' && !Rails.configuration.x.single_user_mode + end end