Reduce composer differences with upstream and simplify code (#2518)
parent
18856371be
commit
3d3fa75c81
|
@ -251,9 +251,9 @@ export function submitCompose(routerHistory) {
|
|||
return;
|
||||
}
|
||||
|
||||
// To make the app more responsive, immediately get the status into the columns
|
||||
|
||||
const insertIfOnline = (timelineId) => {
|
||||
// To make the app more responsive, immediately push the status
|
||||
// into the columns
|
||||
const insertIfOnline = timelineId => {
|
||||
const timeline = getState().getIn(['timelines', timelineId]);
|
||||
|
||||
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) => {
|
||||
let completion;
|
||||
if (suggestion.type === 'emoji') {
|
||||
dispatch(useEmoji(suggestion));
|
||||
completion = suggestion.native || suggestion.colons;
|
||||
|
||||
dispatch(useEmoji(suggestion));
|
||||
} else if (suggestion.type === 'hashtag') {
|
||||
completion = `#${suggestion.name}`;
|
||||
} else if (suggestion.type === 'account') {
|
||||
|
|
|
@ -56,14 +56,14 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
isChangingUpload: PropTypes.bool,
|
||||
isEditing: PropTypes.bool,
|
||||
isUploading: PropTypes.bool,
|
||||
onChange: PropTypes.func,
|
||||
onSubmit: PropTypes.func,
|
||||
onClearSuggestions: PropTypes.func,
|
||||
onFetchSuggestions: PropTypes.func,
|
||||
onSuggestionSelected: PropTypes.func,
|
||||
onChangeSpoilerText: PropTypes.func,
|
||||
onPaste: PropTypes.func,
|
||||
onPickEmoji: PropTypes.func,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onSubmit: PropTypes.func.isRequired,
|
||||
onClearSuggestions: PropTypes.func.isRequired,
|
||||
onFetchSuggestions: PropTypes.func.isRequired,
|
||||
onSuggestionSelected: PropTypes.func.isRequired,
|
||||
onChangeSpoilerText: PropTypes.func.isRequired,
|
||||
onPaste: PropTypes.func.isRequired,
|
||||
onPickEmoji: PropTypes.func.isRequired,
|
||||
showSearch: PropTypes.bool,
|
||||
anyMedia: PropTypes.bool,
|
||||
isInReply: PropTypes.bool,
|
||||
|
@ -77,9 +77,9 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
spoilersAlwaysOn: PropTypes.bool,
|
||||
mediaDescriptionConfirmation: PropTypes.bool,
|
||||
preselectOnReply: PropTypes.bool,
|
||||
onChangeSpoilerness: PropTypes.func,
|
||||
onChangeVisibility: PropTypes.func,
|
||||
onMediaDescriptionConfirm: PropTypes.func,
|
||||
onChangeSpoilerness: PropTypes.func.isRequired,
|
||||
onChangeVisibility: PropTypes.func.isRequired,
|
||||
onMediaDescriptionConfirm: PropTypes.func.isRequired,
|
||||
...WithOptionalRouterPropTypes
|
||||
};
|
||||
|
||||
|
@ -100,6 +100,16 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
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 = () => {
|
||||
return [
|
||||
this.props.spoiler? this.props.spoilerText: '',
|
||||
|
@ -116,14 +126,6 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
handleSubmit = (overriddenVisibility = null) => {
|
||||
const {
|
||||
onSubmit,
|
||||
media,
|
||||
mediaDescriptionConfirmation,
|
||||
onMediaDescriptionConfirm,
|
||||
onChangeVisibility,
|
||||
} = this.props;
|
||||
|
||||
if (this.props.text !== this.textareaRef.current.value) {
|
||||
// Something changed the text inside the textarea (e.g. browser extensions like Grammarly)
|
||||
// Update the state to match the current text
|
||||
|
@ -135,35 +137,14 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
// Submit unless there are media with missing descriptions
|
||||
if (mediaDescriptionConfirmation && onMediaDescriptionConfirm && media && media.some(item => !item.get('description'))) {
|
||||
const firstWithoutDescription = media.find(item => !item.get('description'));
|
||||
onMediaDescriptionConfirm(this.props.history || null, firstWithoutDescription.get('id'), overriddenVisibility);
|
||||
} else if (onSubmit) {
|
||||
if (onChangeVisibility && overriddenVisibility) {
|
||||
onChangeVisibility(overriddenVisibility);
|
||||
if (this.props.mediaDescriptionConfirmation && this.props.media && this.props.media.some(item => !item.get('description'))) {
|
||||
const firstWithoutDescription = this.props.media.find(item => !item.get('description'));
|
||||
this.props.onMediaDescriptionConfirm(this.props.history || null, firstWithoutDescription.get('id'), overriddenVisibility);
|
||||
} else {
|
||||
if (overriddenVisibility) {
|
||||
this.props.onChangeVisibility(overriddenVisibility);
|
||||
}
|
||||
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);
|
||||
this.props.onSubmit(this.props.history || null);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -175,30 +156,24 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
this.handleSubmit(sideArm === 'none' ? null : sideArm);
|
||||
};
|
||||
|
||||
// Selects a suggestion from the autofill.
|
||||
handleSuggestionSelected = (tokenStart, token, value) => {
|
||||
onSuggestionsClearRequested = () => {
|
||||
this.props.onClearSuggestions();
|
||||
};
|
||||
|
||||
onSuggestionsFetchRequested = (token) => {
|
||||
this.props.onFetchSuggestions(token);
|
||||
};
|
||||
|
||||
onSuggestionSelected = (tokenStart, token, value) => {
|
||||
this.props.onSuggestionSelected(tokenStart, token, value, ['text']);
|
||||
};
|
||||
|
||||
handleSpoilerSuggestionSelected = (tokenStart, token, value) => {
|
||||
onSpoilerSuggestionSelected = (tokenStart, token, value) => {
|
||||
this.props.onSuggestionSelected(tokenStart, token, value, ['spoiler_text']);
|
||||
};
|
||||
|
||||
handleKeyDown = (e) => {
|
||||
if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
|
||||
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;
|
||||
}
|
||||
handleChangeSpoilerText = (e) => {
|
||||
this.props.onChangeSpoilerText(e.target.value);
|
||||
};
|
||||
|
||||
handleFocus = () => {
|
||||
|
@ -222,120 +197,99 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
this._updateFocusAndSelection(prevProps);
|
||||
}
|
||||
|
||||
_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) => {
|
||||
const {
|
||||
spoilerText,
|
||||
} = this;
|
||||
const {
|
||||
focusDate,
|
||||
caretPosition,
|
||||
isSubmitting,
|
||||
preselectDate,
|
||||
text,
|
||||
preselectOnReply,
|
||||
singleColumn,
|
||||
} = this.props;
|
||||
// - 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.
|
||||
if (this.props.focusDate && this.props.focusDate !== prevProps.focusDate) {
|
||||
let selectionEnd, selectionStart;
|
||||
|
||||
// Caret/selection handling.
|
||||
if (focusDate !== prevProps.focusDate) {
|
||||
switch (true) {
|
||||
case preselectDate !== prevProps.preselectDate && this.props.isInReply && preselectOnReply:
|
||||
selectionStart = text.search(/\s/) + 1;
|
||||
selectionEnd = text.length;
|
||||
break;
|
||||
case !isNaN(caretPosition) && caretPosition !== null:
|
||||
selectionStart = selectionEnd = caretPosition;
|
||||
break;
|
||||
default:
|
||||
selectionStart = selectionEnd = text.length;
|
||||
if (this.props.preselectDate !== prevProps.preselectDate && this.props.isInReply && this.props.preselectOnReply) {
|
||||
selectionEnd = this.props.text.length;
|
||||
selectionStart = this.props.text.search(/\s/) + 1;
|
||||
} else if (typeof this.props.caretPosition === 'number') {
|
||||
selectionStart = this.props.caretPosition;
|
||||
selectionEnd = this.props.caretPosition;
|
||||
} else {
|
||||
selectionEnd = this.props.text.length;
|
||||
selectionStart = selectionEnd;
|
||||
}
|
||||
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();
|
||||
if (!this.props.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.
|
||||
} else if (this.textareaRef.current && prevProps.isSubmitting && !isSubmitting) {
|
||||
} else if(prevProps.isSubmitting && !this.props.isSubmitting) {
|
||||
this.textareaRef.current.focus();
|
||||
} else if (this.props.spoiler !== prevProps.spoiler) {
|
||||
if (this.props.spoiler) {
|
||||
if (spoilerText) {
|
||||
spoilerText.focus();
|
||||
}
|
||||
} else {
|
||||
if (this.textareaRef.current) {
|
||||
this.spoilerText.input.focus();
|
||||
} else if (prevProps.spoiler) {
|
||||
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 () {
|
||||
const {
|
||||
handleEmojiPick,
|
||||
handleSecondarySubmit,
|
||||
handleSubmit,
|
||||
} = this;
|
||||
const {
|
||||
advancedOptions,
|
||||
intl,
|
||||
advancedOptions,
|
||||
isSubmitting,
|
||||
layout,
|
||||
onChangeSpoilerness,
|
||||
onClearSuggestions,
|
||||
onFetchSuggestions,
|
||||
onPaste,
|
||||
privacy,
|
||||
sensitive,
|
||||
showSearch,
|
||||
sideArm,
|
||||
spoiler,
|
||||
spoilerText,
|
||||
suggestions,
|
||||
spoilersAlwaysOn,
|
||||
isEditing,
|
||||
} = this.props;
|
||||
const { highlighted } = this.state;
|
||||
|
||||
const countText = this.getFulltextForCharacterCounting();
|
||||
const disabled = this.props.isSubmitting;
|
||||
|
||||
return (
|
||||
<div className='compose-form'>
|
||||
<form className='compose-form' onSubmit={this.handleSubmit}>
|
||||
<WarningContainer />
|
||||
|
||||
<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
|
||||
placeholder={intl.formatMessage(messages.spoiler_placeholder)}
|
||||
value={spoilerText}
|
||||
onChange={this.handleChangeSpoiler}
|
||||
value={this.props.spoilerText}
|
||||
onChange={this.handleChangeSpoilerText}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
disabled={!spoiler}
|
||||
ref={this.handleRefSpoilerText}
|
||||
suggestions={suggestions}
|
||||
onSuggestionsFetchRequested={onFetchSuggestions}
|
||||
onSuggestionsClearRequested={onClearSuggestions}
|
||||
onSuggestionSelected={this.handleSpoilerSuggestionSelected}
|
||||
disabled={!this.props.spoiler}
|
||||
ref={this.setSpoilerText}
|
||||
suggestions={this.props.suggestions}
|
||||
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
|
||||
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
|
||||
onSuggestionSelected={this.onSpoilerSuggestionSelected}
|
||||
searchTokens={[':']}
|
||||
id='glitch.composer.spoiler.input'
|
||||
id='cw-spoiler-input'
|
||||
className='spoiler-input__input'
|
||||
lang={this.props.lang}
|
||||
autoFocus={false}
|
||||
|
@ -347,15 +301,15 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
<AutosuggestTextarea
|
||||
ref={this.textareaRef}
|
||||
placeholder={intl.formatMessage(messages.placeholder)}
|
||||
disabled={isSubmitting}
|
||||
disabled={disabled}
|
||||
value={this.props.text}
|
||||
onChange={this.handleChange}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
suggestions={suggestions}
|
||||
suggestions={this.props.suggestions}
|
||||
onFocus={this.handleFocus}
|
||||
onSuggestionsFetchRequested={onFetchSuggestions}
|
||||
onSuggestionsClearRequested={onClearSuggestions}
|
||||
onSuggestionSelected={this.handleSuggestionSelected}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
|
||||
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
|
||||
onSuggestionSelected={this.onSuggestionSelected}
|
||||
onPaste={onPaste}
|
||||
autoFocus={!showSearch && !isMobile(window.innerWidth, layout)}
|
||||
lang={this.props.lang}
|
||||
|
@ -366,34 +320,33 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
<PollFormContainer />
|
||||
</div>
|
||||
</AutosuggestTextarea>
|
||||
<EmojiPickerDropdown onPickEmoji={handleEmojiPick} />
|
||||
<EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} />
|
||||
|
||||
<div className='compose-form__buttons-wrapper'>
|
||||
<OptionsContainer
|
||||
advancedOptions={advancedOptions}
|
||||
disabled={isSubmitting}
|
||||
onToggleSpoiler={spoilersAlwaysOn ? null : onChangeSpoilerness}
|
||||
onToggleSpoiler={this.props.spoilersAlwaysOn ? null : onChangeSpoilerness}
|
||||
onUpload={onPaste}
|
||||
isEditing={isEditing}
|
||||
sensitive={sensitive || (spoilersAlwaysOn && spoilerText && spoilerText.length > 0)}
|
||||
spoiler={spoilersAlwaysOn ? (spoilerText && spoilerText.length > 0) : spoiler}
|
||||
sensitive={sensitive || (spoilersAlwaysOn && this.props.spoilerText && this.props.spoilerText.length > 0)}
|
||||
spoiler={spoilersAlwaysOn ? (this.props.spoilerText && this.props.spoilerText.length > 0) : this.props.spoiler}
|
||||
/>
|
||||
<div className='character-counter__wrapper'>
|
||||
<CharacterCounter text={countText} max={maxChars} />
|
||||
<CharacterCounter max={maxChars} text={this.getFulltextForCharacterCounting()} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Publisher
|
||||
countText={countText}
|
||||
disabled={!this.canSubmit()}
|
||||
isEditing={isEditing}
|
||||
onSecondarySubmit={handleSecondarySubmit}
|
||||
onSubmit={handleSubmit}
|
||||
onSecondarySubmit={this.handleSecondarySubmit}
|
||||
onSubmit={this.handleSubmit}
|
||||
privacy={privacy}
|
||||
sideArm={sideArm}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -30,9 +30,11 @@ export default class NavigationBar extends ImmutablePureComponent {
|
|||
</Permalink>
|
||||
|
||||
<div className='navigation-bar__profile'>
|
||||
<Permalink className='acct' href={this.props.account.get('url')} to={`/@${this.props.account.get('acct')}`}>
|
||||
<span>
|
||||
<Permalink className='acct' href={this.props.account.get('url')} to={`/@${username}`}>
|
||||
<strong className='navigation-bar__profile-account'>@{username}</strong>
|
||||
</Permalink>
|
||||
</span>
|
||||
|
||||
{ profileLink !== undefined && (
|
||||
<a
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
// Package imports.
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
@ -9,8 +8,6 @@ import { connect } from 'react-redux';
|
|||
|
||||
import Toggle from 'react-toggle';
|
||||
|
||||
|
||||
// Components.
|
||||
import { IconButton } from 'flavours/glitch/components/icon_button';
|
||||
import { pollLimits } from 'flavours/glitch/initial_state';
|
||||
|
||||
|
@ -20,11 +17,6 @@ import PrivacyDropdownContainer from '../containers/privacy_dropdown_container';
|
|||
|
||||
import TextIconButton from './text_icon_button';
|
||||
|
||||
|
||||
|
||||
// Utils.
|
||||
|
||||
// Messages.
|
||||
const messages = defineMessages({
|
||||
advanced_options_icon_title: {
|
||||
defaultMessage: 'Advanced options',
|
||||
|
@ -133,12 +125,12 @@ class ComposerOptions extends ImmutablePureComponent {
|
|||
allowPoll: PropTypes.bool,
|
||||
hasPoll: PropTypes.bool,
|
||||
intl: PropTypes.object.isRequired,
|
||||
onChangeAdvancedOption: PropTypes.func,
|
||||
onChangeContentType: PropTypes.func,
|
||||
onTogglePoll: PropTypes.func,
|
||||
onDoodleOpen: PropTypes.func,
|
||||
onChangeAdvancedOption: PropTypes.func.isRequired,
|
||||
onChangeContentType: PropTypes.func.isRequired,
|
||||
onTogglePoll: PropTypes.func.isRequired,
|
||||
onDoodleOpen: PropTypes.func.isRequired,
|
||||
onToggleSpoiler: PropTypes.func,
|
||||
onUpload: PropTypes.func,
|
||||
onUpload: PropTypes.func.isRequired,
|
||||
contentType: PropTypes.string,
|
||||
resetFileKey: PropTypes.number,
|
||||
spoiler: PropTypes.bool,
|
||||
|
@ -146,20 +138,17 @@ class ComposerOptions extends ImmutablePureComponent {
|
|||
isEditing: PropTypes.bool,
|
||||
};
|
||||
|
||||
// Handles file selection.
|
||||
handleChangeFiles = ({ target: { files } }) => {
|
||||
const { onUpload } = this.props;
|
||||
if (files.length && onUpload) {
|
||||
if (files.length) {
|
||||
onUpload(files);
|
||||
}
|
||||
};
|
||||
|
||||
// Handles attachment clicks.
|
||||
handleClickAttach = (name) => {
|
||||
const { fileElement } = this;
|
||||
const { onDoodleOpen } = this.props;
|
||||
|
||||
// We switch over the name of the option.
|
||||
switch (name) {
|
||||
case 'upload':
|
||||
if (fileElement) {
|
||||
|
@ -167,14 +156,11 @@ class ComposerOptions extends ImmutablePureComponent {
|
|||
}
|
||||
return;
|
||||
case 'doodle':
|
||||
if (onDoodleOpen) {
|
||||
onDoodleOpen();
|
||||
}
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Handles a ref to the file input.
|
||||
handleRefFileElement = (fileElement) => {
|
||||
this.fileElement = fileElement;
|
||||
};
|
||||
|
@ -186,7 +172,6 @@ class ComposerOptions extends ImmutablePureComponent {
|
|||
return <ToggleOption name={name} text={text} meta={meta} onChangeAdvancedOption={onChangeAdvancedOption} />;
|
||||
};
|
||||
|
||||
// Rendering.
|
||||
render () {
|
||||
const {
|
||||
acceptContentTypes,
|
||||
|
@ -290,7 +275,7 @@ class ComposerOptions extends ImmutablePureComponent {
|
|||
{onToggleSpoiler && (
|
||||
<TextIconButton
|
||||
active={spoiler}
|
||||
ariaControls='glitch.composer.spoiler.input'
|
||||
ariaControls='cw-spoiler-input'
|
||||
label='CW'
|
||||
onClick={onToggleSpoiler}
|
||||
title={formatMessage(messages.spoiler)}
|
||||
|
|
|
@ -2,15 +2,10 @@ import PropTypes from 'prop-types';
|
|||
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import { length } from 'stringz';
|
||||
|
||||
import { Button } from 'flavours/glitch/components/button';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { maxChars } from 'flavours/glitch/initial_state';
|
||||
|
||||
const messages = defineMessages({
|
||||
publish: {
|
||||
|
@ -31,7 +26,6 @@ const messages = defineMessages({
|
|||
class Publisher extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
countText: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
intl: PropTypes.object.isRequired,
|
||||
onSecondarySubmit: PropTypes.func,
|
||||
|
@ -46,13 +40,7 @@ class Publisher extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
render () {
|
||||
const { countText, 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 { disabled, intl, onSecondarySubmit, privacy, sideArm, isEditing } = this.props;
|
||||
|
||||
const privacyIcons = { direct: 'envelope', private: 'lock', public: 'globe', unlisted: 'unlock' };
|
||||
|
||||
|
@ -78,8 +66,8 @@ class Publisher extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className={computedClass}>
|
||||
{sideArm && !isEditing && sideArm !== 'none' ? (
|
||||
<div className='compose-form__publish'>
|
||||
{sideArm && !isEditing && sideArm !== 'none' && (
|
||||
<div className='compose-form__publish-button-wrapper'>
|
||||
<Button
|
||||
className='side_arm'
|
||||
|
@ -90,7 +78,7 @@ class Publisher extends ImmutablePureComponent {
|
|||
title={`${intl.formatMessage(messages.publish)}: ${intl.formatMessage(privacyNames[sideArm])}`}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
)}
|
||||
<div className='compose-form__publish-button-wrapper'>
|
||||
<Button
|
||||
className='primary'
|
||||
|
|
|
@ -18,13 +18,11 @@ export default class UploadForm extends ImmutablePureComponent {
|
|||
<div className='compose-form__upload-wrapper'>
|
||||
<UploadProgressContainer />
|
||||
|
||||
{mediaIds.size > 0 && (
|
||||
<div className='compose-form__uploads-wrapper'>
|
||||
{mediaIds.map(id => (
|
||||
<UploadContainer id={id} key={id} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!mediaIds.isEmpty() && <SensitiveButtonContainer />}
|
||||
</div>
|
||||
|
|
|
@ -37,9 +37,7 @@ const messages = defineMessages({
|
|||
},
|
||||
});
|
||||
|
||||
// State mapping.
|
||||
function mapStateToProps (state) {
|
||||
const spoilersAlwaysOn = state.getIn(['local_settings', 'always_show_spoilers_field']);
|
||||
const sideArmPrivacy = state => {
|
||||
const inReplyTo = state.getIn(['compose', 'in_reply_to']);
|
||||
const replyPrivacy = inReplyTo ? state.getIn(['statuses', inReplyTo, 'visibility']) : null;
|
||||
const sideArmBasePrivacy = state.getIn(['local_settings', 'side_arm']);
|
||||
|
@ -53,67 +51,67 @@ function mapStateToProps (state) {
|
|||
sideArmPrivacy = sideArmRestrictedPrivacy;
|
||||
break;
|
||||
}
|
||||
sideArmPrivacy = sideArmPrivacy || sideArmBasePrivacy;
|
||||
return {
|
||||
advancedOptions: state.getIn(['compose', 'advanced_options']),
|
||||
return sideArmPrivacy || sideArmBasePrivacy;
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
text: state.getIn(['compose', 'text']),
|
||||
suggestions: state.getIn(['compose', 'suggestions']),
|
||||
spoiler: state.getIn(['local_settings', 'always_show_spoilers_field']) || state.getIn(['compose', 'spoiler']),
|
||||
spoilerText: state.getIn(['compose', 'spoiler_text']),
|
||||
privacy: state.getIn(['compose', 'privacy']),
|
||||
focusDate: state.getIn(['compose', 'focusDate']),
|
||||
caretPosition: state.getIn(['compose', 'caretPosition']),
|
||||
preselectDate: state.getIn(['compose', 'preselectDate']),
|
||||
isSubmitting: state.getIn(['compose', 'is_submitting']),
|
||||
isEditing: state.getIn(['compose', 'id']) !== null,
|
||||
isChangingUpload: state.getIn(['compose', 'is_changing_upload']),
|
||||
isUploading: state.getIn(['compose', 'is_uploading']),
|
||||
layout: state.getIn(['local_settings', 'layout']),
|
||||
media: state.getIn(['compose', 'media_attachments']),
|
||||
preselectDate: state.getIn(['compose', 'preselectDate']),
|
||||
privacy: state.getIn(['compose', 'privacy']),
|
||||
sideArm: sideArmPrivacy,
|
||||
sensitive: state.getIn(['compose', 'sensitive']),
|
||||
showSearch: state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']),
|
||||
spoiler: spoilersAlwaysOn || state.getIn(['compose', 'spoiler']),
|
||||
spoilerText: state.getIn(['compose', 'spoiler_text']),
|
||||
suggestions: state.getIn(['compose', 'suggestions']),
|
||||
text: state.getIn(['compose', 'text']),
|
||||
anyMedia: state.getIn(['compose', 'media_attachments']).size > 0,
|
||||
spoilersAlwaysOn: spoilersAlwaysOn,
|
||||
mediaDescriptionConfirmation: state.getIn(['local_settings', 'confirm_missing_media_description']),
|
||||
preselectOnReply: state.getIn(['local_settings', 'preselect_on_reply']),
|
||||
isInReply: state.getIn(['compose', 'in_reply_to']) !== null,
|
||||
lang: state.getIn(['compose', 'language']),
|
||||
};
|
||||
}
|
||||
advancedOptions: state.getIn(['compose', 'advanced_options']),
|
||||
layout: state.getIn(['local_settings', 'layout']),
|
||||
media: state.getIn(['compose', 'media_attachments']),
|
||||
sideArm: sideArmPrivacy(state),
|
||||
sensitive: state.getIn(['compose', 'sensitive']),
|
||||
showSearch: state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']),
|
||||
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 }) => ({
|
||||
|
||||
onChange(text) {
|
||||
onChange (text) {
|
||||
dispatch(changeCompose(text));
|
||||
},
|
||||
|
||||
onSubmit(routerHistory) {
|
||||
dispatch(submitCompose(routerHistory));
|
||||
onSubmit (router) {
|
||||
dispatch(submitCompose(router));
|
||||
},
|
||||
|
||||
onClearSuggestions() {
|
||||
onClearSuggestions () {
|
||||
dispatch(clearComposeSuggestions());
|
||||
},
|
||||
|
||||
onFetchSuggestions(token) {
|
||||
onFetchSuggestions (token) {
|
||||
dispatch(fetchComposeSuggestions(token));
|
||||
},
|
||||
|
||||
onSuggestionSelected(position, token, suggestion, path) {
|
||||
onSuggestionSelected (position, token, suggestion, path) {
|
||||
dispatch(selectComposeSuggestion(position, token, suggestion, path));
|
||||
},
|
||||
|
||||
onChangeSpoilerText(text) {
|
||||
onChangeSpoilerText (text) {
|
||||
dispatch(changeComposeSpoilerText(text));
|
||||
},
|
||||
|
||||
onPaste(files) {
|
||||
onPaste (files) {
|
||||
dispatch(uploadCompose(files));
|
||||
},
|
||||
|
||||
onPickEmoji(position, emoji) {
|
||||
onPickEmoji (position, emoji) {
|
||||
dispatch(insertEmojiCompose(position, emoji));
|
||||
},
|
||||
|
||||
|
|
Loading…
Reference in New Issue