diff --git a/app/javascript/flavours/glitch/containers/dropdown_menu_container.js b/app/javascript/flavours/glitch/containers/dropdown_menu_container.js
index d18e640a4d..0c4a2b50ff 100644
--- a/app/javascript/flavours/glitch/containers/dropdown_menu_container.js
+++ b/app/javascript/flavours/glitch/containers/dropdown_menu_container.js
@@ -14,12 +14,7 @@ const mapDispatchToProps = (dispatch, { status, items, scrollKey }) => ({
onOpen(id, onItemClick, dropdownPlacement, keyboard) {
dispatch(isUserTouching() ? openModal('ACTIONS', {
status,
- actions: items.map(
- (item, i) => item ? {
- ...item,
- name: `${item.text}-${i}`,
- } : null
- ),
+ actions: items,
onClick: onItemClick,
}) : openDropdownMenu(id, dropdownPlacement, keyboard, scrollKey));
},
diff --git a/app/javascript/flavours/glitch/features/compose/components/dropdown.js b/app/javascript/flavours/glitch/features/compose/components/dropdown.js
index f8fe819a59..4708e2ecec 100644
--- a/app/javascript/flavours/glitch/features/compose/components/dropdown.js
+++ b/app/javascript/flavours/glitch/features/compose/components/dropdown.js
@@ -21,10 +21,9 @@ export default class ComposerOptionsDropdown extends React.PureComponent {
icon: PropTypes.string,
items: PropTypes.arrayOf(PropTypes.shape({
icon: PropTypes.string,
- meta: PropTypes.node,
+ meta: PropTypes.string,
name: PropTypes.string.isRequired,
- on: PropTypes.bool,
- text: PropTypes.node,
+ text: PropTypes.string,
})).isRequired,
onModalOpen: PropTypes.func,
onModalClose: PropTypes.func,
@@ -32,10 +31,15 @@ export default class ComposerOptionsDropdown extends React.PureComponent {
value: PropTypes.string,
onChange: PropTypes.func,
container: PropTypes.func,
+ renderItemContents: PropTypes.func,
+ closeOnChange: PropTypes.bool,
+ };
+
+ static defaultProps = {
+ closeOnChange: true,
};
state = {
- needsModalUpdate: false,
open: false,
openedViaKeyboard: undefined,
placement: 'bottom',
@@ -106,6 +110,23 @@ export default class ComposerOptionsDropdown extends React.PureComponent {
this.setState({ open: false });
}
+ handleItemClick = (e) => {
+ const {
+ items,
+ onChange,
+ onModalClose,
+ closeOnChange,
+ } = this.props;
+
+ const i = Number(e.currentTarget.getAttribute('data-index'));
+
+ const { name } = this.props.items[i];
+
+ e.preventDefault(); // Prevents focus from changing
+ if (closeOnChange) onModalClose();
+ onChange(name);
+ };
+
// Creates an action modal object.
handleMakeModal = () => {
const component = this;
@@ -124,6 +145,8 @@ export default class ComposerOptionsDropdown extends React.PureComponent {
// The object.
return {
+ renderItemContents: this.props.renderItemContents,
+ onClick: this.handleItemClick,
actions: items.map(
({
name,
@@ -132,48 +155,11 @@ export default class ComposerOptionsDropdown extends React.PureComponent {
...rest,
active: value && name === value,
name,
- onClick (e) {
- e.preventDefault(); // Prevents focus from changing
- onModalClose();
- onChange(name);
- },
- onPassiveClick (e) {
- e.preventDefault(); // Prevents focus from changing
- onChange(name);
- component.setState({ needsModalUpdate: true });
- },
})
),
};
}
- // If our modal is open and our props update, we need to also update
- // the modal.
- handleUpdate = () => {
- const { onModalOpen } = this.props;
- const { needsModalUpdate } = this.state;
-
- // Gets our modal object.
- const modal = this.handleMakeModal();
-
- // Reopens the modal with the new object.
- if (needsModalUpdate && modal && onModalOpen) {
- onModalOpen(modal);
- }
- }
-
- // Updates our modal as necessary.
- componentDidUpdate (prevProps) {
- const { items } = this.props;
- const { needsModalUpdate } = this.state;
- if (needsModalUpdate && items.find(
- (item, i) => item.on !== prevProps.items[i].on
- )) {
- this.handleUpdate();
- this.setState({ needsModalUpdate: false });
- }
- }
-
// Rendering.
render () {
const {
@@ -185,6 +171,8 @@ export default class ComposerOptionsDropdown extends React.PureComponent {
onChange,
value,
container,
+ renderItemContents,
+ closeOnChange,
} = this.props;
const { open, placement } = this.state;
const computedClass = classNames('composer--options--dropdown', {
@@ -225,10 +213,12 @@ export default class ComposerOptionsDropdown extends React.PureComponent {
>
diff --git a/app/javascript/flavours/glitch/features/compose/components/dropdown_menu.js b/app/javascript/flavours/glitch/features/compose/components/dropdown_menu.js
index 16eb1ef9d6..0649fe1caa 100644
--- a/app/javascript/flavours/glitch/features/compose/components/dropdown_menu.js
+++ b/app/javascript/flavours/glitch/features/compose/components/dropdown_menu.js
@@ -2,7 +2,6 @@
import PropTypes from 'prop-types';
import React from 'react';
import spring from 'react-motion/lib/spring';
-import Toggle from 'react-toggle';
import ImmutablePureComponent from 'react-immutable-pure-component';
import classNames from 'classnames';
@@ -28,18 +27,20 @@ export default class ComposerOptionsDropdownContent extends React.PureComponent
icon: PropTypes.string,
meta: PropTypes.node,
name: PropTypes.string.isRequired,
- on: PropTypes.bool,
text: PropTypes.node,
})),
onChange: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
style: PropTypes.object,
value: PropTypes.string,
+ renderItemContents: PropTypes.func,
openedViaKeyboard: PropTypes.bool,
+ closeOnChange: PropTypes.bool,
};
static defaultProps = {
style: {},
+ closeOnChange: true,
};
state = {
@@ -83,12 +84,13 @@ export default class ComposerOptionsDropdownContent extends React.PureComponent
const {
onChange,
onClose,
+ closeOnChange,
items,
} = this.props;
- const { on, name } = this.props.items[i];
+ const { name } = this.props.items[i];
e.preventDefault(); // Prevents change in focus on click
- if ((on === null || typeof on === 'undefined')) {
+ if (closeOnChange) {
onClose();
}
onChange(name);
@@ -150,18 +152,25 @@ export default class ComposerOptionsDropdownContent extends React.PureComponent
}
renderItem = (item, i) => {
- const { name, icon, meta, on, text } = item;
+ const { name, icon, meta, text } = item;
const active = (name === (this.props.value || this.state.value));
const computedClass = classNames('composer--options--dropdown--content--item', { active });
- let prefix = null;
+ let contents = this.props.renderItemContents && this.props.renderItemContents(item, i);
- if (on !== null && typeof on !== 'undefined') {
- prefix = ;
- } else if (icon) {
- prefix =
+ if (!contents) {
+ contents = (
+
+ {icon && }
+
+
+ {text}
+ {meta}
+
+
+ );
}
return (
@@ -175,12 +184,7 @@ export default class ComposerOptionsDropdownContent extends React.PureComponent
data-index={i}
ref={active ? this.setFocusRef : null}
>
- {prefix}
-
-
- {text}
- {meta}
-
+ {contents}
);
}
diff --git a/app/javascript/flavours/glitch/features/compose/components/options.js b/app/javascript/flavours/glitch/features/compose/components/options.js
index f9212bbaeb..d30fb2a98a 100644
--- a/app/javascript/flavours/glitch/features/compose/components/options.js
+++ b/app/javascript/flavours/glitch/features/compose/components/options.js
@@ -2,8 +2,10 @@
import PropTypes from 'prop-types';
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
-import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
+import { defineMessages, injectIntl } from 'react-intl';
import spring from 'react-motion/lib/spring';
+import Toggle from 'react-toggle';
+import { connect } from 'react-redux';
// Components.
import IconButton from 'flavours/glitch/components/icon_button';
@@ -80,6 +82,36 @@ const messages = defineMessages({
},
});
+@connect((state, { name }) => ({ checked: state.getIn(['compose', 'advanced_options', name]) }))
+class ToggleOption extends ImmutablePureComponent {
+
+ static propTypes = {
+ name: PropTypes.string.isRequired,
+ checked: PropTypes.bool,
+ onChangeAdvancedOption: PropTypes.func.isRequired,
+ };
+
+ handleChange = () => {
+ this.props.onChangeAdvancedOption(this.props.name);
+ };
+
+ render() {
+ const { name, meta, text, checked } = this.props;
+
+ return (
+
+
+
+
+ {text}
+ {meta}
+
+
+ );
+ }
+
+}
+
export default @injectIntl
class ComposerOptions extends ImmutablePureComponent {
@@ -141,6 +173,13 @@ class ComposerOptions extends ImmutablePureComponent {
this.fileElement = fileElement;
}
+ renderToggleItemContents = (item, index) => {
+ const { onChangeAdvancedOption } = this.props;
+ const { name, meta, text } = item;
+
+ return ;
+ };
+
// Rendering.
render () {
const {
@@ -152,7 +191,6 @@ class ComposerOptions extends ImmutablePureComponent {
hasMedia,
allowPoll,
hasPoll,
- intl,
onChangeAdvancedOption,
onChangeContentType,
onChangeVisibility,
@@ -164,23 +202,24 @@ class ComposerOptions extends ImmutablePureComponent {
resetFileKey,
spoiler,
showContentTypeChoice,
+ intl: { formatMessage },
} = this.props;
const contentTypeItems = {
plain: {
icon: 'file-text',
name: 'text/plain',
- text: ,
+ text: formatMessage(messages.plain),
},
html: {
icon: 'code',
name: 'text/html',
- text: ,
+ text: formatMessage(messages.html),
},
markdown: {
icon: 'arrow-circle-down',
name: 'text/markdown',
- text: ,
+ text: formatMessage(messages.markdown),
},
};
@@ -204,18 +243,18 @@ class ComposerOptions extends ImmutablePureComponent {
{
icon: 'cloud-upload',
name: 'upload',
- text: ,
+ text: formatMessage(messages.upload),
},
{
icon: 'paint-brush',
name: 'doodle',
- text: ,
+ text: formatMessage(messages.doodle),
},
]}
onChange={this.handleClickAttach}
onModalClose={onModalClose}
onModalOpen={onModalOpen}
- title={intl.formatMessage(messages.attach)}
+ title={formatMessage(messages.attach)}
/>
{!!pollLimits && (
)}
@@ -252,7 +291,7 @@ class ComposerOptions extends ImmutablePureComponent {
onChange={onChangeContentType}
onModalClose={onModalClose}
onModalOpen={onModalOpen}
- title={intl.formatMessage(messages.content_type)}
+ title={formatMessage(messages.content_type)}
value={contentType}
/>
)}
@@ -262,7 +301,7 @@ class ComposerOptions extends ImmutablePureComponent {
ariaControls='glitch.composer.spoiler.input'
label='CW'
onClick={onToggleSpoiler}
- title={intl.formatMessage(messages.spoiler)}
+ title={formatMessage(messages.spoiler)}
/>
)}
,
+ meta: formatMessage(messages.local_only_long),
name: 'do_not_federate',
- on: advancedOptions.get('do_not_federate'),
- text: ,
+ text: formatMessage(messages.local_only_short),
},
{
- meta: ,
+ meta: formatMessage(messages.threaded_mode_long),
name: 'threaded_mode',
- on: advancedOptions.get('threaded_mode'),
- text: ,
+ text: formatMessage(messages.threaded_mode_short),
},
] : null}
onChange={onChangeAdvancedOption}
+ renderItemContents={this.renderToggleItemContents}
onModalClose={onModalClose}
onModalOpen={onModalOpen}
- title={intl.formatMessage(messages.advanced_options_icon_title)}
+ title={formatMessage(messages.advanced_options_icon_title)}
+ closeOnChange={false}
/>
);
diff --git a/app/javascript/flavours/glitch/features/compose/components/privacy_dropdown.js b/app/javascript/flavours/glitch/features/compose/components/privacy_dropdown.js
index d5d29a7516..4113e4061d 100644
--- a/app/javascript/flavours/glitch/features/compose/components/privacy_dropdown.js
+++ b/app/javascript/flavours/glitch/features/compose/components/privacy_dropdown.js
@@ -5,42 +5,15 @@ import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
import Dropdown from './dropdown';
const messages = defineMessages({
- change_privacy: {
- defaultMessage: 'Adjust status privacy',
- id: 'privacy.change',
- },
- direct_long: {
- defaultMessage: 'Visible for mentioned users only',
- id: 'privacy.direct.long',
- },
- direct_short: {
- defaultMessage: 'Direct',
- id: 'privacy.direct.short',
- },
- private_long: {
- defaultMessage: 'Visible for followers only',
- id: 'privacy.private.long',
- },
- private_short: {
- defaultMessage: 'Followers-only',
- id: 'privacy.private.short',
- },
- public_long: {
- defaultMessage: 'Visible for all, shown in public timelines',
- id: 'privacy.public.long',
- },
- public_short: {
- defaultMessage: 'Public',
- id: 'privacy.public.short',
- },
- unlisted_long: {
- defaultMessage: 'Visible for all, but not in public timelines',
- id: 'privacy.unlisted.long',
- },
- unlisted_short: {
- defaultMessage: 'Unlisted',
- id: 'privacy.unlisted.short',
- },
+ public_short: { id: 'privacy.public.short', defaultMessage: 'Public' },
+ public_long: { id: 'privacy.public.long', defaultMessage: 'Visible for all, shown in public timelines' },
+ unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' },
+ unlisted_long: { id: 'privacy.unlisted.long', defaultMessage: 'Visible for all, but not in public timelines' },
+ private_short: { id: 'privacy.private.short', defaultMessage: 'Followers-only' },
+ private_long: { id: 'privacy.private.long', defaultMessage: 'Visible for followers only' },
+ direct_short: { id: 'privacy.direct.short', defaultMessage: 'Direct' },
+ direct_long: { id: 'privacy.direct.long', defaultMessage: 'Visible for mentioned users only' },
+ change_privacy: { id: 'privacy.change', defaultMessage: 'Adjust status privacy' },
});
export default @injectIntl
@@ -58,34 +31,34 @@ class PrivacyDropdown extends React.PureComponent {
};
render () {
- const { value, onChange, onModalOpen, onModalClose, disabled, noDirect, container, intl } = this.props;
+ const { value, onChange, onModalOpen, onModalClose, disabled, noDirect, container, intl: { formatMessage } } = this.props;
// We predefine our privacy items so that we can easily pick the
// dropdown icon later.
const privacyItems = {
direct: {
icon: 'envelope',
- meta: ,
+ meta: formatMessage(messages.direct_long),
name: 'direct',
- text: ,
+ text: formatMessage(messages.direct_short),
},
private: {
icon: 'lock',
- meta: ,
+ meta: formatMessage(messages.private_long),
name: 'private',
- text: ,
+ text: formatMessage(messages.private_short),
},
public: {
icon: 'globe',
- meta: ,
+ meta: formatMessage(messages.public_long),
name: 'public',
- text: ,
+ text: formatMessage(messages.public_short),
},
unlisted: {
icon: 'unlock',
- meta: ,
+ meta: formatMessage(messages.unlisted_long),
name: 'unlisted',
- text: ,
+ text: formatMessage(messages.unlisted_short),
},
};
@@ -103,7 +76,7 @@ class PrivacyDropdown extends React.PureComponent {
onChange={onChange}
onModalClose={onModalClose}
onModalOpen={onModalOpen}
- title={intl.formatMessage(messages.change_privacy)}
+ title={formatMessage(messages.change_privacy)}
container={container}
value={value}
/>
diff --git a/app/javascript/flavours/glitch/features/ui/components/actions_modal.js b/app/javascript/flavours/glitch/features/ui/components/actions_modal.js
index 4ae3a47663..aae2e4426d 100644
--- a/app/javascript/flavours/glitch/features/ui/components/actions_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/actions_modal.js
@@ -7,8 +7,7 @@ import Avatar from 'flavours/glitch/components/avatar';
import RelativeTimestamp from 'flavours/glitch/components/relative_timestamp';
import DisplayName from 'flavours/glitch/components/display_name';
import classNames from 'classnames';
-import Icon from 'flavours/glitch/components/icon';
-import Toggle from 'react-toggle';
+import IconButton from 'flavours/glitch/components/icon_button';
export default class ActionsModal extends ImmutablePureComponent {
@@ -19,12 +18,11 @@ export default class ActionsModal extends ImmutablePureComponent {
active: PropTypes.bool,
href: PropTypes.string,
icon: PropTypes.string,
- meta: PropTypes.node,
+ meta: PropTypes.string,
name: PropTypes.string,
- on: PropTypes.bool,
- onPassiveClick: PropTypes.func,
- text: PropTypes.node,
+ text: PropTypes.string,
})),
+ renderItemContents: PropTypes.func,
};
renderAction = (action, i) => {
@@ -32,40 +30,25 @@ export default class ActionsModal extends ImmutablePureComponent {
return ;
}
- const {
- active,
- href,
- icon,
- meta,
- name,
- on,
- onClick,
- onPassiveClick,
- text,
- } = action;
+ const { icon = null, text, meta = null, active = false, href = '#' } = action;
+ let contents = this.props.renderItemContents && this.props.renderItemContents(action, i);
+
+ if (!contents) {
+ contents = (
+
+ {icon && }
+
+
+ );
+ }
return (
-
-
- {on !== null && typeof on !== 'undefined' && (
-
- )}
- {icon && (
-
- )}
- {meta ? (
-
- {text}
- {meta}
-
- ) : {text}
}
+
+
+ {contents}
);