Progress
This commit is contained in:
64
app/javascript/gabsocial/features/bookmark_collections.js
Normal file
64
app/javascript/gabsocial/features/bookmark_collections.js
Normal file
@@ -0,0 +1,64 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { connect } from 'react-redux'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import { fetchBookmarkCollections } from '../actions/bookmarks'
|
||||
import ColumnIndicator from '../components/column_indicator'
|
||||
import List from '../components/list'
|
||||
|
||||
class BookmarkCollections extends ImmutablePureComponent {
|
||||
|
||||
componentDidMount() {
|
||||
this.props.onFetchBookmarkCollections()
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
isLoading,
|
||||
isError,
|
||||
bookmarkCollections,
|
||||
} = this.props
|
||||
|
||||
if (isError) {
|
||||
return <ColumnIndicator type='error' message='Error fetching bookmark collections' />
|
||||
}
|
||||
|
||||
const listItems = shortcuts.map((s) => ({
|
||||
to: s.get('to'),
|
||||
title: s.get('title'),
|
||||
image: s.get('image'),
|
||||
}))
|
||||
|
||||
return (
|
||||
<List
|
||||
scrollKey='bookmark-collections'
|
||||
emptyMessage='You have no bookmark collections'
|
||||
items={listItems}
|
||||
showLoading={isLoading}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
isError: state.getIn(['bookmark_collections', 'isError']),
|
||||
isLoading: state.getIn(['bookmark_collections', 'isLoading']),
|
||||
shortcuts: state.getIn(['bookmark_collections', 'items']),
|
||||
})
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
onFetchBookmarkCollections() {
|
||||
dispatch(fetchBookmarkCollections())
|
||||
},
|
||||
})
|
||||
|
||||
BookmarkCollections.propTypes = {
|
||||
isLoading: PropTypes.bool.isRequired,
|
||||
isError: PropTypes.bool.isRequired,
|
||||
onFetchBookmarkCollections: PropTypes.func.isRequired,
|
||||
bookmarkCollections: ImmutablePropTypes.list,
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(BookmarkCollections)
|
||||
@@ -1,75 +1,94 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { defineMessages, injectIntl } from 'react-intl'
|
||||
import { connect } from 'react-redux'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import { length } from 'stringz'
|
||||
import { isMobile } from '../../../utils/is_mobile'
|
||||
import { countableText } from '../../ui/util/counter'
|
||||
import {
|
||||
CX,
|
||||
MAX_POST_CHARACTER_COUNT,
|
||||
ALLOWED_AROUND_SHORT_CODE,
|
||||
BREAKPOINT_EXTRA_SMALL,
|
||||
BREAKPOINT_EXTRA_EXTRA_SMALL,
|
||||
BREAKPOINT_MEDIUM,
|
||||
MODAL_COMPOSE,
|
||||
POPOVER_COMPOSE_POST_DESTINATION,
|
||||
} from '../../../constants'
|
||||
import AutosuggestTextbox from '../../../components/autosuggest_textbox'
|
||||
import Responsive from '../../ui/util/responsive_component'
|
||||
import ResponsiveClassesComponent from '../../ui/util/responsive_classes_component'
|
||||
import { openModal } from '../../../actions/modal'
|
||||
import { openPopover } from '../../../actions/popover'
|
||||
import Avatar from '../../../components/avatar'
|
||||
import Button from '../../../components/button'
|
||||
import EmojiPickerButton from './emoji_picker_button'
|
||||
import PollButton from './poll_button'
|
||||
import PollForm from './poll_form'
|
||||
import SchedulePostButton from './schedule_post_button'
|
||||
import SpoilerButton from './spoiler_button'
|
||||
import ExpiresPostButton from './expires_post_button'
|
||||
import RichTextEditorButton from './rich_text_editor_button'
|
||||
import StatusContainer from '../../../containers/status_container'
|
||||
import StatusVisibilityButton from './status_visibility_button'
|
||||
import UploadButton from './media_upload_button'
|
||||
import UploadForm from './upload_form'
|
||||
import Input from '../../../components/input'
|
||||
import Text from '../../../components/text'
|
||||
import Icon from '../../../components/icon'
|
||||
import ComposeExtraButtonList from './compose_extra_button_list'
|
||||
import Text from '../../../components/text'
|
||||
|
||||
class ComposeDestinationHeader extends ImmutablePureComponent {
|
||||
|
||||
handleOnClick = () => {
|
||||
this.props.onOpenPopover(this.desinationBtn)
|
||||
}
|
||||
|
||||
handleOnExpand = () => {
|
||||
this.props.onOpenModal()
|
||||
}
|
||||
|
||||
setDestinationBtn = (c) => {
|
||||
this.desinationBtn = c
|
||||
}
|
||||
|
||||
render() {
|
||||
const { account } = this.props
|
||||
const { account, isModal } = this.props
|
||||
|
||||
const title = 'Post to timeline'
|
||||
|
||||
return (
|
||||
<div className={[_s.d, _s.flexRow, _s.aiCenter, _s.bgPrimary, _s.borderBottom1PX, _s.borderTop1PX, _s.borderColorSecondary, _s.mb5, _s.mt5, _s.px15, _s.w100PC, _s.h40PX].join(' ')}>
|
||||
<Avatar account={account} size={28} />
|
||||
<div className={[_s.ml15].join(' ')}>
|
||||
<Button
|
||||
isNarrow
|
||||
radiusSmall
|
||||
backgroundColor='tertiary'
|
||||
color='primary'
|
||||
onClick={this.handleOnClick}
|
||||
>
|
||||
<Text color='inherit' size='small' className={_s.jcCenter}>
|
||||
{title}
|
||||
<Icon id='caret-down' size='8px' className={_s.ml5} />
|
||||
</Text>
|
||||
</Button>
|
||||
<div className={[_s.d, _s.flexRow, _s.aiCenter, _s.bgPrimary, _s.w100PC, _s.h40PX, _s.pr15].join(' ')}>
|
||||
<div className={[_s.d, _s.flexRow, _s.aiCenter, _s.pl15, _s.flexGrow1, _s.mrAuto, _s.h40PX].join(' ')}>
|
||||
<Avatar account={account} size={28} />
|
||||
<div className={[_s.ml15].join(' ')}>
|
||||
<Button
|
||||
isNarrow
|
||||
isOutline
|
||||
radiusSmall
|
||||
buttonRef={this.setDestinationBtn}
|
||||
backgroundColor='secondary'
|
||||
color='primary'
|
||||
onClick={this.handleOnClick}
|
||||
className={[_s.border1PX, _s.borderColorPrimary].join(' ')}
|
||||
>
|
||||
<Text color='inherit' size='small' className={_s.jcCenter}>
|
||||
{title}
|
||||
<Icon id='caret-down' size='8px' className={_s.ml5} />
|
||||
</Text>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
!isModal &&
|
||||
<Button
|
||||
isText
|
||||
isNarrow
|
||||
backgroundColor='none'
|
||||
color='tertiary'
|
||||
icon='fullscreen'
|
||||
onClick={this.handleOnExpand}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
onOpenModal() {
|
||||
dispatch(openModal(MODAL_COMPOSE))
|
||||
},
|
||||
onOpenPopover(targetRef) {
|
||||
dispatch(openPopover(POPOVER_COMPOSE_POST_DESTINATION, {
|
||||
targetRef,
|
||||
position: 'bottom',
|
||||
}))
|
||||
},
|
||||
})
|
||||
|
||||
ComposeDestinationHeader.propTypes = {
|
||||
account: ImmutablePropTypes.map,
|
||||
isModal: PropTypes.bool,
|
||||
onOpenModal: PropTypes.func.isRequired,
|
||||
onOpenPopover: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
export default ComposeDestinationHeader
|
||||
export default connect(null, mapDispatchToProps)(ComposeDestinationHeader)
|
||||
@@ -22,14 +22,14 @@ class ComposeExtraButton extends React.PureComponent {
|
||||
|
||||
const containerClasses = CX({
|
||||
d: 1,
|
||||
mr5: 1,
|
||||
jcCenter: 1,
|
||||
h40PX: 1,
|
||||
mr5: 1,
|
||||
})
|
||||
|
||||
const btnClasses = CX({
|
||||
d: 1,
|
||||
circle: 1,
|
||||
circle: small,
|
||||
noUnderline: 1,
|
||||
font: 1,
|
||||
cursorPointer: 1,
|
||||
@@ -37,21 +37,25 @@ class ComposeExtraButton extends React.PureComponent {
|
||||
outlineNone: 1,
|
||||
bgTransparent: 1,
|
||||
flexRow: 1,
|
||||
aiCenter: 1,
|
||||
// jcCenter: !small,
|
||||
bgSubtle_onHover: !active,
|
||||
bgBrandLight: active,
|
||||
py10: 1,
|
||||
px10: 1,
|
||||
px10: small,
|
||||
radiusSmall: !small,
|
||||
})
|
||||
|
||||
const iconClasses = CX(iconClassName, {
|
||||
const iconClasses = CX(active ? null : iconClassName, {
|
||||
cSecondary: !active,
|
||||
cWhite: active,
|
||||
mr10: 1,
|
||||
mr10: !small,
|
||||
py2: small,
|
||||
ml10: small,
|
||||
ml10: !small,
|
||||
px2: small,
|
||||
})
|
||||
|
||||
const iconSize = !small ? '18px' : '16px'
|
||||
const iconSize = '16px'
|
||||
const textColor = !active ? 'primary' : 'white'
|
||||
|
||||
return (
|
||||
@@ -65,13 +69,13 @@ class ComposeExtraButton extends React.PureComponent {
|
||||
backgroundColor='none'
|
||||
iconClassName={iconClasses}
|
||||
icon={icon}
|
||||
iconSize={iconSize}
|
||||
iconSize='16px'
|
||||
buttonRef={!children ? buttonRef : undefined}
|
||||
>
|
||||
{ children }
|
||||
{
|
||||
!small &&
|
||||
<Text color={textColor} weight='medium' className={[_s.pr5].join(' ')}>
|
||||
<Text color={textColor} weight='medium' className={[_s.pr10].join(' ')}>
|
||||
{title}
|
||||
</Text>
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
} from '../../../constants'
|
||||
import Responsive from '../../ui/util/responsive_component'
|
||||
import ResponsiveClassesComponent from '../../ui/util/responsive_classes_component'
|
||||
import Text from '../../../components/text'
|
||||
import EmojiPickerButton from './emoji_picker_button'
|
||||
import PollButton from './poll_button'
|
||||
import SchedulePostButton from './schedule_post_button'
|
||||
@@ -22,6 +23,7 @@ class ComposeExtraButtonList extends React.PureComponent {
|
||||
|
||||
state = {
|
||||
height: initialState.height,
|
||||
width: initialState.width,
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@@ -31,9 +33,9 @@ class ComposeExtraButtonList extends React.PureComponent {
|
||||
}
|
||||
|
||||
handleResize = () => {
|
||||
const { height } = getWindowDimension()
|
||||
const { height, width } = getWindowDimension()
|
||||
|
||||
this.setState({ height })
|
||||
this.setState({ height, width })
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
@@ -48,26 +50,33 @@ class ComposeExtraButtonList extends React.PureComponent {
|
||||
edit,
|
||||
hidePro,
|
||||
isModal,
|
||||
isStandalone,
|
||||
formLocation,
|
||||
} = this.props
|
||||
const { height } = this.state
|
||||
const { height, width } = this.state
|
||||
|
||||
const small = (height <= 660 || isModal) && !isStandalone
|
||||
const isXS = width <= BREAKPOINT_EXTRA_SMALL
|
||||
const isStandalone = formLocation === 'standalone'
|
||||
const isTimeline = formLocation === 'timeline'
|
||||
const small = (!isModal && isXS && !isStandalone) || isTimeline
|
||||
|
||||
console.log("small, formLocation:", small, formLocation)
|
||||
|
||||
const containerClasses = CX({
|
||||
d: 1,
|
||||
w100PC: 1,
|
||||
bgPrimary: 1,
|
||||
px15: 1,
|
||||
py10: 1,
|
||||
px5: 1,
|
||||
py5: 1,
|
||||
mb10: 1,
|
||||
mtAuto: 1,
|
||||
boxShadowBlockY: 1,
|
||||
topLeftRadiusSmall: 1,
|
||||
radiusSmall: 1,
|
||||
borderTop1PX: 1,
|
||||
borderBottom1PX: 1,
|
||||
boxShadowBlock: 1,
|
||||
borderColorSecondary: 1,
|
||||
topRightRadiusSmall: 1,
|
||||
flexRow: small,
|
||||
overflowXScroll: small,
|
||||
noScrollbar: small,
|
||||
flexWrap: 1,
|
||||
flexRow: (small || !isTimeline || isXS) && !isStandalone,
|
||||
jcSpaceAround: isXS,
|
||||
})
|
||||
|
||||
return (
|
||||
@@ -79,8 +88,8 @@ class ComposeExtraButtonList extends React.PureComponent {
|
||||
<SpoilerButton small={small} />
|
||||
{ !hidePro && !edit && <SchedulePostButton small={small} /> }
|
||||
{ !hidePro && !edit && <ExpiresPostButton small={small} /> }
|
||||
{ !hidePro && <RichTextEditorButton small={small} /> }
|
||||
</div>
|
||||
{ !hidePro && !isXS && <RichTextEditorButton small={small} /> }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -90,7 +99,7 @@ ComposeExtraButtonList.propTypes = {
|
||||
edit: PropTypes.bool,
|
||||
isMatch: PropTypes.bool,
|
||||
isModal: PropTypes.bool,
|
||||
isStandalone: PropTypes.bool,
|
||||
formLocation: PropTypes.string,
|
||||
}
|
||||
|
||||
export default ComposeExtraButtonList
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { NavLink } from 'react-router-dom'
|
||||
import { defineMessages, injectIntl } from 'react-intl'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
@@ -11,7 +12,6 @@ import {
|
||||
MAX_POST_CHARACTER_COUNT,
|
||||
ALLOWED_AROUND_SHORT_CODE,
|
||||
BREAKPOINT_EXTRA_SMALL,
|
||||
BREAKPOINT_EXTRA_EXTRA_SMALL,
|
||||
BREAKPOINT_MEDIUM,
|
||||
} from '../../../constants'
|
||||
import AutosuggestTextbox from '../../../components/autosuggest_textbox'
|
||||
@@ -62,80 +62,70 @@ class ComposeForm extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
handleComposeFocus = () => {
|
||||
this.setState({
|
||||
composeFocused: true,
|
||||
});
|
||||
this.setState({ composeFocused: true })
|
||||
}
|
||||
|
||||
handleKeyDown = (e) => {
|
||||
if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
|
||||
this.handleSubmit();
|
||||
this.handleSubmit()
|
||||
}
|
||||
}
|
||||
|
||||
handleClick = (e) => {
|
||||
const { isStandalone, isModalOpen, shouldCondense } = this.props
|
||||
|
||||
const { isModalOpen, shouldCondense } = this.props
|
||||
|
||||
if (!this.form) return false
|
||||
if (e.target) {
|
||||
if (e.target.classList.contains('react-datepicker__time-list-item')) return false
|
||||
}
|
||||
if (!this.form.contains(e.target)) {
|
||||
this.handleClickOutside()
|
||||
} else {
|
||||
// : todo :
|
||||
// if mobile go to /compose else openModal
|
||||
if (!isStandalone && !isModalOpen && !shouldCondense) {
|
||||
this.props.openComposeModal()
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleClickOutside = () => {
|
||||
const { shouldCondense, scheduledAt, text, isModalOpen } = this.props;
|
||||
const condensed = shouldCondense && !text;
|
||||
const { shouldCondense, scheduledAt, text, isModalOpen } = this.props
|
||||
const condensed = shouldCondense && !text
|
||||
|
||||
if (condensed && scheduledAt && !isModalOpen) { //Reset scheduled date if condensing
|
||||
this.props.setScheduledAt(null);
|
||||
this.props.setScheduledAt(null)
|
||||
}
|
||||
|
||||
this.setState({
|
||||
composeFocused: false,
|
||||
});
|
||||
this.setState({ composeFocused: false })
|
||||
}
|
||||
|
||||
handleSubmit = () => {
|
||||
// if (this.props.text !== this.autosuggestTextarea.textbox.value) {
|
||||
// Something changed the text inside the textarea (e.g. browser extensions like Grammarly)
|
||||
// Update the state to match the current text
|
||||
// this.props.onChange(this.autosuggestTextarea.textbox.value);
|
||||
// this.props.onChange(this.autosuggestTextarea.textbox.value)
|
||||
// }
|
||||
|
||||
// Submit disabled:
|
||||
const { isSubmitting, isChangingUpload, isUploading, anyMedia, groupId, autoJoinGroup } = this.props
|
||||
const fulltext = [this.props.spoilerText, countableText(this.props.text)].join('');
|
||||
const fulltext = [this.props.spoilerText, countableText(this.props.text)].join('')
|
||||
|
||||
if (isSubmitting || isUploading || isChangingUpload || length(fulltext) > MAX_POST_CHARACTER_COUNT || (fulltext.length !== 0 && fulltext.trim().length === 0 && !anyMedia)) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
this.props.onSubmit(groupId, this.props.replyToId, this.context.router, autoJoinGroup)
|
||||
}
|
||||
|
||||
onSuggestionsClearRequested = () => {
|
||||
this.props.onClearSuggestions();
|
||||
this.props.onClearSuggestions()
|
||||
}
|
||||
|
||||
onSuggestionsFetchRequested = (token) => {
|
||||
this.props.onFetchSuggestions(token);
|
||||
this.props.onFetchSuggestions(token)
|
||||
}
|
||||
|
||||
onSuggestionSelected = (tokenStart, token, value) => {
|
||||
this.props.onSuggestionSelected(tokenStart, token, value, ['text']);
|
||||
this.props.onSuggestionSelected(tokenStart, token, value, ['text'])
|
||||
}
|
||||
|
||||
onSpoilerSuggestionSelected = (tokenStart, token, value) => {
|
||||
this.props.onSuggestionSelected(tokenStart, token, value, ['spoiler_text']);
|
||||
this.props.onSuggestionSelected(tokenStart, token, value, ['spoiler_text'])
|
||||
}
|
||||
|
||||
handleChangeSpoilerText = (value) => {
|
||||
@@ -143,11 +133,11 @@ class ComposeForm extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
document.addEventListener('click', this.handleClick, false);
|
||||
document.addEventListener('click', this.handleClick, false)
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener('click', this.handleClick, false);
|
||||
document.removeEventListener('click', this.handleClick, false)
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
@@ -156,24 +146,24 @@ class ComposeForm extends ImmutablePureComponent {
|
||||
// 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;
|
||||
// - 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 !== prevProps.focusDate) {
|
||||
let selectionEnd, selectionStart;
|
||||
let selectionEnd, selectionStart
|
||||
|
||||
if (this.props.preselectDate !== prevProps.preselectDate) {
|
||||
selectionEnd = this.props.text.length;
|
||||
selectionStart = this.props.text.search(/\s/) + 1;
|
||||
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;
|
||||
selectionStart = this.props.caretPosition
|
||||
selectionEnd = this.props.caretPosition
|
||||
} else {
|
||||
selectionEnd = this.props.text.length;
|
||||
selectionStart = selectionEnd;
|
||||
selectionEnd = this.props.text.length
|
||||
selectionStart = selectionEnd
|
||||
}
|
||||
|
||||
// this.autosuggestTextarea.textbox.setSelectionRange(selectionStart, selectionEnd);
|
||||
// this.autosuggestTextarea.textbox.focus();
|
||||
// this.autosuggestTextarea.textbox.setSelectionRange(selectionStart, selectionEnd)
|
||||
// this.autosuggestTextarea.textbox.focus()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,7 +180,6 @@ class ComposeForm extends ImmutablePureComponent {
|
||||
intl,
|
||||
account,
|
||||
onPaste,
|
||||
showSearch,
|
||||
anyMedia,
|
||||
shouldCondense,
|
||||
autoFocus,
|
||||
@@ -208,218 +197,151 @@ class ComposeForm extends ImmutablePureComponent {
|
||||
isSubmitting,
|
||||
isPro,
|
||||
hidePro,
|
||||
isStandalone,
|
||||
dontOpenModal,
|
||||
formLocation,
|
||||
} = this.props
|
||||
|
||||
const disabled = isSubmitting
|
||||
const text = [this.props.spoilerText, countableText(this.props.text)].join('');
|
||||
const text = [this.props.spoilerText, countableText(this.props.text)].join('')
|
||||
const disabledButton = isSubmitting || isUploading || isChangingUpload || length(text) > MAX_POST_CHARACTER_COUNT || (length(text.trim()) === 0 && !anyMedia)
|
||||
const shouldAutoFocus = autoFocus && !showSearch && !isMobile(window.innerWidth)
|
||||
const shouldAutoFocus = autoFocus && !isMobile(window.innerWidth)
|
||||
|
||||
const parentContainerClasses = CX({
|
||||
const containerClasses = CX({
|
||||
d: 1,
|
||||
w100PC: 1,
|
||||
flexRow: !shouldCondense,
|
||||
pb10: !shouldCondense,
|
||||
pb10: 1,
|
||||
calcMaxH410PX: 1,
|
||||
minH200PX: isModalOpen && isMatch,
|
||||
overflowYScroll: 1,
|
||||
boxShadowBlock: 1,
|
||||
borderTop1PX: 1,
|
||||
borderColorSecondary: 1,
|
||||
})
|
||||
|
||||
const childContainerClasses = CX({
|
||||
d: 1,
|
||||
flexWrap: 1,
|
||||
overflowHidden: 1,
|
||||
flex1: 1,
|
||||
minH28PX: 1,
|
||||
py2: shouldCondense,
|
||||
aiEnd: shouldCondense,
|
||||
flexRow: shouldCondense,
|
||||
radiusSmall: shouldCondense,
|
||||
bgSubtle: shouldCondense,
|
||||
px5: shouldCondense,
|
||||
})
|
||||
|
||||
const actionsContainerClasses = CX({
|
||||
d: 1,
|
||||
flexRow: 1,
|
||||
aiCenter: !shouldCondense,
|
||||
aiStart: shouldCondense,
|
||||
mt10: !shouldCondense,
|
||||
px10: !shouldCondense,
|
||||
mlAuto: shouldCondense,
|
||||
flexWrap: !shouldCondense,
|
||||
})
|
||||
|
||||
const commentPublishBtnClasses = CX({
|
||||
d: 1,
|
||||
jcCenter: 1,
|
||||
displayNone: length(this.props.text) === 0,
|
||||
})
|
||||
const textbox = (
|
||||
<AutosuggestTextbox
|
||||
ref={(isModalOpen && shouldCondense) ? null : this.setAutosuggestTextarea}
|
||||
placeholder={intl.formatMessage((shouldCondense || !!reduxReplyToId) && isMatch ? messages.commentPlaceholder : messages.placeholder)}
|
||||
disabled={disabled}
|
||||
value={this.props.text}
|
||||
valueMarkdown={this.props.markdown}
|
||||
onChange={this.handleChange}
|
||||
suggestions={this.props.suggestions}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
onFocus={this.handleComposeFocus}
|
||||
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
|
||||
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
|
||||
onSuggestionSelected={this.onSuggestionSelected}
|
||||
onPaste={onPaste}
|
||||
autoFocus={shouldAutoFocus}
|
||||
small={shouldCondense}
|
||||
isModalOpen={isModalOpen}
|
||||
isPro={isPro}
|
||||
isEdit={!!edit}
|
||||
id='main-composer'
|
||||
/>
|
||||
)
|
||||
|
||||
if (shouldCondense) {
|
||||
return (
|
||||
<div className={[_s.d, _s.w100PC].join(' ')}>
|
||||
<div className={[_s.d, _s.flexRow, _s.w100PC].join(' ')}>
|
||||
<div className={[_s.d, _s.flexRow, _s.w100PC, _s.aiCenter].join(' ')}>
|
||||
<div className={[_s.d, _s.mr10].join(' ')}>
|
||||
<Avatar account={account} size={28} noHover />
|
||||
<Avatar account={account} size={30} noHover />
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={[_s.d, _s.flexWrap, _s.overflowHidden, _s.flex1, _s.minH28PX, _s.py2, _s.aiEnd, _s.flexRow, _s.radiusSmall, _s.bgSubtle, _s.px5].join(' ')}
|
||||
className={[_s.d, _s.flexWrap, _s.overflowHidden, _s.flex1, _s.minH28PX, _s.py5, _s.aiEnd, _s.flexRow, _s.radiusSmall, _s.bgSubtle, _s.px5].join(' ')}
|
||||
ref={this.setForm}
|
||||
onClick={this.handleClick}
|
||||
>
|
||||
|
||||
<AutosuggestTextbox
|
||||
ref={(isModalOpen && shouldCondense) ? null : this.setAutosuggestTextarea}
|
||||
placeholder={intl.formatMessage(messages.commentPlaceholder)}
|
||||
disabled={disabled}
|
||||
value={this.props.text}
|
||||
onChange={this.handleChange}
|
||||
suggestions={this.props.suggestions}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
onFocus={this.handleComposeFocus}
|
||||
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
|
||||
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
|
||||
onSuggestionSelected={this.onSuggestionSelected}
|
||||
onPaste={onPaste}
|
||||
autoFocus={shouldAutoFocus}
|
||||
small={shouldCondense}
|
||||
isPro={isPro}
|
||||
isEdit={!!edit}
|
||||
id='comment-composer'
|
||||
/>
|
||||
|
||||
<div className={[_s.d, _s.flexRow, _s.aiStart, _s.mlAuto].join(' ')}>
|
||||
<div className={[_s.d, _s.flexRow, _s.mrAuto].join(' ')}>
|
||||
<div className={commentPublishBtnClasses}>
|
||||
<Button
|
||||
isNarrow
|
||||
onClick={this.handleSubmit}
|
||||
isDisabled={disabledButton}
|
||||
className={_s.px10}
|
||||
>
|
||||
{intl.formatMessage(scheduledAt ? messages.schedulePost : messages.post)}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{ textbox }
|
||||
{ isMatch && <ComposeFormSubmitButton type='comment' /> }
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{
|
||||
(isUploading || anyMedia) &&
|
||||
(isUploading || anyMedia) && isMatch &&
|
||||
<div className={[_s.d, _s.w100PC, _s.pl35, _s.mt5].join(' ')}>
|
||||
<UploadForm replyToId={replyToId} isModalOpen={isModalOpen} />
|
||||
<UploadForm isModalOpen={isModalOpen} />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (isStandalone || isModalOpen) {
|
||||
return (
|
||||
<div className={[_s.d, _s.w100PC, _s.flexGrow1, _s.bgTertiary].join(' ')}>
|
||||
|
||||
<div className={[_s.d, _s.pb10, _s.calcMaxH370PX, _s.overflowYScroll, _s.boxShadowBlock, _s.borderBottom1PX, _s.borderColorSecondary].join(' ')}>
|
||||
<ComposeDestinationHeader account={account} />
|
||||
|
||||
<div
|
||||
className={[_s.d, _s.bgPrimary, _s.boxShadowBlock, _s.w100PC, _s.minH200PX, _s.pb10, _s.borderBottom1PX, _s.borderTop1PX, _s.borderColorSecondary].join(' ')}
|
||||
ref={this.setForm}
|
||||
onClick={this.handleClick}
|
||||
>
|
||||
|
||||
{
|
||||
!!reduxReplyToId && isModalOpen && isMatch &&
|
||||
<div className={[_s.d, _s.px15, _s.py10, _s.mt5].join(' ')}>
|
||||
<StatusContainer contextType='compose' id={reduxReplyToId} isChild />
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
!!spoiler &&
|
||||
<div className={[_s.d, _s.px15, _s.py10, _s.borderBottom1PX, _s.borderColorSecondary].join(' ')}>
|
||||
<Input
|
||||
placeholder={intl.formatMessage(messages.spoiler_placeholder)}
|
||||
value={this.props.spoilerText}
|
||||
onChange={this.handleChangeSpoilerText}
|
||||
disabled={!this.props.spoiler}
|
||||
prependIcon='warning'
|
||||
maxLength={256}
|
||||
id='cw-spoiler-input'
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
||||
<AutosuggestTextbox
|
||||
ref={(isModalOpen && shouldCondense) ? null : this.setAutosuggestTextarea}
|
||||
placeholder={intl.formatMessage((shouldCondense || !!reduxReplyToId) && isMatch ? messages.commentPlaceholder : messages.placeholder)}
|
||||
disabled={disabled}
|
||||
value={this.props.text}
|
||||
valueMarkdown={this.props.markdown}
|
||||
onChange={this.handleChange}
|
||||
suggestions={this.props.suggestions}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
onFocus={this.handleComposeFocus}
|
||||
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
|
||||
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
|
||||
onSuggestionSelected={this.onSuggestionSelected}
|
||||
onPaste={onPaste}
|
||||
autoFocus={shouldAutoFocus}
|
||||
small={shouldCondense}
|
||||
isPro={isPro}
|
||||
isEdit={!!edit}
|
||||
id='main-composer'
|
||||
/>
|
||||
|
||||
{
|
||||
(isUploading || anyMedia) &&
|
||||
<div className={[_s.d, _s.px15, _s.mt5].join(' ')}>
|
||||
<UploadForm replyToId={replyToId} isModalOpen={isModalOpen} />
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
!edit && hasPoll &&
|
||||
<div className={[_s.d, _s.px15, _s.mt5].join(' ')}>
|
||||
<PollForm replyToId={replyToId} />
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
!!quoteOfId && isModalOpen && isMatch &&
|
||||
<div className={[_s.d, _s.px15, _s.py10, _s.mt5].join(' ')}>
|
||||
<StatusContainer contextType='compose' id={quoteOfId} isChild />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{ !isModalOpen && <ComposeFormSubmitButton /> }
|
||||
|
||||
<ComposeExtraButtonList isStandalone={isStandalone} isMatch={isMatch} hidePro={hidePro} edit={edit} isModal={isModalOpen} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={[_s.d, _s.w100PC, _s.pb10, _s.flexWrap, _s.overflowHidden, _s.flex1].join(' ')}
|
||||
ref={this.setForm}
|
||||
onClick={this.handleClick}
|
||||
>
|
||||
<Text className={[_s.d, _s.px15, _s.pt15, _s.pb10].join(' ')} size='medium' color='tertiary'>
|
||||
{intl.formatMessage((shouldCondense || !!reduxReplyToId) && isMatch ? messages.commentPlaceholder : messages.placeholder)}
|
||||
</Text>
|
||||
<div className={[_s.d, _s.flexRow, _s.aiCenter, _s.mt5, _s.px10, _s.flexWrap].join(' ')}>
|
||||
<UploadButton />
|
||||
<EmojiPickerButton isMatch={isMatch} />
|
||||
<PollButton />
|
||||
<MoreButton />
|
||||
<ComposeFormSubmitButton />
|
||||
<div className={[_s.d, _s.w100PC, _s.flexGrow1, _s.bgPrimary].join(' ')}>
|
||||
<div className={[_s.d, _s.calcMaxH410PX, _s.overflowYScroll].join(' ')}>
|
||||
|
||||
<Responsive min={BREAKPOINT_EXTRA_SMALL}>
|
||||
<ComposeDestinationHeader account={account} isModal={isModalOpen} />
|
||||
</Responsive>
|
||||
|
||||
<div className={containerClasses} ref={this.setForm} onClick={this.handleClick}>
|
||||
{
|
||||
!!reduxReplyToId && isModalOpen && isMatch &&
|
||||
<div className={[_s.d, _s.px15, _s.py10, _s.mt5].join(' ')}>
|
||||
<StatusContainer contextType='compose' id={reduxReplyToId} isChild />
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
!!spoiler &&
|
||||
<div className={[_s.d, _s.px15, _s.py10, _s.borderBottom1PX, _s.borderColorSecondary].join(' ')}>
|
||||
<Input
|
||||
placeholder={intl.formatMessage(messages.spoiler_placeholder)}
|
||||
value={this.props.spoilerText}
|
||||
onChange={this.handleChangeSpoilerText}
|
||||
disabled={!this.props.spoiler}
|
||||
prependIcon='warning'
|
||||
maxLength={256}
|
||||
id='cw-spoiler-input'
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
||||
{ textbox }
|
||||
|
||||
{
|
||||
(isUploading || anyMedia) &&
|
||||
<div className={[_s.d, _s.px15, _s.mt5].join(' ')}>
|
||||
<div className={[_s.d, _s.borderTop1PX, _s.borderColorSecondary].join(' ')} />
|
||||
<UploadForm />
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
!edit && hasPoll &&
|
||||
<div className={[_s.d, _s.px15, _s.mt5].join(' ')}>
|
||||
<PollForm />
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
!!quoteOfId && isModalOpen && isMatch &&
|
||||
<div className={[_s.d, _s.px15, _s.py10, _s.mt5].join(' ')}>
|
||||
<StatusContainer contextType='compose' id={quoteOfId} isChild />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div className={[_s.d, _s.posAbs, _s.z2, _s.left0, _s.right0, _s.bottom0, _s.top0].join(' ')} />
|
||||
|
||||
<div className={[_s.d, _s.px10].join(' ')}>
|
||||
<ComposeExtraButtonList formLocation={formLocation} isMatch={isMatch} hidePro={hidePro} edit={edit} isModal={isModalOpen} />
|
||||
</div>
|
||||
|
||||
{
|
||||
(!disabledButton || isModalOpen) && isMatch &&
|
||||
<div className={[_s.d, _s.mb10, _s.px10].join(' ')}>
|
||||
<ComposeFormSubmitButton type='block' />
|
||||
</div>
|
||||
}
|
||||
|
||||
<Responsive max={BREAKPOINT_EXTRA_SMALL}>
|
||||
{
|
||||
formLocation === 'timeline' &&
|
||||
<NavLink to='/compose' className={[_s.d, _s.z4, _s.posAbs, _s.top0, _s.left0, _s.right0, _s.bottom0].join(' ')} />
|
||||
}
|
||||
</Responsive>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -450,9 +372,7 @@ ComposeForm.propTypes = {
|
||||
onFetchSuggestions: PropTypes.func.isRequired,
|
||||
onSuggestionSelected: PropTypes.func.isRequired,
|
||||
onChangeSpoilerText: PropTypes.func.isRequired,
|
||||
openComposeModal: PropTypes.func.isRequired,
|
||||
onPaste: PropTypes.func.isRequired,
|
||||
showSearch: PropTypes.bool,
|
||||
anyMedia: PropTypes.bool,
|
||||
shouldCondense: PropTypes.bool,
|
||||
autoFocus: PropTypes.bool,
|
||||
@@ -466,11 +386,7 @@ ComposeForm.propTypes = {
|
||||
isPro: PropTypes.bool,
|
||||
hidePro: PropTypes.bool,
|
||||
autoJoinGroup: PropTypes.bool,
|
||||
isStandalone: PropTypes.bool,
|
||||
}
|
||||
|
||||
ComposeForm.defaultProps = {
|
||||
showSearch: false,
|
||||
formLocation: PropTypes.string,
|
||||
}
|
||||
|
||||
export default injectIntl(ComposeForm)
|
||||
@@ -1,30 +1,80 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { CX } from '../../../constants'
|
||||
import { connect } from 'react-redux'
|
||||
import { defineMessages, injectIntl } from 'react-intl'
|
||||
import { length } from 'stringz'
|
||||
import { countableText } from '../../ui/util/counter'
|
||||
import { submitCompose } from '../../../actions/compose'
|
||||
import {
|
||||
CX,
|
||||
MAX_POST_CHARACTER_COUNT,
|
||||
} from '../../../constants'
|
||||
import Button from '../../../components/button'
|
||||
import Text from '../../../components/text'
|
||||
|
||||
class ComposeFormSubmitButton extends React.PureComponent {
|
||||
|
||||
handleSubmit = () => {
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
intl,
|
||||
title,
|
||||
active,
|
||||
small,
|
||||
disabledButton,
|
||||
type,
|
||||
|
||||
edit,
|
||||
text,
|
||||
isSubmitting,
|
||||
isChangingUpload,
|
||||
isUploading,
|
||||
anyMedia,
|
||||
quoteOfId,
|
||||
scheduledAt,
|
||||
hasPoll,
|
||||
} = this.props
|
||||
|
||||
const disabledButton = isSubmitting || isUploading || isChangingUpload || length(text) > MAX_POST_CHARACTER_COUNT || (length(text.trim()) === 0 && !anyMedia)
|
||||
|
||||
if (type === 'comment') {
|
||||
const commentPublishBtnClasses = CX({
|
||||
d: 1,
|
||||
jcCenter: 1,
|
||||
displayNone: disabledButton,
|
||||
})
|
||||
|
||||
return (
|
||||
<div className={[_s.d, _s.flexRow, _s.aiStart, _s.mlAuto].join(' ')}>
|
||||
<div className={[_s.d, _s.flexRow, _s.mrAuto].join(' ')}>
|
||||
<div className={commentPublishBtnClasses}>
|
||||
<Button
|
||||
isNarrow
|
||||
radiusSmall
|
||||
onClick={this.handleSubmit}
|
||||
isDisabled={disabledButton}
|
||||
className={_s.px15}
|
||||
>
|
||||
{intl.formatMessage(scheduledAt ? messages.schedulePost : messages.post)}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const containerClasses = CX({
|
||||
d: 1,
|
||||
mr5: 1,
|
||||
jcCenter: 1,
|
||||
h40PX: 1,
|
||||
})
|
||||
|
||||
const btnClasses = CX({
|
||||
d: 1,
|
||||
circle: 1,
|
||||
radiusSmall: 1,
|
||||
noUnderline: 1,
|
||||
font: 1,
|
||||
cursorPointer: 1,
|
||||
@@ -37,31 +87,33 @@ class ComposeFormSubmitButton extends React.PureComponent {
|
||||
py10: 1,
|
||||
px10: 1,
|
||||
})
|
||||
|
||||
const iconClasses = CX({
|
||||
cSecondary: !active,
|
||||
cWhite: active,
|
||||
mr10: 1,
|
||||
py2: small,
|
||||
ml10: small,
|
||||
})
|
||||
|
||||
const iconSize = !small ? '18px' : '16px'
|
||||
const textColor = !active ? 'primary' : 'white'
|
||||
|
||||
|
||||
let backgroundColor, color
|
||||
if (disabledButton) {
|
||||
backgroundColor = 'tertiary'
|
||||
color = 'tertiary'
|
||||
} else if (type === 'navigation') {
|
||||
backgroundColor = 'white'
|
||||
color = 'brand'
|
||||
} else {
|
||||
backgroundColor = 'brand'
|
||||
color = 'white'
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={containerClasses}>
|
||||
<div className={[_s.d, _s.w100PC, _s.py10, _s.px10].join(' ')}>
|
||||
<div className={[_s.d, _s.w100PC].join(' ')}>
|
||||
<Button
|
||||
isBlock
|
||||
radiusSmall
|
||||
isDisabled={disabledButton}
|
||||
backgroundColor={disabledButton ? 'secondary' : 'brand'}
|
||||
color={disabledButton ? 'tertiary' : 'white'}
|
||||
backgroundColor={backgroundColor}
|
||||
color={color}
|
||||
className={[_s.fs15PX, _s.px15, _s.flexGrow1, _s.mlAuto].join(' ')}
|
||||
onClick={this.handleSubmit}
|
||||
>
|
||||
<Text color='inherit' weight='medium' align='center'>
|
||||
post
|
||||
<Text color='inherit' size='medium' weight='bold' align='center'>
|
||||
{intl.formatMessage(scheduledAt ? messages.schedulePost : edit ? messages.postEdit : messages.post)}
|
||||
</Text>
|
||||
</Button>
|
||||
</div>
|
||||
@@ -71,9 +123,32 @@ class ComposeFormSubmitButton extends React.PureComponent {
|
||||
|
||||
}
|
||||
|
||||
// {intl.formatMessage(scheduledAt ? messages.schedulePost : edit ? messages.postEdit : messages.post)}
|
||||
const messages = defineMessages({
|
||||
post: { id: 'compose_form.post', defaultMessage: 'Post' },
|
||||
postEdit: { id: 'compose_form.post_edit', defaultMessage: 'Post Edit' },
|
||||
schedulePost: { id: 'compose_form.schedule_post', defaultMessage: 'Schedule Post' },
|
||||
})
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
edit: state.getIn(['compose', 'id']) !== null,
|
||||
text: state.getIn(['compose', 'text']),
|
||||
isSubmitting: state.getIn(['compose', 'is_submitting']),
|
||||
isChangingUpload: state.getIn(['compose', 'is_changing_upload']),
|
||||
isUploading: state.getIn(['compose', 'is_uploading']),
|
||||
anyMedia: state.getIn(['compose', 'media_attachments']).size > 0,
|
||||
quoteOfId: state.getIn(['compose', 'quote_of_id']),
|
||||
scheduledAt: state.getIn(['compose', 'scheduled_at']),
|
||||
hasPoll: state.getIn(['compose', 'poll']),
|
||||
})
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
onSubmit(groupId, replyToId = null, router, isStandalone, autoJoinGroup) {
|
||||
dispatch(submitCompose(groupId, replyToId, router, isStandalone, autoJoinGroup))
|
||||
}
|
||||
})
|
||||
|
||||
ComposeFormSubmitButton.propTypes = {
|
||||
type: PropTypes.oneOf(['header', 'block', 'comment'])
|
||||
type: PropTypes.oneOf(['header', 'navigation', 'block', 'comment'])
|
||||
}
|
||||
|
||||
export default ComposeFormSubmitButton
|
||||
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(ComposeFormSubmitButton))
|
||||
@@ -48,7 +48,7 @@ class ExpiresPostButton extends React.PureComponent {
|
||||
}
|
||||
|
||||
const messages = defineMessages({
|
||||
expires: { id: 'expiration.title', defaultMessage: 'Add status expiration' },
|
||||
expires: { id: 'expiration.title', defaultMessage: 'Status expiration' },
|
||||
})
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
|
||||
@@ -19,7 +19,7 @@ class Upload extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
state = {
|
||||
hovered: false,
|
||||
hovering: false,
|
||||
focused: false,
|
||||
dirtyDescription: null,
|
||||
}
|
||||
@@ -45,11 +45,11 @@ class Upload extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
handleMouseEnter = () => {
|
||||
this.setState({ hovered: true })
|
||||
this.setState({ hovering: true })
|
||||
}
|
||||
|
||||
handleMouseLeave = () => {
|
||||
this.setState({ hovered: false })
|
||||
this.setState({ hovering: false })
|
||||
}
|
||||
|
||||
handleInputFocus = () => {
|
||||
@@ -75,66 +75,60 @@ class Upload extends ImmutablePureComponent {
|
||||
|
||||
render() {
|
||||
const { intl, media } = this.props
|
||||
const active = this.state.hovered || this.state.focused
|
||||
const description = this.state.dirtyDescription || (this.state.dirtyDescription !== '' && media.get('description')) || ''
|
||||
const { hovering } = this.state
|
||||
|
||||
const descriptionContainerClasses = CX({
|
||||
d: 1,
|
||||
posAbs: 1,
|
||||
right0: 1,
|
||||
bottom0: 1,
|
||||
left0: 1,
|
||||
mt5: 1,
|
||||
mb5: 1,
|
||||
ml5: 1,
|
||||
mr5: 1,
|
||||
displayNone: !active,
|
||||
})
|
||||
const active = hovering || this.state.focused
|
||||
const description = this.state.dirtyDescription || (this.state.dirtyDescription !== '' && media.get('description')) || ''
|
||||
|
||||
return (
|
||||
<div
|
||||
tabIndex='0'
|
||||
className={[_s.d, _s.w50PC, _s.px5, _s.py5].join(' ')}
|
||||
className={[_s.d, _s.w100PC, _s.mt10].join(' ')}
|
||||
onMouseEnter={this.handleMouseEnter}
|
||||
onMouseLeave={this.handleMouseLeave}
|
||||
onClick={this.handleClick}
|
||||
role='button'
|
||||
>
|
||||
<div className={[_s.d, _s.radiusSmall, _s.overflowHidden, _s.h158PX].join(' ')}>
|
||||
<div className={[_s.d, _s.radiusSmall, _s.borderColorSecondary, _s.border1PX, _s.overflowHidden, _s.maxH100VH, _s.minH106PX].join(' ')}>
|
||||
<Image
|
||||
className={[_s.d, _s.h158PX].join(' ')}
|
||||
className={[_s.d, _s.minH106PX, _s.maxH100VH].join(' ')}
|
||||
src={media.get('preview_url')}
|
||||
/>
|
||||
{ hovering && <div className={[_s.d, _s.posAbs, _s.z2, _s.top0, _s.bottom0, _s.right0, _s.left0, _s.bgBlackOpaquest].join(' ')} /> }
|
||||
{
|
||||
media.get('type') === 'gifv' &&
|
||||
<div className={[_s.d, _s.posAbs, _s.z2, _s.radiusSmall, _s.bgBlackOpaque, _s.px5, _s.py5, _s.ml10, _s.mt10, _s.top0, _s.left0].join(' ')}>
|
||||
<div className={[_s.d, _s.posAbs, _s.z3, _s.radiusSmall, _s.bgBlackOpaque, _s.px5, _s.py5, _s.ml10, _s.mt10, _s.bottom0, _s.right0].join(' ')}>
|
||||
<Text size='extraSmall' color='white' weight='medium'>GIF</Text>
|
||||
</div>
|
||||
}
|
||||
<Button
|
||||
backgroundColor='black'
|
||||
color='white'
|
||||
title={intl.formatMessage(messages.delete)}
|
||||
onClick={this.handleUndoClick}
|
||||
icon='close'
|
||||
iconSize='10px'
|
||||
iconClassName={_s.inherit}
|
||||
className={[_s.top0, _s.right0, _s.posAbs, _s.mr5, _s.mt5, _s.px10].join(' ')}
|
||||
/>
|
||||
|
||||
<div className={descriptionContainerClasses}>
|
||||
<Input
|
||||
small
|
||||
hideLabel
|
||||
id={`input-${media.get('id')}`}
|
||||
title={intl.formatMessage(messages.description)}
|
||||
placeholder={intl.formatMessage(messages.description)}
|
||||
value={description}
|
||||
maxLength={420}
|
||||
onFocus={this.handleInputFocus}
|
||||
onChange={this.handleInputChange}
|
||||
onBlur={this.handleInputBlur}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
<div className={[_s.d, _s.posAbs, _s.px15, _s.pt15, _s.z3, _s.flexRow, _s.top0, _s.left0, _s.right0].join(' ')}>
|
||||
{
|
||||
active &&
|
||||
<div className={[_s.d, _s.flexGrow1, _s.mr15].join(' ')}>
|
||||
<Input
|
||||
small
|
||||
hideLabel
|
||||
id={`input-${media.get('id')}`}
|
||||
title={intl.formatMessage(messages.description)}
|
||||
placeholder={intl.formatMessage(messages.description)}
|
||||
value={description}
|
||||
maxLength={420}
|
||||
onFocus={this.handleInputFocus}
|
||||
onChange={this.handleInputChange}
|
||||
onBlur={this.handleInputBlur}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
<Button
|
||||
backgroundColor='black'
|
||||
color='white'
|
||||
title={intl.formatMessage(messages.delete)}
|
||||
onClick={this.handleUndoClick}
|
||||
icon='close'
|
||||
iconSize='10px'
|
||||
iconClassName={_s.inherit}
|
||||
className={[_s.mlAuto, _s.px10].join(' ')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@ class SensitiveMediaButton extends React.PureComponent {
|
||||
const { active, disabled, onClick, intl } = this.props
|
||||
|
||||
return (
|
||||
<div className={[_s.d, _s.aiStart, _s.px5].join(' ')}>
|
||||
<div className={[_s.d, _s.aiStart, _s.px5, _s.py10].join(' ')}>
|
||||
<Switch
|
||||
id='mark-sensitive'
|
||||
type='checkbox'
|
||||
|
||||
@@ -18,23 +18,16 @@ class UploadForm extends ImmutablePureComponent {
|
||||
|
||||
return (
|
||||
<div className={_s.d}>
|
||||
{ isUploading && <ProgressBar small progress={uploadProgress} /> }
|
||||
|
||||
<div className={[_s.d, _s.flexRow, _s.flexWrap].join(' ')}>
|
||||
{
|
||||
mediaIds.map(id => (
|
||||
<Upload id={id} key={id} />
|
||||
))
|
||||
}
|
||||
{mediaIds.map(id => (
|
||||
<Upload id={id} key={id} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
{
|
||||
!mediaIds.isEmpty() &&
|
||||
<SensitiveMediaButton />
|
||||
}
|
||||
|
||||
{
|
||||
isUploading &&
|
||||
<ProgressBar small progress={uploadProgress} />
|
||||
}
|
||||
{ !mediaIds.isEmpty() && <SensitiveMediaButton /> }
|
||||
{ isUploading && <ProgressBar small progress={uploadProgress} /> }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -48,7 +41,6 @@ const mapStateToProps = (state) => ({
|
||||
})
|
||||
|
||||
UploadForm.propTypes = {
|
||||
isModalOpen: PropTypes.bool,
|
||||
isUploading: PropTypes.bool,
|
||||
mediaIds: ImmutablePropTypes.list.isRequired,
|
||||
uploadProgress: PropTypes.number,
|
||||
|
||||
@@ -11,7 +11,7 @@ class Compose extends React.PureComponent {
|
||||
}
|
||||
|
||||
render () {
|
||||
return <ComposeFormContainer isStandalone />
|
||||
return <ComposeFormContainer formLocation='standalone' />
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -186,9 +186,9 @@ class SlideFirstPost extends React.PureComponent {
|
||||
|
||||
<div className={[_s.d, _s.mt15, _s.boxShadowBlock, _s.radiusSmall].join(' ')}>
|
||||
<ComposeFormContainer
|
||||
formLocation='introduction'
|
||||
groupId={GAB_COM_INTRODUCE_YOURSELF_GROUP_ID}
|
||||
hidePro
|
||||
autoFocus
|
||||
autoJoinGroup
|
||||
/>
|
||||
</div>
|
||||
@@ -325,7 +325,7 @@ class Introduction extends ImmutablePureComponent {
|
||||
<Button
|
||||
href={currentIndex === 3 ? '/home' : undefined}
|
||||
onClick={this.handleNext}
|
||||
className={_s.px10}
|
||||
className={[_s.px10, _s.aiCenter, _s.flexRow].join(' ')}
|
||||
icon={currentIndex !== 3 ? 'arrow-right' : undefined}
|
||||
iconSize={currentIndex !== 3 ? '18px' : undefined}
|
||||
>
|
||||
@@ -336,7 +336,7 @@ class Introduction extends ImmutablePureComponent {
|
||||
<Text color='white' className={_s.px5}>{nextTitle}</Text>
|
||||
</Responsive>
|
||||
<Responsive max={BREAKPOINT_EXTRA_SMALL}>
|
||||
<Text color='white' className={[_s.px5, _s.mr10].join(' ')}>Done</Text>
|
||||
<Text color='white' className={[_s.px5, _s.mr5].join(' ')}>Done</Text>
|
||||
<Icon id='check' size='14px' className={_s.cWhite} />
|
||||
</Responsive>
|
||||
</React.Fragment>
|
||||
|
||||
@@ -35,16 +35,17 @@ class ChatConversationsListItem extends ImmutablePureComponent {
|
||||
|
||||
if (!chatConversation) return <div/>
|
||||
|
||||
console.log("chatConversation:", chatConversation)
|
||||
|
||||
const containerClasses = CX({
|
||||
d: 1,
|
||||
w100PC: 1,
|
||||
bgTransparent: 1,
|
||||
bgSubtle_onHover: 1,
|
||||
borderBottom1PX: 1,
|
||||
borderColorSecondary: 1,
|
||||
noUnderline: 1,
|
||||
outlineNone: 1,
|
||||
cursorPointer: 1,
|
||||
pl15: 1,
|
||||
})
|
||||
|
||||
const innerContainerClasses = CX({
|
||||
@@ -71,6 +72,9 @@ class ChatConversationsListItem extends ImmutablePureComponent {
|
||||
className={containerClasses}
|
||||
onClick={this.handleOnClick}
|
||||
>
|
||||
|
||||
{ chatConversation.get('is_unread') && <div className={[_s.d, _s.posAbs, _s.left0, _s.top50PC, _s.ml10, _s.mtNeg5PX, _s.circle, _s.w10PX, _s.h10PX, _s.bgBrand].join(' ')} /> }
|
||||
|
||||
<div className={innerContainerClasses}>
|
||||
<AvatarGroup accounts={otherAccounts} size={avatarSize} noHover />
|
||||
|
||||
@@ -88,6 +92,7 @@ class ChatConversationsListItem extends ImmutablePureComponent {
|
||||
|
||||
<div className={[_s.py5, _s.dangerousContent, _s.textAlignLeft].join(' ')} dangerouslySetInnerHTML={content} />
|
||||
</div>
|
||||
<div className={[_s.d, _s.posAbs, _s.h1PX, _s.w100PC, _s.bottom0, _s.right0, _s.bgSecondary].join(' ')} />
|
||||
</div>
|
||||
</button>
|
||||
)
|
||||
|
||||
@@ -8,6 +8,7 @@ import { openModal } from '../../../actions/modal'
|
||||
import { sendChatMessage } from '../../../actions/chat_messages'
|
||||
import { CX } from '../../../constants'
|
||||
import Button from '../../../components/button'
|
||||
import Icon from '../../../components/icon'
|
||||
import Input from '../../../components/input'
|
||||
import Text from '../../../components/text'
|
||||
|
||||
@@ -23,6 +24,10 @@ class ChatMessagesComposeForm extends React.PureComponent {
|
||||
this.setState({ value: '' })
|
||||
}
|
||||
|
||||
handleOnExpire = () => {
|
||||
//
|
||||
}
|
||||
|
||||
onChange = (e) => {
|
||||
this.setState({ value: e.target.value })
|
||||
}
|
||||
@@ -68,6 +73,10 @@ class ChatMessagesComposeForm extends React.PureComponent {
|
||||
this.sendBtn = c
|
||||
}
|
||||
|
||||
setExpiresBtn = (c) => {
|
||||
this.expiresBtn = c
|
||||
}
|
||||
|
||||
render () {
|
||||
const { isXS, chatConversationId } = this.props
|
||||
const { value } = this.state
|
||||
@@ -85,9 +94,7 @@ class ChatMessagesComposeForm extends React.PureComponent {
|
||||
px10: 1,
|
||||
fs14PX: 1,
|
||||
maxH200PX: 1,
|
||||
borderColorSecondary: 1,
|
||||
border1PX: 1,
|
||||
radiusRounded: 1,
|
||||
w100PC: 1,
|
||||
py10: 1,
|
||||
})
|
||||
|
||||
@@ -105,6 +112,7 @@ class ChatMessagesComposeForm extends React.PureComponent {
|
||||
onBlur={this.onBlur}
|
||||
onKeyDown={this.onKeyDown}
|
||||
aria-autocomplete='list'
|
||||
maxLength={1600}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -114,18 +122,33 @@ class ChatMessagesComposeForm extends React.PureComponent {
|
||||
disabled={disabled}
|
||||
onClick={this.handleOnSendChatMessage}
|
||||
>
|
||||
<Text color='inherit' weight='medium' className={_s.px10}>Send</Text>
|
||||
<Text color='inherit' weight='medium' className={isXS ? undefined : _s.px10}>Send</Text>
|
||||
</Button>
|
||||
)
|
||||
|
||||
const expiresBtn = (
|
||||
<button
|
||||
ref={this.setExpiresBtn}
|
||||
className={[_s.d, _s.bgSubtle, _s.borderRight1PX, _s.borderColorSecondary, _s.w40PX, _s.h100PC, _s.aiCenter, _s.jcCenter, _s.cursorPointer, _s.outlineNone].join(' ')}
|
||||
onClick={this.handleOnExpire}
|
||||
>
|
||||
<Icon id='stopwatch' className={[_s.cPrimary, _s.ml2].join(' ')} size='15px' />
|
||||
</button>
|
||||
)
|
||||
|
||||
if (isXS) {
|
||||
return (
|
||||
<div className={[_s.d, _s.z4, _s.minH58PX, _s.w100PC].join(' ')}>
|
||||
<div className={[_s.d, _s.minH58PX, _s.bgPrimary, _s.aiCenter, _s.z3, _s.bottom0, _s.right0, _s.left0, _s.posFixed].join(' ')} >
|
||||
<div className={[_s.d, _s.w100PC, _s.pb5, _s.px15, _s.aiCenter, _s.jcCenter, _s.saveAreaInsetPB, _s.saveAreaInsetPL, _s.saveAreaInsetPR, _s.w100PC].join(' ')}>
|
||||
<div className={[_s.d, _s.flexRow, _s.aiCenter, _s.minH58PX, _s.w100PC, _s.borderTop1PX, _s.borderColorSecondary, _s.px10].join(' ')}>
|
||||
<div className={[_s.d, _s.pr15, _s.flexGrow1, _s.py10].join(' ')}>
|
||||
{textarea}
|
||||
<div className={[_s.d, _s.flexRow, _s.radiusRounded, _s.border1PX, _s.borderColorSecondary, _s.overflowHidden].join(' ')}>
|
||||
<div className={_s.d}>
|
||||
{expiresBtn}
|
||||
</div>
|
||||
<div className={[_s.d, _s.flexGrow1].join(' ')}>
|
||||
{textarea}
|
||||
</div>
|
||||
</div>
|
||||
<div className={[_s.d, _s.h100PC, _s.aiCenter, _s.jcCenter].join(' ')}>
|
||||
{button}
|
||||
@@ -140,9 +163,16 @@ class ChatMessagesComposeForm extends React.PureComponent {
|
||||
return (
|
||||
<div className={[_s.d, _s.posAbs, _s.bottom0, _s.left0, _s.right0, _s.flexRow, _s.aiCenter, _s.minH58PX, _s.bgPrimary, _s.w100PC, _s.borderTop1PX, _s.borderColorSecondary, _s.px15].join(' ')}>
|
||||
<div className={[_s.d, _s.pr15, _s.flexGrow1, _s.py10].join(' ')}>
|
||||
{textarea}
|
||||
<div className={[_s.d, _s.flexRow, _s.radiusRounded, _s.border1PX, _s.borderColorSecondary, _s.overflowHidden].join(' ')}>
|
||||
<div className={_s.d}>
|
||||
{expiresBtn}
|
||||
</div>
|
||||
<div className={[_s.d, _s.flexGrow1].join(' ')}>
|
||||
{textarea}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={[_s.d, _s.h100PC, _s.aiCenter, _s.jcCenter].join(' ')}>
|
||||
<div className={[_s.d, _s.h100PC, _s.mtAuto, _s.mb10, _s.aiCenter, _s.jcCenter].join(' ')}>
|
||||
{button}
|
||||
</div>
|
||||
</div>
|
||||
@@ -163,4 +193,4 @@ ChatMessagesComposeForm.propTypes = {
|
||||
onSendMessage: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(ChatMessagesComposeForm)
|
||||
export default connect(mapDispatchToProps)(ChatMessagesComposeForm)
|
||||
@@ -8,7 +8,7 @@ import { NavLink } from 'react-router-dom'
|
||||
import { openPopover } from '../../../actions/popover'
|
||||
import {
|
||||
CX,
|
||||
POPOVER_CHAT_MESSAGE_DELETE,
|
||||
POPOVER_CHAT_MESSAGE_OPTIONS,
|
||||
} from '../../../constants'
|
||||
import { me } from '../../../initial_state'
|
||||
import Input from '../../../components/input'
|
||||
@@ -51,7 +51,7 @@ class ChatMessageItem extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
handleMoreClick = () => {
|
||||
this.props.onOpenChatMessageDeletePopover(this.props.chatMessageId, this.deleteBtnRef)
|
||||
this.props.onOpenChatMessageOptionsPopover(this.props.chatMessageId, this.deleteBtnRef)
|
||||
}
|
||||
|
||||
setDeleteBtnRef = (c) => {
|
||||
@@ -122,7 +122,7 @@ class ChatMessageItem extends ImmutablePureComponent {
|
||||
const buttonContainerClasses = CX({
|
||||
d: 1,
|
||||
flexRow: 1,
|
||||
displayNone: !isHovering && alt,
|
||||
displayNone: !isHovering,
|
||||
})
|
||||
|
||||
return (
|
||||
@@ -145,19 +145,16 @@ class ChatMessageItem extends ImmutablePureComponent {
|
||||
<div className={messageInnerContainerClasses}>
|
||||
<div className={[_s.py5, _s.dangerousContent, _s.cPrimary].join(' ')} dangerouslySetInnerHTML={content} />
|
||||
</div>
|
||||
{
|
||||
alt &&
|
||||
<div className={buttonContainerClasses}>
|
||||
<Button
|
||||
buttonRef={this.setDeleteBtnRef}
|
||||
onClick={this.handleMoreClick}
|
||||
color='tertiary'
|
||||
backgroundColor='none'
|
||||
icon='ellipsis'
|
||||
iconSize='18px'
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
<div className={buttonContainerClasses}>
|
||||
<Button
|
||||
buttonRef={this.setDeleteBtnRef}
|
||||
onClick={this.handleMoreClick}
|
||||
color='tertiary'
|
||||
backgroundColor='none'
|
||||
icon='ellipsis'
|
||||
iconSize='18px'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={lowerContainerClasses}>
|
||||
<Text size='extraSmall' color='tertiary' align={alt ? 'right' : 'left'}>
|
||||
@@ -178,8 +175,8 @@ const mapStateToProps = (state, { lastChatMessageId, chatMessageId }) => ({
|
||||
})
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
onOpenChatMessageDeletePopover(chatMessageId, targetRef) {
|
||||
dispatch(openPopover(POPOVER_CHAT_MESSAGE_DELETE, {
|
||||
onOpenChatMessageOptionsPopover(chatMessageId, targetRef) {
|
||||
dispatch(openPopover(POPOVER_CHAT_MESSAGE_OPTIONS, {
|
||||
targetRef,
|
||||
chatMessageId,
|
||||
position: 'top',
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
expandChatMessages,
|
||||
scrollBottomChatMessageConversation,
|
||||
} from '../../../actions/chat_conversation_messages'
|
||||
import { readChatConversation } from '../../../actions/chat_conversations'
|
||||
import IntersectionObserverArticle from '../../../components/intersection_observer_article'
|
||||
import IntersectionObserverWrapper from '../../ui/util/intersection_observer_wrapper'
|
||||
import ChatMessagePlaceholder from '../../../components/placeholder/chat_message_placeholder'
|
||||
@@ -58,7 +59,6 @@ class ChatMessageScrollingList extends ImmutablePureComponent {
|
||||
// Reset the scroll position when a new child comes in in order not to
|
||||
// jerk the scrollbar around if you're already scrolled down the page.
|
||||
if (snapshot !== null && this.scrollContainerRef) {
|
||||
console.log("snapshot:", snapshot)
|
||||
this.setScrollTop(this.scrollContainerRef.scrollHeight - snapshot)
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@ class ChatMessageScrollingList extends ImmutablePureComponent {
|
||||
|
||||
if (prevProps.chatMessageIds.size === 0 && this.props.chatMessageIds.size > 0 && this.scrollContainerRef) {
|
||||
this.scrollContainerRef.scrollTop = this.scrollContainerRef.scrollHeight
|
||||
this.props.onReadChatConversation(this.props.chatConversationId)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,6 +364,9 @@ const mapDispatchToProps = (dispatch, ownProps) => ({
|
||||
onSetChatConversationSelected: (chatConversationId) => {
|
||||
dispatch(setChatConversationSelected(chatConversationId))
|
||||
},
|
||||
onReadChatConversation(chatConversationId) {
|
||||
dispatch(readChatConversation(chatConversationId))
|
||||
},
|
||||
})
|
||||
|
||||
ChatMessageScrollingList.propTypes = {
|
||||
|
||||
@@ -52,11 +52,13 @@ import DeckPage from '../../pages/deck_page'
|
||||
|
||||
import {
|
||||
About,
|
||||
AccountAlbums,
|
||||
AccountGallery,
|
||||
AccountTimeline,
|
||||
AccountCommentsTimeline,
|
||||
Assets,
|
||||
BlockedAccounts,
|
||||
BookmarkCollections,
|
||||
BookmarkedStatuses,
|
||||
CaliforniaConsumerProtection,
|
||||
CaliforniaConsumerProtectionContact,
|
||||
@@ -274,9 +276,11 @@ class SwitchingArea extends React.PureComponent {
|
||||
|
||||
<WrappedRoute path='/:username/photos' page={ProfilePage} component={AccountGallery} content={children} componentParams={{ noSidebar: true, mediaType: 'photo' }} />
|
||||
<WrappedRoute path='/:username/videos' page={ProfilePage} component={AccountGallery} content={children} componentParams={{ noSidebar: true, mediaType: 'video' }} />
|
||||
<WrappedRoute path='/:username/albums' page={ProfilePage} component={AccountAlbums} content={children} componentParams={{ noSidebar: true, mediaType: 'photo' }} />
|
||||
|
||||
<WrappedRoute path='/:username/likes' page={ProfilePage} component={LikedStatuses} content={children} />
|
||||
<WrappedRoute path='/:username/bookmarks' page={ProfilePage} component={BookmarkedStatuses} content={children} />
|
||||
<WrappedRoute path='/:username/bookmarks' page={ProfilePage} component={BookmarkCollections} content={children} />
|
||||
<WrappedRoute path='/:username/:bookmarkCollectionId/bookmarks' page={ProfilePage} component={BookmarkedStatuses} content={children} />
|
||||
|
||||
<WrappedRoute path='/:username/posts/:statusId' publicRoute exact page={BasicPage} component={StatusFeature} content={children} componentParams={{ title: 'Status', page: 'status' }} />
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ export function AccountGallery() { return import(/* webpackChunkName: "features/
|
||||
export function Assets() { return import(/* webpackChunkName: "features/about/assets" */'../../about/assets') }
|
||||
export function BlockAccountModal() { return import(/* webpackChunkName: "components/block_account_modal" */'../../../components/modal/block_account_modal') }
|
||||
export function BlockedAccounts() { return import(/* webpackChunkName: "features/blocked_accounts" */'../../blocked_accounts') }
|
||||
export function BookmarkCollections() { return import(/* webpackChunkName: "features/bookmark_collections" */'../../bookmark_collections') }
|
||||
export function BookmarkedStatuses() { return import(/* webpackChunkName: "features/bookmarked_statuses" */'../../bookmarked_statuses') }
|
||||
export function BoostModal() { return import(/* webpackChunkName: "components/boost_modal" */'../../../components/modal/boost_modal') }
|
||||
export function CaliforniaConsumerProtection() { return import(/* webpackChunkName: "features/california_consumer_protection" */'../../about/california_consumer_protection') }
|
||||
@@ -17,17 +18,19 @@ export function ChatConversationDeleteModal() { return import(/* webpackChunkNam
|
||||
export function ChatConversationMutedAccounts() { return import(/* webpackChunkName: "features/chat_conversation_muted_accounts" */'../../chat_conversation_muted_accounts') }
|
||||
export function ChatConversationOptionsPopover() { return import(/* webpackChunkName: "components/chat_conversation_options_popover" */'../../../components/popover/chat_conversation_options_popover') }
|
||||
export function ChatConversationRequests() { return import(/* webpackChunkName: "features/chat_conversation_requests" */'../../chat_conversation_requests') }
|
||||
export function ChatMessageDeletePopover() { return import(/* webpackChunkName: "components/chat_message_delete_popover" */'../../../components/popover/chat_message_delete_popover') }
|
||||
export function ChatMessageOptionsPopover() { return import(/* webpackChunkName: "components/chat_message_options_popover" */'../../../components/popover/chat_message_options_popover') }
|
||||
export function CommentSortingOptionsPopover() { return import(/* webpackChunkName: "components/comment_sorting_options_popover" */'../../../components/popover/comment_sorting_options_popover') }
|
||||
export function CommunityTimeline() { return import(/* webpackChunkName: "features/community_timeline" */'../../community_timeline') }
|
||||
export function CommunityTimelineSettingsModal() { return import(/* webpackChunkName: "components/community_timeline_settings_modal" */'../../../components/modal/community_timeline_settings_modal') }
|
||||
export function Compose() { return import(/* webpackChunkName: "features/compose" */'../../compose') }
|
||||
export function ComposeForm() { return import(/* webpackChunkName: "components/compose_form" */'../../compose/components/compose_form') }
|
||||
export function ComposeModal() { return import(/* webpackChunkName: "components/compose_modal" */'../../../components/modal/compose_modal') }
|
||||
export function ComposePostDesinationPopover() { return import(/* webpackChunkName: "components/compose_post_destination_popover" */'../../../components/popover/compose_post_destination_popover') }
|
||||
export function ConfirmationModal() { return import(/* webpackChunkName: "components/confirmation_modal" */'../../../components/modal/confirmation_modal') }
|
||||
export function DatePickerPopover() { return import(/* webpackChunkName: "components/date_picker_popover" */'../../../components/popover/date_picker_popover') }
|
||||
export function Deck() { return import(/* webpackChunkName: "features/deck" */'../../deck') }
|
||||
export function DeckColumnAddModal() { return import(/* webpackChunkName: "components/deck_column_add_modal" */'../../../components/modal/deck_column_add_modal') }
|
||||
export function DeckColumnAddOptionsModal() { return import(/* webpackChunkName: "components/deck_column_add_options_modal" */'../../../components/modal/deck_column_add_options_modal') }
|
||||
export function DisplayOptionsModal() { return import(/* webpackChunkName: "components/display_options_modal" */'../../../components/modal/display_options_modal') }
|
||||
export function DMCA() { return import(/* webpackChunkName: "features/about/dmca" */'../../about/dmca') }
|
||||
export function EditProfileModal() { return import(/* webpackChunkName: "components/edit_profile_modal" */'../../../components/modal/edit_profile_modal') }
|
||||
|
||||
Reference in New Issue
Block a user