diff --git a/app/javascript/flavours/glitch/actions/boosts.js b/app/javascript/flavours/glitch/actions/boosts.js
new file mode 100644
index 0000000000..6e14065d6f
--- /dev/null
+++ b/app/javascript/flavours/glitch/actions/boosts.js
@@ -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,
+ });
+ };
+}
diff --git a/app/javascript/flavours/glitch/actions/interactions.js b/app/javascript/flavours/glitch/actions/interactions.js
index 4407f8b6ea..336c8fa907 100644
--- a/app/javascript/flavours/glitch/actions/interactions.js
+++ b/app/javascript/flavours/glitch/actions/interactions.js
@@ -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));
diff --git a/app/javascript/flavours/glitch/components/dropdown_menu.js b/app/javascript/flavours/glitch/components/dropdown_menu.js
index d1aba691ca..023fecb9ab 100644
--- a/app/javascript/flavours/glitch/components/dropdown_menu.js
+++ b/app/javascript/flavours/glitch/components/dropdown_menu.js
@@ -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,
diff --git a/app/javascript/flavours/glitch/containers/dropdown_menu_container.js b/app/javascript/flavours/glitch/containers/dropdown_menu_container.js
index e60ee814d8..1c0385b740 100644
--- a/app/javascript/flavours/glitch/containers/dropdown_menu_container.js
+++ b/app/javascript/flavours/glitch/containers/dropdown_menu_container.js
@@ -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']),
diff --git a/app/javascript/flavours/glitch/containers/status_container.js b/app/javascript/flavours/glitch/containers/status_container.js
index 7782246a60..6461bf8050 100644
--- a/app/javascript/flavours/glitch/containers/status_container.js
+++ b/app/javascript/flavours/glitch/containers/status_container.js
@@ -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 }));
}
});
},
diff --git a/app/javascript/flavours/glitch/features/compose/components/dropdown.js b/app/javascript/flavours/glitch/features/compose/components/dropdown.js
index 04ef3964b2..abf7cbba14 100644
--- a/app/javascript/flavours/glitch/features/compose/components/dropdown.js
+++ b/app/javascript/flavours/glitch/features/compose/components/dropdown.js
@@ -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}
>
);
diff --git a/app/javascript/flavours/glitch/features/picture_in_picture/components/footer.js b/app/javascript/flavours/glitch/features/picture_in_picture/components/footer.js
index 2ddba140e2..d8989ec61b 100644
--- a/app/javascript/flavours/glitch/features/picture_in_picture/components/footer.js
+++ b/app/javascript/flavours/glitch/features/picture_in_picture/components/footer.js
@@ -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 }));
}
};
diff --git a/app/javascript/flavours/glitch/features/status/containers/detailed_status_container.js b/app/javascript/flavours/glitch/features/status/containers/detailed_status_container.js
index 9d11f37e05..40e186569e 100644
--- a/app/javascript/flavours/glitch/features/status/containers/detailed_status_container.js
+++ b/app/javascript/flavours/glitch/features/status/containers/detailed_status_container.js
@@ -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 }));
}
}
},
diff --git a/app/javascript/flavours/glitch/features/status/index.js b/app/javascript/flavours/glitch/features/status/index.js
index b330adf3ff..21e4414070 100644
--- a/app/javascript/flavours/glitch/features/status/index.js
+++ b/app/javascript/flavours/glitch/features/status/index.js
@@ -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 }));
}
}
diff --git a/app/javascript/flavours/glitch/features/ui/components/boost_modal.js b/app/javascript/flavours/glitch/features/ui/components/boost_modal.js
index 12ad426c84..c4af25599a 100644
--- a/app/javascript/flavours/glitch/features/ui/components/boost_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/boost_modal.js
@@ -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 {
Shift + }} />
}
+
+ {status.get('visibility') !== 'private' && !status.get('reblogged') && (
+
+ )}
diff --git a/app/javascript/flavours/glitch/reducers/boosts.js b/app/javascript/flavours/glitch/reducers/boosts.js
new file mode 100644
index 0000000000..3541ca0c28
--- /dev/null
+++ b/app/javascript/flavours/glitch/reducers/boosts.js
@@ -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;
+ }
+}
diff --git a/app/javascript/flavours/glitch/reducers/index.js b/app/javascript/flavours/glitch/reducers/index.js
index b1ddb769e5..c452e834cc 100644
--- a/app/javascript/flavours/glitch/reducers/index.js
+++ b/app/javascript/flavours/glitch/reducers/index.js
@@ -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,
diff --git a/app/javascript/flavours/glitch/styles/components/modal.scss b/app/javascript/flavours/glitch/styles/components/modal.scss
index 16c05bc3ee..0bda305a8b 100644
--- a/app/javascript/flavours/glitch/styles/components/modal.scss
+++ b/app/javascript/flavours/glitch/styles/components/modal.scss
@@ -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;
+}