mirror of
https://github.com/lunaisnotaboy/mastodon.git
synced 2024-12-19 19:56:56 +00:00
[Glitch] Add dropdown for boost privacy in boost confirmation modal
Port 07b46cb332
to glitch-soc
Signed-off-by: Claire <claire.github-309c@sitedethib.com>
This commit is contained in:
parent
9213b02656
commit
49eef466b8
29
app/javascript/flavours/glitch/actions/boosts.js
Normal file
29
app/javascript/flavours/glitch/actions/boosts.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
import { openModal } from './modal';
|
||||
|
||||
export const BOOSTS_INIT_MODAL = 'BOOSTS_INIT_MODAL';
|
||||
export const BOOSTS_CHANGE_PRIVACY = 'BOOSTS_CHANGE_PRIVACY';
|
||||
|
||||
export function initBoostModal(props) {
|
||||
return (dispatch, getState) => {
|
||||
const default_privacy = getState().getIn(['compose', 'default_privacy']);
|
||||
|
||||
const privacy = props.status.get('visibility') === 'private' ? 'private' : default_privacy;
|
||||
|
||||
dispatch({
|
||||
type: BOOSTS_INIT_MODAL,
|
||||
privacy
|
||||
});
|
||||
|
||||
dispatch(openModal('BOOST', props));
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export function changeBoostPrivacy(privacy) {
|
||||
return dispatch => {
|
||||
dispatch({
|
||||
type: BOOSTS_CHANGE_PRIVACY,
|
||||
privacy,
|
||||
});
|
||||
};
|
||||
}
|
|
@ -41,11 +41,11 @@ export const UNBOOKMARK_REQUEST = 'UNBOOKMARKED_REQUEST';
|
|||
export const UNBOOKMARK_SUCCESS = 'UNBOOKMARKED_SUCCESS';
|
||||
export const UNBOOKMARK_FAIL = 'UNBOOKMARKED_FAIL';
|
||||
|
||||
export function reblog(status) {
|
||||
export function reblog(status, visibility) {
|
||||
return function (dispatch, getState) {
|
||||
dispatch(reblogRequest(status));
|
||||
|
||||
api(getState).post(`/api/v1/statuses/${status.get('id')}/reblog`).then(function (response) {
|
||||
api(getState).post(`/api/v1/statuses/${status.get('id')}/reblog`, { visibility }).then(function (response) {
|
||||
// The reblog API method returns a new status wrapped around the original. In this case we are only
|
||||
// interested in how the original is modified, hence passing it skipping the wrapper
|
||||
dispatch(importFetchedStatus(response.data.reblog));
|
||||
|
|
|
@ -177,7 +177,6 @@ export default class Dropdown extends React.PureComponent {
|
|||
disabled: PropTypes.bool,
|
||||
status: ImmutablePropTypes.map,
|
||||
isUserTouching: PropTypes.func,
|
||||
isModalOpen: PropTypes.bool.isRequired,
|
||||
onOpen: PropTypes.func.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
dropdownPlacement: PropTypes.string,
|
||||
|
|
|
@ -5,7 +5,6 @@ import DropdownMenu from 'flavours/glitch/components/dropdown_menu';
|
|||
import { isUserTouching } from 'flavours/glitch/util/is_mobile';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
isModalOpen: state.get('modal').modalType === 'ACTIONS',
|
||||
dropdownPlacement: state.getIn(['dropdown_menu', 'placement']),
|
||||
openDropdownId: state.getIn(['dropdown_menu', 'openId']),
|
||||
openedViaKeyboard: state.getIn(['dropdown_menu', 'keyboard']),
|
||||
|
|
|
@ -21,6 +21,7 @@ import { muteStatus, unmuteStatus, deleteStatus } from 'flavours/glitch/actions/
|
|||
import { initMuteModal } from 'flavours/glitch/actions/mutes';
|
||||
import { initBlockModal } from 'flavours/glitch/actions/blocks';
|
||||
import { initReport } from 'flavours/glitch/actions/reports';
|
||||
import { initBoostModal } from 'flavours/glitch/actions/boosts';
|
||||
import { openModal } from 'flavours/glitch/actions/modal';
|
||||
import { deployPictureInPicture } from 'flavours/glitch/actions/picture_in_picture';
|
||||
import { changeLocalSetting } from 'flavours/glitch/actions/local_settings';
|
||||
|
@ -96,11 +97,11 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
|
|||
});
|
||||
},
|
||||
|
||||
onModalReblog (status) {
|
||||
onModalReblog (status, privacy) {
|
||||
if (status.get('reblogged')) {
|
||||
dispatch(unreblog(status));
|
||||
} else {
|
||||
dispatch(reblog(status));
|
||||
dispatch(reblog(status, privacy));
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -108,11 +109,11 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
|
|||
dispatch((_, getState) => {
|
||||
let state = getState();
|
||||
if (state.getIn(['local_settings', 'confirm_boost_missing_media_description']) && status.get('media_attachments').some(item => !item.get('description')) && !status.get('reblogged')) {
|
||||
dispatch(openModal('BOOST', { status, onReblog: this.onModalReblog, missingMediaDescription: true }));
|
||||
dispatch(initBoostModal({ status, onReblog: this.onModalReblog, missingMediaDescription: true }));
|
||||
} else if (e.shiftKey || !boostModal) {
|
||||
this.onModalReblog(status);
|
||||
} else {
|
||||
dispatch(openModal('BOOST', { status, onReblog: this.onModalReblog }));
|
||||
dispatch(initBoostModal({ status, onReblog: this.onModalReblog }));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
|
@ -31,6 +31,8 @@ export default class ComposerOptionsDropdown extends React.PureComponent {
|
|||
title: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
onChange: PropTypes.func,
|
||||
noModal: PropTypes.bool,
|
||||
container: PropTypes.func,
|
||||
};
|
||||
|
||||
state = {
|
||||
|
@ -42,10 +44,10 @@ export default class ComposerOptionsDropdown extends React.PureComponent {
|
|||
|
||||
// Toggles opening and closing the dropdown.
|
||||
handleToggle = ({ target, type }) => {
|
||||
const { onModalOpen } = this.props;
|
||||
const { onModalOpen, noModal } = this.props;
|
||||
const { open } = this.state;
|
||||
|
||||
if (isUserTouching()) {
|
||||
if (!noModal && isUserTouching()) {
|
||||
if (this.state.open) {
|
||||
this.props.onModalClose();
|
||||
} else {
|
||||
|
@ -183,6 +185,7 @@ export default class ComposerOptionsDropdown extends React.PureComponent {
|
|||
items,
|
||||
onChange,
|
||||
value,
|
||||
container,
|
||||
} = this.props;
|
||||
const { open, placement } = this.state;
|
||||
const computedClass = classNames('composer--options--dropdown', {
|
||||
|
@ -219,6 +222,7 @@ export default class ComposerOptionsDropdown extends React.PureComponent {
|
|||
placement={placement}
|
||||
show={open}
|
||||
target={this}
|
||||
container={container}
|
||||
>
|
||||
<DropdownMenu
|
||||
items={items}
|
||||
|
|
|
@ -53,12 +53,13 @@ class PrivacyDropdown extends React.PureComponent {
|
|||
value: PropTypes.string.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
noDirect: PropTypes.bool,
|
||||
noModal: PropTypes.bool,
|
||||
container: PropTypes.func,
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
render () {
|
||||
const { value, onChange, onModalOpen, onModalClose, disabled, intl } = this.props;
|
||||
const { value, onChange, onModalOpen, onModalClose, disabled, noDirect, noModal, container, intl } = this.props;
|
||||
|
||||
// We predefine our privacy items so that we can easily pick the
|
||||
// dropdown icon later.
|
||||
|
@ -89,7 +90,11 @@ class PrivacyDropdown extends React.PureComponent {
|
|||
},
|
||||
};
|
||||
|
||||
const items = [privacyItems.public, privacyItems.unlisted, privacyItems.private, privacyItems.direct];
|
||||
const items = [privacyItems.public, privacyItems.unlisted, privacyItems.private];
|
||||
|
||||
if (!noDirect) {
|
||||
items.push(privacyItems.direct);
|
||||
}
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
|
@ -100,6 +105,8 @@ class PrivacyDropdown extends React.PureComponent {
|
|||
onModalClose={onModalClose}
|
||||
onModalOpen={onModalOpen}
|
||||
title={intl.formatMessage(messages.change_privacy)}
|
||||
container={container}
|
||||
noModal={noModal}
|
||||
value={value}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -11,6 +11,7 @@ import { replyCompose } from 'flavours/glitch/actions/compose';
|
|||
import { reblog, favourite, unreblog, unfavourite } from 'flavours/glitch/actions/interactions';
|
||||
import { makeGetStatus } from 'flavours/glitch/selectors';
|
||||
import { openModal } from 'flavours/glitch/actions/modal';
|
||||
import { initBoostModal } from 'flavours/glitch/actions/boosts';
|
||||
|
||||
const messages = defineMessages({
|
||||
reply: { id: 'status.reply', defaultMessage: 'Reply' },
|
||||
|
@ -82,9 +83,9 @@ class Footer extends ImmutablePureComponent {
|
|||
}
|
||||
};
|
||||
|
||||
_performReblog = () => {
|
||||
_performReblog = (privacy) => {
|
||||
const { dispatch, status } = this.props;
|
||||
dispatch(reblog(status));
|
||||
dispatch(reblog(status, privacy));
|
||||
}
|
||||
|
||||
handleReblogClick = e => {
|
||||
|
@ -95,7 +96,7 @@ class Footer extends ImmutablePureComponent {
|
|||
} else if ((e && e.shiftKey) || !boostModal) {
|
||||
this._performReblog();
|
||||
} else {
|
||||
dispatch(openModal('BOOST', { status, onReblog: this._performReblog }));
|
||||
dispatch(initBoostModal({ status, onReblog: this._performReblog }));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ import {
|
|||
import { initMuteModal } from 'flavours/glitch/actions/mutes';
|
||||
import { initBlockModal } from 'flavours/glitch/actions/blocks';
|
||||
import { initReport } from 'flavours/glitch/actions/reports';
|
||||
import { initBoostModal } from 'flavours/glitch/actions/boosts';
|
||||
import { openModal } from 'flavours/glitch/actions/modal';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import { boostModal, deleteModal } from 'flavours/glitch/util/initial_state';
|
||||
|
@ -67,8 +68,8 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
|||
});
|
||||
},
|
||||
|
||||
onModalReblog (status) {
|
||||
dispatch(reblog(status));
|
||||
onModalReblog (status, privacy) {
|
||||
dispatch(reblog(status, privacy));
|
||||
},
|
||||
|
||||
onReblog (status, e) {
|
||||
|
@ -78,7 +79,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
|||
if (e.shiftKey || !boostModal) {
|
||||
this.onModalReblog(status);
|
||||
} else {
|
||||
dispatch(openModal('BOOST', { status, onReblog: this.onModalReblog }));
|
||||
dispatch(initBoostModal({ status, onReblog: this.onModalReblog }));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -30,6 +30,7 @@ import { muteStatus, unmuteStatus, deleteStatus } from 'flavours/glitch/actions/
|
|||
import { initMuteModal } from 'flavours/glitch/actions/mutes';
|
||||
import { initBlockModal } from 'flavours/glitch/actions/blocks';
|
||||
import { initReport } from 'flavours/glitch/actions/reports';
|
||||
import { initBoostModal } from 'flavours/glitch/actions/boosts';
|
||||
import { makeGetStatus } from 'flavours/glitch/selectors';
|
||||
import { ScrollContainer } from 'react-router-scroll-4';
|
||||
import ColumnBackButton from 'flavours/glitch/components/column_back_button';
|
||||
|
@ -262,13 +263,13 @@ class Status extends ImmutablePureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
handleModalReblog = (status) => {
|
||||
handleModalReblog = (status, privacy) => {
|
||||
const { dispatch } = this.props;
|
||||
|
||||
if (status.get('reblogged')) {
|
||||
dispatch(unreblog(status));
|
||||
} else {
|
||||
dispatch(reblog(status));
|
||||
dispatch(reblog(status, privacy));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -276,11 +277,11 @@ class Status extends ImmutablePureComponent {
|
|||
const { settings, dispatch } = this.props;
|
||||
|
||||
if (settings.get('confirm_boost_missing_media_description') && status.get('media_attachments').some(item => !item.get('description')) && !status.get('reblogged')) {
|
||||
dispatch(openModal('BOOST', { status, onReblog: this.handleModalReblog, missingMediaDescription: true }));
|
||||
dispatch(initBoostModal({ status, onReblog: this.handleModalReblog, missingMediaDescription: true }));
|
||||
} else if ((e && e.shiftKey) || !boostModal) {
|
||||
this.handleModalReblog(status);
|
||||
} else {
|
||||
dispatch(openModal('BOOST', { status, onReblog: this.handleModalReblog }));
|
||||
dispatch(initBoostModal({ status, onReblog: this.handleModalReblog }));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import PropTypes from 'prop-types';
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
|
@ -10,7 +11,9 @@ import DisplayName from 'flavours/glitch/components/display_name';
|
|||
import AttachmentList from 'flavours/glitch/components/attachment_list';
|
||||
import Icon from 'flavours/glitch/components/icon';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import PrivacyDropdown from 'flavours/glitch/features/compose/components/privacy_dropdown';
|
||||
import classNames from 'classnames';
|
||||
import { changeBoostPrivacy } from 'flavours/glitch/actions/boosts';
|
||||
|
||||
const messages = defineMessages({
|
||||
cancel_reblog: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
|
||||
|
@ -21,7 +24,22 @@ const messages = defineMessages({
|
|||
direct_short: { id: 'privacy.direct.short', defaultMessage: 'Direct' },
|
||||
});
|
||||
|
||||
export default @injectIntl
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
privacy: state.getIn(['boosts', 'new', 'privacy']),
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
onChangeBoostPrivacy(value) {
|
||||
dispatch(changeBoostPrivacy(value));
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default @connect(mapStateToProps, mapDispatchToProps)
|
||||
@injectIntl
|
||||
class BoostModal extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
|
@ -41,7 +59,7 @@ class BoostModal extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
handleReblog = () => {
|
||||
this.props.onReblog(this.props.status);
|
||||
this.props.onReblog(this.props.status, this.props.privacy);
|
||||
this.props.onClose();
|
||||
}
|
||||
|
||||
|
@ -55,12 +73,16 @@ class BoostModal extends ImmutablePureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
_findContainer = () => {
|
||||
return document.getElementsByClassName('modal-root__container')[0];
|
||||
};
|
||||
|
||||
setRef = (c) => {
|
||||
this.button = c;
|
||||
}
|
||||
|
||||
render () {
|
||||
const { status, missingMediaDescription, intl } = this.props;
|
||||
const { status, missingMediaDescription, privacy, intl } = this.props;
|
||||
const buttonText = status.get('reblogged') ? messages.cancel_reblog : messages.reblog;
|
||||
|
||||
const visibilityIconInfo = {
|
||||
|
@ -111,6 +133,15 @@ class BoostModal extends ImmutablePureComponent {
|
|||
<FormattedMessage id='boost_modal.combo' defaultMessage='You can press {combo} to skip this next time' values={{ combo: <span>Shift + <Icon id='retweet' /></span> }} />
|
||||
}
|
||||
</div>
|
||||
|
||||
{status.get('visibility') !== 'private' && !status.get('reblogged') && (
|
||||
<PrivacyDropdown
|
||||
noDirect
|
||||
value={privacy}
|
||||
container={this._findContainer}
|
||||
onChange={this.props.onChangeBoostPrivacy}
|
||||
/>
|
||||
)}
|
||||
<Button text={intl.formatMessage(buttonText)} onClick={this.handleReblog} ref={this.setRef} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
25
app/javascript/flavours/glitch/reducers/boosts.js
Normal file
25
app/javascript/flavours/glitch/reducers/boosts.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
import Immutable from 'immutable';
|
||||
|
||||
import {
|
||||
BOOSTS_INIT_MODAL,
|
||||
BOOSTS_CHANGE_PRIVACY,
|
||||
} from 'flavours/glitch/actions/boosts';
|
||||
|
||||
const initialState = Immutable.Map({
|
||||
new: Immutable.Map({
|
||||
privacy: 'public',
|
||||
}),
|
||||
});
|
||||
|
||||
export default function mutes(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case BOOSTS_INIT_MODAL:
|
||||
return state.withMutations((state) => {
|
||||
state.setIn(['new', 'privacy'], action.privacy);
|
||||
});
|
||||
case BOOSTS_CHANGE_PRIVACY:
|
||||
return state.setIn(['new', 'privacy'], action.privacy);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@ import status_lists from './status_lists';
|
|||
import mutes from './mutes';
|
||||
import blocks from './blocks';
|
||||
import reports from './reports';
|
||||
import boosts from './boosts';
|
||||
import contexts from './contexts';
|
||||
import compose from './compose';
|
||||
import search from './search';
|
||||
|
@ -61,6 +62,7 @@ const reducers = {
|
|||
mutes,
|
||||
blocks,
|
||||
reports,
|
||||
boosts,
|
||||
contexts,
|
||||
compose,
|
||||
search,
|
||||
|
|
|
@ -1031,3 +1031,12 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal-root__container .composer--options--dropdown {
|
||||
flex-grow: 0;
|
||||
}
|
||||
|
||||
.modal-root__container .composer--options--dropdown--content {
|
||||
pointer-events: auto;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue