Reduce composer differences with upstream and simplify code (#2518)

pull/2454/merge
Claire 2023-12-18 13:20:08 +01:00 committed by GitHub
parent 18856371be
commit 3d3fa75c81
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 176 additions and 251 deletions

View File

@ -251,9 +251,9 @@ export function submitCompose(routerHistory) {
return; return;
} }
// To make the app more responsive, immediately get the status into the columns // To make the app more responsive, immediately push the status
// into the columns
const insertIfOnline = (timelineId) => { const insertIfOnline = timelineId => {
const timeline = getState().getIn(['timelines', timelineId]); const timeline = getState().getIn(['timelines', timelineId]);
if (timeline && timeline.get('items').size > 0 && timeline.getIn(['items', 0]) !== null && timeline.get('online')) { if (timeline && timeline.get('items').size > 0 && timeline.getIn(['items', 0]) !== null && timeline.get('online')) {
@ -662,8 +662,9 @@ export function selectComposeSuggestion(position, token, suggestion, path) {
return (dispatch, getState) => { return (dispatch, getState) => {
let completion; let completion;
if (suggestion.type === 'emoji') { if (suggestion.type === 'emoji') {
dispatch(useEmoji(suggestion));
completion = suggestion.native || suggestion.colons; completion = suggestion.native || suggestion.colons;
dispatch(useEmoji(suggestion));
} else if (suggestion.type === 'hashtag') { } else if (suggestion.type === 'hashtag') {
completion = `#${suggestion.name}`; completion = `#${suggestion.name}`;
} else if (suggestion.type === 'account') { } else if (suggestion.type === 'account') {

View File

@ -56,14 +56,14 @@ class ComposeForm extends ImmutablePureComponent {
isChangingUpload: PropTypes.bool, isChangingUpload: PropTypes.bool,
isEditing: PropTypes.bool, isEditing: PropTypes.bool,
isUploading: PropTypes.bool, isUploading: PropTypes.bool,
onChange: PropTypes.func, onChange: PropTypes.func.isRequired,
onSubmit: PropTypes.func, onSubmit: PropTypes.func.isRequired,
onClearSuggestions: PropTypes.func, onClearSuggestions: PropTypes.func.isRequired,
onFetchSuggestions: PropTypes.func, onFetchSuggestions: PropTypes.func.isRequired,
onSuggestionSelected: PropTypes.func, onSuggestionSelected: PropTypes.func.isRequired,
onChangeSpoilerText: PropTypes.func, onChangeSpoilerText: PropTypes.func.isRequired,
onPaste: PropTypes.func, onPaste: PropTypes.func.isRequired,
onPickEmoji: PropTypes.func, onPickEmoji: PropTypes.func.isRequired,
showSearch: PropTypes.bool, showSearch: PropTypes.bool,
anyMedia: PropTypes.bool, anyMedia: PropTypes.bool,
isInReply: PropTypes.bool, isInReply: PropTypes.bool,
@ -77,9 +77,9 @@ class ComposeForm extends ImmutablePureComponent {
spoilersAlwaysOn: PropTypes.bool, spoilersAlwaysOn: PropTypes.bool,
mediaDescriptionConfirmation: PropTypes.bool, mediaDescriptionConfirmation: PropTypes.bool,
preselectOnReply: PropTypes.bool, preselectOnReply: PropTypes.bool,
onChangeSpoilerness: PropTypes.func, onChangeSpoilerness: PropTypes.func.isRequired,
onChangeVisibility: PropTypes.func, onChangeVisibility: PropTypes.func.isRequired,
onMediaDescriptionConfirm: PropTypes.func, onMediaDescriptionConfirm: PropTypes.func.isRequired,
...WithOptionalRouterPropTypes ...WithOptionalRouterPropTypes
}; };
@ -100,6 +100,16 @@ class ComposeForm extends ImmutablePureComponent {
this.props.onChange(e.target.value); this.props.onChange(e.target.value);
}; };
handleKeyDown = (e) => {
if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
this.handleSubmit();
}
if (e.keyCode === 13 && e.altKey) {
this.handleSecondarySubmit();
}
};
getFulltextForCharacterCounting = () => { getFulltextForCharacterCounting = () => {
return [ return [
this.props.spoiler? this.props.spoilerText: '', this.props.spoiler? this.props.spoilerText: '',
@ -116,14 +126,6 @@ class ComposeForm extends ImmutablePureComponent {
}; };
handleSubmit = (overriddenVisibility = null) => { handleSubmit = (overriddenVisibility = null) => {
const {
onSubmit,
media,
mediaDescriptionConfirmation,
onMediaDescriptionConfirm,
onChangeVisibility,
} = this.props;
if (this.props.text !== this.textareaRef.current.value) { if (this.props.text !== this.textareaRef.current.value) {
// Something changed the text inside the textarea (e.g. browser extensions like Grammarly) // Something changed the text inside the textarea (e.g. browser extensions like Grammarly)
// Update the state to match the current text // Update the state to match the current text
@ -135,35 +137,14 @@ class ComposeForm extends ImmutablePureComponent {
} }
// Submit unless there are media with missing descriptions // Submit unless there are media with missing descriptions
if (mediaDescriptionConfirmation && onMediaDescriptionConfirm && media && media.some(item => !item.get('description'))) { if (this.props.mediaDescriptionConfirmation && this.props.media && this.props.media.some(item => !item.get('description'))) {
const firstWithoutDescription = media.find(item => !item.get('description')); const firstWithoutDescription = this.props.media.find(item => !item.get('description'));
onMediaDescriptionConfirm(this.props.history || null, firstWithoutDescription.get('id'), overriddenVisibility); this.props.onMediaDescriptionConfirm(this.props.history || null, firstWithoutDescription.get('id'), overriddenVisibility);
} else if (onSubmit) { } else {
if (onChangeVisibility && overriddenVisibility) { if (overriddenVisibility) {
onChangeVisibility(overriddenVisibility); this.props.onChangeVisibility(overriddenVisibility);
} }
onSubmit(this.props.history || null); this.props.onSubmit(this.props.history || null);
}
};
// Changes the text value of the spoiler.
handleChangeSpoiler = ({ target: { value } }) => {
const { onChangeSpoilerText } = this.props;
if (onChangeSpoilerText) {
onChangeSpoilerText(value);
}
};
setRef = c => {
this.composeForm = c;
};
// Inserts an emoji at the caret.
handleEmojiPick = (data) => {
const position = this.textareaRef.current.selectionStart;
if (this.props.onPickEmoji) {
this.props.onPickEmoji(position, data);
} }
}; };
@ -175,30 +156,24 @@ class ComposeForm extends ImmutablePureComponent {
this.handleSubmit(sideArm === 'none' ? null : sideArm); this.handleSubmit(sideArm === 'none' ? null : sideArm);
}; };
// Selects a suggestion from the autofill. onSuggestionsClearRequested = () => {
handleSuggestionSelected = (tokenStart, token, value) => { this.props.onClearSuggestions();
};
onSuggestionsFetchRequested = (token) => {
this.props.onFetchSuggestions(token);
};
onSuggestionSelected = (tokenStart, token, value) => {
this.props.onSuggestionSelected(tokenStart, token, value, ['text']); this.props.onSuggestionSelected(tokenStart, token, value, ['text']);
}; };
handleSpoilerSuggestionSelected = (tokenStart, token, value) => { onSpoilerSuggestionSelected = (tokenStart, token, value) => {
this.props.onSuggestionSelected(tokenStart, token, value, ['spoiler_text']); this.props.onSuggestionSelected(tokenStart, token, value, ['spoiler_text']);
}; };
handleKeyDown = (e) => { handleChangeSpoilerText = (e) => {
if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) { this.props.onChangeSpoilerText(e.target.value);
this.handleSubmit();
}
if (e.keyCode === 13 && e.altKey) {
this.handleSecondarySubmit();
}
};
// Sets a reference to the CW field.
handleRefSpoilerText = (spoilerComponent) => {
if (spoilerComponent) {
this.spoilerText = spoilerComponent.input;
}
}; };
handleFocus = () => { handleFocus = () => {
@ -222,120 +197,99 @@ class ComposeForm extends ImmutablePureComponent {
this._updateFocusAndSelection(prevProps); this._updateFocusAndSelection(prevProps);
} }
// This statement does several things:
// - If we're beginning a reply, and,
// - Replying to zero or one users, places the cursor at the end
// of the textbox.
// - Replying to more than one user, selects any usernames past
// the first; this provides a convenient shortcut to drop
// everyone else from the conversation.
_updateFocusAndSelection = (prevProps) => { _updateFocusAndSelection = (prevProps) => {
const { // This statement does several things:
spoilerText, // - If we're beginning a reply, and,
} = this; // - Replying to zero or one users, places the cursor at the end of the textbox.
const { // - Replying to more than one user, selects any usernames past the first;
focusDate, // this provides a convenient shortcut to drop everyone else from the conversation.
caretPosition, if (this.props.focusDate && this.props.focusDate !== prevProps.focusDate) {
isSubmitting, let selectionEnd, selectionStart;
preselectDate,
text,
preselectOnReply,
singleColumn,
} = this.props;
let selectionEnd, selectionStart;
// Caret/selection handling. if (this.props.preselectDate !== prevProps.preselectDate && this.props.isInReply && this.props.preselectOnReply) {
if (focusDate !== prevProps.focusDate) { selectionEnd = this.props.text.length;
switch (true) { selectionStart = this.props.text.search(/\s/) + 1;
case preselectDate !== prevProps.preselectDate && this.props.isInReply && preselectOnReply: } else if (typeof this.props.caretPosition === 'number') {
selectionStart = text.search(/\s/) + 1; selectionStart = this.props.caretPosition;
selectionEnd = text.length; selectionEnd = this.props.caretPosition;
break; } else {
case !isNaN(caretPosition) && caretPosition !== null: selectionEnd = this.props.text.length;
selectionStart = selectionEnd = caretPosition; selectionStart = selectionEnd;
break;
default:
selectionStart = selectionEnd = text.length;
}
if (this.textareaRef.current) {
// Because of the wicg-inert polyfill, the activeElement may not be
// immediately selectable, we have to wait for observers to run, as
// described in https://github.com/WICG/inert#performance-and-gotchas
Promise.resolve().then(() => {
this.textareaRef.current.setSelectionRange(selectionStart, selectionEnd);
this.textareaRef.current.focus();
if (!singleColumn) this.textareaRef.current.scrollIntoView();
this.setState({ highlighted: true });
this.timeout = setTimeout(() => this.setState({ highlighted: false }), 700);
}).catch(console.error);
} }
// Refocuses the textarea after submitting. // Because of the wicg-inert polyfill, the activeElement may not be
} else if (this.textareaRef.current && prevProps.isSubmitting && !isSubmitting) { // immediately selectable, we have to wait for observers to run, as
// described in https://github.com/WICG/inert#performance-and-gotchas
Promise.resolve().then(() => {
this.textareaRef.current.setSelectionRange(selectionStart, selectionEnd);
this.textareaRef.current.focus();
if (!this.props.singleColumn) this.textareaRef.current.scrollIntoView();
this.setState({ highlighted: true });
this.timeout = setTimeout(() => this.setState({ highlighted: false }), 700);
}).catch(console.error);
} else if(prevProps.isSubmitting && !this.props.isSubmitting) {
this.textareaRef.current.focus(); this.textareaRef.current.focus();
} else if (this.props.spoiler !== prevProps.spoiler) { } else if (this.props.spoiler !== prevProps.spoiler) {
if (this.props.spoiler) { if (this.props.spoiler) {
if (spoilerText) { this.spoilerText.input.focus();
spoilerText.focus(); } else if (prevProps.spoiler) {
} this.textareaRef.current.focus();
} else {
if (this.textareaRef.current) {
this.textareaRef.current.focus();
}
} }
} }
}; };
setSpoilerText = (c) => {
this.spoilerText = c;
};
setRef = c => {
this.composeForm = c;
};
handleEmojiPick = (data) => {
const position = this.textareaRef.current.selectionStart;
this.props.onPickEmoji(position, data);
};
render () { render () {
const { const {
handleEmojiPick,
handleSecondarySubmit,
handleSubmit,
} = this;
const {
advancedOptions,
intl, intl,
advancedOptions,
isSubmitting, isSubmitting,
layout, layout,
onChangeSpoilerness, onChangeSpoilerness,
onClearSuggestions,
onFetchSuggestions,
onPaste, onPaste,
privacy, privacy,
sensitive, sensitive,
showSearch, showSearch,
sideArm, sideArm,
spoiler,
spoilerText,
suggestions,
spoilersAlwaysOn, spoilersAlwaysOn,
isEditing, isEditing,
} = this.props; } = this.props;
const { highlighted } = this.state; const { highlighted } = this.state;
const disabled = this.props.isSubmitting;
const countText = this.getFulltextForCharacterCounting();
return ( return (
<div className='compose-form'> <form className='compose-form' onSubmit={this.handleSubmit}>
<WarningContainer /> <WarningContainer />
<ReplyIndicatorContainer /> <ReplyIndicatorContainer />
<div className={`spoiler-input ${spoiler ? 'spoiler-input--visible' : ''}`} ref={this.setRef} aria-hidden={!this.props.spoiler}> <div className={`spoiler-input ${this.props.spoiler ? 'spoiler-input--visible' : ''}`} ref={this.setRef} aria-hidden={!this.props.spoiler}>
<AutosuggestInput <AutosuggestInput
placeholder={intl.formatMessage(messages.spoiler_placeholder)} placeholder={intl.formatMessage(messages.spoiler_placeholder)}
value={spoilerText} value={this.props.spoilerText}
onChange={this.handleChangeSpoiler} onChange={this.handleChangeSpoilerText}
onKeyDown={this.handleKeyDown} onKeyDown={this.handleKeyDown}
disabled={!spoiler} disabled={!this.props.spoiler}
ref={this.handleRefSpoilerText} ref={this.setSpoilerText}
suggestions={suggestions} suggestions={this.props.suggestions}
onSuggestionsFetchRequested={onFetchSuggestions} onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={onClearSuggestions} onSuggestionsClearRequested={this.onSuggestionsClearRequested}
onSuggestionSelected={this.handleSpoilerSuggestionSelected} onSuggestionSelected={this.onSpoilerSuggestionSelected}
searchTokens={[':']} searchTokens={[':']}
id='glitch.composer.spoiler.input' id='cw-spoiler-input'
className='spoiler-input__input' className='spoiler-input__input'
lang={this.props.lang} lang={this.props.lang}
autoFocus={false} autoFocus={false}
@ -347,15 +301,15 @@ class ComposeForm extends ImmutablePureComponent {
<AutosuggestTextarea <AutosuggestTextarea
ref={this.textareaRef} ref={this.textareaRef}
placeholder={intl.formatMessage(messages.placeholder)} placeholder={intl.formatMessage(messages.placeholder)}
disabled={isSubmitting} disabled={disabled}
value={this.props.text} value={this.props.text}
onChange={this.handleChange} onChange={this.handleChange}
onKeyDown={this.handleKeyDown} suggestions={this.props.suggestions}
suggestions={suggestions}
onFocus={this.handleFocus} onFocus={this.handleFocus}
onSuggestionsFetchRequested={onFetchSuggestions} onKeyDown={this.handleKeyDown}
onSuggestionsClearRequested={onClearSuggestions} onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionSelected={this.handleSuggestionSelected} onSuggestionsClearRequested={this.onSuggestionsClearRequested}
onSuggestionSelected={this.onSuggestionSelected}
onPaste={onPaste} onPaste={onPaste}
autoFocus={!showSearch && !isMobile(window.innerWidth, layout)} autoFocus={!showSearch && !isMobile(window.innerWidth, layout)}
lang={this.props.lang} lang={this.props.lang}
@ -366,34 +320,33 @@ class ComposeForm extends ImmutablePureComponent {
<PollFormContainer /> <PollFormContainer />
</div> </div>
</AutosuggestTextarea> </AutosuggestTextarea>
<EmojiPickerDropdown onPickEmoji={handleEmojiPick} /> <EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} />
<div className='compose-form__buttons-wrapper'> <div className='compose-form__buttons-wrapper'>
<OptionsContainer <OptionsContainer
advancedOptions={advancedOptions} advancedOptions={advancedOptions}
disabled={isSubmitting} disabled={isSubmitting}
onToggleSpoiler={spoilersAlwaysOn ? null : onChangeSpoilerness} onToggleSpoiler={this.props.spoilersAlwaysOn ? null : onChangeSpoilerness}
onUpload={onPaste} onUpload={onPaste}
isEditing={isEditing} isEditing={isEditing}
sensitive={sensitive || (spoilersAlwaysOn && spoilerText && spoilerText.length > 0)} sensitive={sensitive || (spoilersAlwaysOn && this.props.spoilerText && this.props.spoilerText.length > 0)}
spoiler={spoilersAlwaysOn ? (spoilerText && spoilerText.length > 0) : spoiler} spoiler={spoilersAlwaysOn ? (this.props.spoilerText && this.props.spoilerText.length > 0) : this.props.spoiler}
/> />
<div className='character-counter__wrapper'> <div className='character-counter__wrapper'>
<CharacterCounter text={countText} max={maxChars} /> <CharacterCounter max={maxChars} text={this.getFulltextForCharacterCounting()} />
</div> </div>
</div> </div>
</div> </div>
<Publisher <Publisher
countText={countText}
disabled={!this.canSubmit()} disabled={!this.canSubmit()}
isEditing={isEditing} isEditing={isEditing}
onSecondarySubmit={handleSecondarySubmit} onSecondarySubmit={this.handleSecondarySubmit}
onSubmit={handleSubmit} onSubmit={this.handleSubmit}
privacy={privacy} privacy={privacy}
sideArm={sideArm} sideArm={sideArm}
/> />
</div> </form>
); );
} }

View File

@ -30,9 +30,11 @@ export default class NavigationBar extends ImmutablePureComponent {
</Permalink> </Permalink>
<div className='navigation-bar__profile'> <div className='navigation-bar__profile'>
<Permalink className='acct' href={this.props.account.get('url')} to={`/@${this.props.account.get('acct')}`}> <span>
<strong className='navigation-bar__profile-account'>@{username}</strong> <Permalink className='acct' href={this.props.account.get('url')} to={`/@${username}`}>
</Permalink> <strong className='navigation-bar__profile-account'>@{username}</strong>
</Permalink>
</span>
{ profileLink !== undefined && ( { profileLink !== undefined && (
<a <a

View File

@ -1,4 +1,3 @@
// Package imports.
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { defineMessages, injectIntl } from 'react-intl'; import { defineMessages, injectIntl } from 'react-intl';
@ -9,8 +8,6 @@ import { connect } from 'react-redux';
import Toggle from 'react-toggle'; import Toggle from 'react-toggle';
// Components.
import { IconButton } from 'flavours/glitch/components/icon_button'; import { IconButton } from 'flavours/glitch/components/icon_button';
import { pollLimits } from 'flavours/glitch/initial_state'; import { pollLimits } from 'flavours/glitch/initial_state';
@ -20,11 +17,6 @@ import PrivacyDropdownContainer from '../containers/privacy_dropdown_container';
import TextIconButton from './text_icon_button'; import TextIconButton from './text_icon_button';
// Utils.
// Messages.
const messages = defineMessages({ const messages = defineMessages({
advanced_options_icon_title: { advanced_options_icon_title: {
defaultMessage: 'Advanced options', defaultMessage: 'Advanced options',
@ -133,12 +125,12 @@ class ComposerOptions extends ImmutablePureComponent {
allowPoll: PropTypes.bool, allowPoll: PropTypes.bool,
hasPoll: PropTypes.bool, hasPoll: PropTypes.bool,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
onChangeAdvancedOption: PropTypes.func, onChangeAdvancedOption: PropTypes.func.isRequired,
onChangeContentType: PropTypes.func, onChangeContentType: PropTypes.func.isRequired,
onTogglePoll: PropTypes.func, onTogglePoll: PropTypes.func.isRequired,
onDoodleOpen: PropTypes.func, onDoodleOpen: PropTypes.func.isRequired,
onToggleSpoiler: PropTypes.func, onToggleSpoiler: PropTypes.func,
onUpload: PropTypes.func, onUpload: PropTypes.func.isRequired,
contentType: PropTypes.string, contentType: PropTypes.string,
resetFileKey: PropTypes.number, resetFileKey: PropTypes.number,
spoiler: PropTypes.bool, spoiler: PropTypes.bool,
@ -146,20 +138,17 @@ class ComposerOptions extends ImmutablePureComponent {
isEditing: PropTypes.bool, isEditing: PropTypes.bool,
}; };
// Handles file selection.
handleChangeFiles = ({ target: { files } }) => { handleChangeFiles = ({ target: { files } }) => {
const { onUpload } = this.props; const { onUpload } = this.props;
if (files.length && onUpload) { if (files.length) {
onUpload(files); onUpload(files);
} }
}; };
// Handles attachment clicks.
handleClickAttach = (name) => { handleClickAttach = (name) => {
const { fileElement } = this; const { fileElement } = this;
const { onDoodleOpen } = this.props; const { onDoodleOpen } = this.props;
// We switch over the name of the option.
switch (name) { switch (name) {
case 'upload': case 'upload':
if (fileElement) { if (fileElement) {
@ -167,14 +156,11 @@ class ComposerOptions extends ImmutablePureComponent {
} }
return; return;
case 'doodle': case 'doodle':
if (onDoodleOpen) { onDoodleOpen();
onDoodleOpen();
}
return; return;
} }
}; };
// Handles a ref to the file input.
handleRefFileElement = (fileElement) => { handleRefFileElement = (fileElement) => {
this.fileElement = fileElement; this.fileElement = fileElement;
}; };
@ -186,7 +172,6 @@ class ComposerOptions extends ImmutablePureComponent {
return <ToggleOption name={name} text={text} meta={meta} onChangeAdvancedOption={onChangeAdvancedOption} />; return <ToggleOption name={name} text={text} meta={meta} onChangeAdvancedOption={onChangeAdvancedOption} />;
}; };
// Rendering.
render () { render () {
const { const {
acceptContentTypes, acceptContentTypes,
@ -290,7 +275,7 @@ class ComposerOptions extends ImmutablePureComponent {
{onToggleSpoiler && ( {onToggleSpoiler && (
<TextIconButton <TextIconButton
active={spoiler} active={spoiler}
ariaControls='glitch.composer.spoiler.input' ariaControls='cw-spoiler-input'
label='CW' label='CW'
onClick={onToggleSpoiler} onClick={onToggleSpoiler}
title={formatMessage(messages.spoiler)} title={formatMessage(messages.spoiler)}

View File

@ -2,15 +2,10 @@ import PropTypes from 'prop-types';
import { defineMessages, injectIntl } from 'react-intl'; import { defineMessages, injectIntl } from 'react-intl';
import classNames from 'classnames';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { length } from 'stringz';
import { Button } from 'flavours/glitch/components/button'; import { Button } from 'flavours/glitch/components/button';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
import { maxChars } from 'flavours/glitch/initial_state';
const messages = defineMessages({ const messages = defineMessages({
publish: { publish: {
@ -31,7 +26,6 @@ const messages = defineMessages({
class Publisher extends ImmutablePureComponent { class Publisher extends ImmutablePureComponent {
static propTypes = { static propTypes = {
countText: PropTypes.string,
disabled: PropTypes.bool, disabled: PropTypes.bool,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
onSecondarySubmit: PropTypes.func, onSecondarySubmit: PropTypes.func,
@ -46,13 +40,7 @@ class Publisher extends ImmutablePureComponent {
}; };
render () { render () {
const { countText, disabled, intl, onSecondarySubmit, privacy, sideArm, isEditing } = this.props; const { disabled, intl, onSecondarySubmit, privacy, sideArm, isEditing } = this.props;
const diff = maxChars - length(countText || '');
const computedClass = classNames('compose-form__publish', {
disabled: disabled,
over: diff < 0,
});
const privacyIcons = { direct: 'envelope', private: 'lock', public: 'globe', unlisted: 'unlock' }; const privacyIcons = { direct: 'envelope', private: 'lock', public: 'globe', unlisted: 'unlock' };
@ -78,8 +66,8 @@ class Publisher extends ImmutablePureComponent {
}; };
return ( return (
<div className={computedClass}> <div className='compose-form__publish'>
{sideArm && !isEditing && sideArm !== 'none' ? ( {sideArm && !isEditing && sideArm !== 'none' && (
<div className='compose-form__publish-button-wrapper'> <div className='compose-form__publish-button-wrapper'>
<Button <Button
className='side_arm' className='side_arm'
@ -90,7 +78,7 @@ class Publisher extends ImmutablePureComponent {
title={`${intl.formatMessage(messages.publish)}: ${intl.formatMessage(privacyNames[sideArm])}`} title={`${intl.formatMessage(messages.publish)}: ${intl.formatMessage(privacyNames[sideArm])}`}
/> />
</div> </div>
) : null} )}
<div className='compose-form__publish-button-wrapper'> <div className='compose-form__publish-button-wrapper'>
<Button <Button
className='primary' className='primary'

View File

@ -18,13 +18,11 @@ export default class UploadForm extends ImmutablePureComponent {
<div className='compose-form__upload-wrapper'> <div className='compose-form__upload-wrapper'>
<UploadProgressContainer /> <UploadProgressContainer />
{mediaIds.size > 0 && ( <div className='compose-form__uploads-wrapper'>
<div className='compose-form__uploads-wrapper'> {mediaIds.map(id => (
{mediaIds.map(id => ( <UploadContainer id={id} key={id} />
<UploadContainer id={id} key={id} /> ))}
))} </div>
</div>
)}
{!mediaIds.isEmpty() && <SensitiveButtonContainer />} {!mediaIds.isEmpty() && <SensitiveButtonContainer />}
</div> </div>

View File

@ -37,9 +37,7 @@ const messages = defineMessages({
}, },
}); });
// State mapping. const sideArmPrivacy = state => {
function mapStateToProps (state) {
const spoilersAlwaysOn = state.getIn(['local_settings', 'always_show_spoilers_field']);
const inReplyTo = state.getIn(['compose', 'in_reply_to']); const inReplyTo = state.getIn(['compose', 'in_reply_to']);
const replyPrivacy = inReplyTo ? state.getIn(['statuses', inReplyTo, 'visibility']) : null; const replyPrivacy = inReplyTo ? state.getIn(['statuses', inReplyTo, 'visibility']) : null;
const sideArmBasePrivacy = state.getIn(['local_settings', 'side_arm']); const sideArmBasePrivacy = state.getIn(['local_settings', 'side_arm']);
@ -53,67 +51,67 @@ function mapStateToProps (state) {
sideArmPrivacy = sideArmRestrictedPrivacy; sideArmPrivacy = sideArmRestrictedPrivacy;
break; break;
} }
sideArmPrivacy = sideArmPrivacy || sideArmBasePrivacy; return sideArmPrivacy || sideArmBasePrivacy;
return { };
advancedOptions: state.getIn(['compose', 'advanced_options']),
focusDate: state.getIn(['compose', 'focusDate']), const mapStateToProps = state => ({
caretPosition: state.getIn(['compose', 'caretPosition']), text: state.getIn(['compose', 'text']),
isSubmitting: state.getIn(['compose', 'is_submitting']), suggestions: state.getIn(['compose', 'suggestions']),
isEditing: state.getIn(['compose', 'id']) !== null, spoiler: state.getIn(['local_settings', 'always_show_spoilers_field']) || state.getIn(['compose', 'spoiler']),
isChangingUpload: state.getIn(['compose', 'is_changing_upload']), spoilerText: state.getIn(['compose', 'spoiler_text']),
isUploading: state.getIn(['compose', 'is_uploading']), privacy: state.getIn(['compose', 'privacy']),
layout: state.getIn(['local_settings', 'layout']), focusDate: state.getIn(['compose', 'focusDate']),
media: state.getIn(['compose', 'media_attachments']), caretPosition: state.getIn(['compose', 'caretPosition']),
preselectDate: state.getIn(['compose', 'preselectDate']), preselectDate: state.getIn(['compose', 'preselectDate']),
privacy: state.getIn(['compose', 'privacy']), isSubmitting: state.getIn(['compose', 'is_submitting']),
sideArm: sideArmPrivacy, isEditing: state.getIn(['compose', 'id']) !== null,
sensitive: state.getIn(['compose', 'sensitive']), isChangingUpload: state.getIn(['compose', 'is_changing_upload']),
showSearch: state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']), isUploading: state.getIn(['compose', 'is_uploading']),
spoiler: spoilersAlwaysOn || state.getIn(['compose', 'spoiler']), anyMedia: state.getIn(['compose', 'media_attachments']).size > 0,
spoilerText: state.getIn(['compose', 'spoiler_text']), isInReply: state.getIn(['compose', 'in_reply_to']) !== null,
suggestions: state.getIn(['compose', 'suggestions']), lang: state.getIn(['compose', 'language']),
text: state.getIn(['compose', 'text']), advancedOptions: state.getIn(['compose', 'advanced_options']),
anyMedia: state.getIn(['compose', 'media_attachments']).size > 0, layout: state.getIn(['local_settings', 'layout']),
spoilersAlwaysOn: spoilersAlwaysOn, media: state.getIn(['compose', 'media_attachments']),
mediaDescriptionConfirmation: state.getIn(['local_settings', 'confirm_missing_media_description']), sideArm: sideArmPrivacy(state),
preselectOnReply: state.getIn(['local_settings', 'preselect_on_reply']), sensitive: state.getIn(['compose', 'sensitive']),
isInReply: state.getIn(['compose', 'in_reply_to']) !== null, showSearch: state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']),
lang: state.getIn(['compose', 'language']), spoilersAlwaysOn: state.getIn(['local_settings', 'always_show_spoilers_field']),
}; mediaDescriptionConfirmation: state.getIn(['local_settings', 'confirm_missing_media_description']),
} preselectOnReply: state.getIn(['local_settings', 'preselect_on_reply']),
});
// Dispatch mapping.
const mapDispatchToProps = (dispatch, { intl }) => ({ const mapDispatchToProps = (dispatch, { intl }) => ({
onChange(text) { onChange (text) {
dispatch(changeCompose(text)); dispatch(changeCompose(text));
}, },
onSubmit(routerHistory) { onSubmit (router) {
dispatch(submitCompose(routerHistory)); dispatch(submitCompose(router));
}, },
onClearSuggestions() { onClearSuggestions () {
dispatch(clearComposeSuggestions()); dispatch(clearComposeSuggestions());
}, },
onFetchSuggestions(token) { onFetchSuggestions (token) {
dispatch(fetchComposeSuggestions(token)); dispatch(fetchComposeSuggestions(token));
}, },
onSuggestionSelected(position, token, suggestion, path) { onSuggestionSelected (position, token, suggestion, path) {
dispatch(selectComposeSuggestion(position, token, suggestion, path)); dispatch(selectComposeSuggestion(position, token, suggestion, path));
}, },
onChangeSpoilerText(text) { onChangeSpoilerText (text) {
dispatch(changeComposeSpoilerText(text)); dispatch(changeComposeSpoilerText(text));
}, },
onPaste(files) { onPaste (files) {
dispatch(uploadCompose(files)); dispatch(uploadCompose(files));
}, },
onPickEmoji(position, emoji) { onPickEmoji (position, emoji) {
dispatch(insertEmojiCompose(position, emoji)); dispatch(insertEmojiCompose(position, emoji));
}, },