Progress on Gab Deck, Updates on Compose
• Progress on Gab Deck, Updates on Compose
This commit is contained in:
parent
6950f67520
commit
998f00ae48
|
@ -1,9 +1,12 @@
|
|||
import { me } from '../initial_state'
|
||||
import { saveSettings } from './settings'
|
||||
|
||||
export const DECK_CONNECT = 'DECK_CONNECT'
|
||||
export const DECK_DISCONNECT = 'DECK_DISCONNECT'
|
||||
|
||||
export const DECK_SET_COLUMN_AT_INDEX = 'DECK_SET_COLUMN_AT_INDEX'
|
||||
export const DECK_DELETE_COLUMN_AT_INDEX = 'DECK_DELETE_COLUMN_AT_INDEX'
|
||||
export const DECK_CHANGE_COLUMN_AT_INDEX = 'DECK_CHANGE_COLUMN_AT_INDEX'
|
||||
|
||||
export const deckConnect = () => ({
|
||||
type: DECK_CONNECT,
|
||||
|
@ -13,8 +16,28 @@ export const deckDisconnect = () => ({
|
|||
type: DECK_DISCONNECT,
|
||||
})
|
||||
|
||||
export const setDeckColumnAtIndex = (column, index) => ({
|
||||
type: DECK_SET_COLUMN_AT_INDEX,
|
||||
column,
|
||||
index,
|
||||
})
|
||||
export const setDeckColumnAtIndex = (column, index) => (dispatch) => {
|
||||
dispatch({
|
||||
type: DECK_SET_COLUMN_AT_INDEX,
|
||||
column,
|
||||
index,
|
||||
})
|
||||
dispatch(saveSettings())
|
||||
}
|
||||
|
||||
export const deleteDeckColumnAtIndex = (index) => (dispatch) => {
|
||||
dispatch({
|
||||
type: DECK_DELETE_COLUMN_AT_INDEX,
|
||||
index,
|
||||
})
|
||||
dispatch(saveSettings())
|
||||
}
|
||||
|
||||
export const updateDeckColumnAtIndex = (oldIndex, newIndex) => (dispatch) => {
|
||||
dispatch({
|
||||
type: DECK_CHANGE_COLUMN_AT_INDEX,
|
||||
oldIndex,
|
||||
newIndex,
|
||||
})
|
||||
dispatch(saveSettings())
|
||||
}
|
||||
|
|
|
@ -261,7 +261,7 @@ class Composer extends React.PureComponent {
|
|||
<div className={_s.d}>
|
||||
|
||||
{
|
||||
!small && isPro &&
|
||||
isPro &&
|
||||
<RichTextEditorBar
|
||||
editorState={editorState}
|
||||
onChange={this.onChange}
|
||||
|
|
|
@ -10,12 +10,13 @@ class DeckColumn extends React.PureComponent {
|
|||
subtitle,
|
||||
icon,
|
||||
children,
|
||||
index,
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
<div className={[_s.d, _s.w360PX, _s.px2, _s.bgSecondary, _s.h100VH].join(' ')}>
|
||||
<div className={[_s.d, _s.w100PC, _s.bgPrimary, _s.h100VH].join(' ')}>
|
||||
<DeckColumnHeader title={title} subtitle={subtitle} icon={icon} />
|
||||
<DeckColumnHeader title={title} subtitle={subtitle} icon={icon} index={index} />
|
||||
<div className={[_s.d, _s.w100PC, _s.overflowYScroll, _s.boxShadowNone, _s.posAbs, _s.top60PX, _s.left0, _s.right0, _s.bottom0].join(' ')}>
|
||||
{children}
|
||||
</div>
|
||||
|
@ -30,6 +31,7 @@ DeckColumn.propTypes = {
|
|||
title: PropTypes.string,
|
||||
subtitle: PropTypes.string,
|
||||
icon: PropTypes.string,
|
||||
index: PropTypes.number,
|
||||
}
|
||||
|
||||
export default DeckColumn
|
|
@ -1,11 +1,21 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { connect } from 'react-redux'
|
||||
import { deleteDeckColumnAtIndex } from '../actions/deck'
|
||||
import Icon from './icon'
|
||||
import Text from './text'
|
||||
import Button from './button'
|
||||
|
||||
class DeckColumnHeader extends React.PureComponent {
|
||||
|
||||
handleClickDelete = () => {
|
||||
this.props.dispatch(deleteDeckColumnAtIndex(this.props.index))
|
||||
}
|
||||
|
||||
handleClickRefresh = () => {
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
title,
|
||||
|
@ -15,22 +25,41 @@ class DeckColumnHeader extends React.PureComponent {
|
|||
} = this.props
|
||||
|
||||
return (
|
||||
<div className={[_s.d, _s.w100PC, _s.flexRow, _s.aiCenter, _s.h60PX, _s.px15, _s.py10, _s.borderBottom1PX, _s.borderColorSecondary, _s.bgPrimary].join(' ')}>
|
||||
{ !!icon && <Icon id={icon} className={_s.cPrimary} size='20px' /> }
|
||||
<div data-sort-header className={[_s.d, _s.w100PC, _s.flexRow, _s.aiCenter, _s.h60PX, _s.px15, _s.py10, _s.borderBottom1PX, _s.borderColorSecondary, _s.bgPrimary].join(' ')}>
|
||||
{
|
||||
!!icon &&
|
||||
<div data-sort-header className={[_s.d, _s.flexRow, _s.mr15, _s.cursorEWResize].join(' ')}>
|
||||
<span className={[_s.d, _s.w1PX, _s.h24PX, _s.mr2, _s.bgSecondary].join(' ')} />
|
||||
<span className={[_s.d, _s.w1PX, _s.h24PX, _s.mr2, _s.bgSecondary].join(' ')} />
|
||||
<span className={[_s.d, _s.w1PX, _s.h24PX, _s.bgSecondary].join(' ')} />
|
||||
</div>
|
||||
}
|
||||
{ !!icon && <Icon id={icon} className={_s.cPrimary} size='18px' /> }
|
||||
<div className={[_s.d, _s.flexRow, _s.aiEnd, _s.ml15].join(' ')}>
|
||||
{ !!title && <Text size='large' weight='medium'>{title}</Text> }
|
||||
{ !!subtitle && <Text className={_s.ml5} size='small' color='secondary'>{subtitle}</Text> }
|
||||
{ !!title && <Text size='extraLarge' weight='medium'>{title}</Text> }
|
||||
{ !!subtitle && <Text className={_s.ml5} color='secondary'>{subtitle}</Text> }
|
||||
</div>
|
||||
<div className={[_s.d, _s.aiCenter, _s.mlAuto, _s.jcCenter].join(' ')}>
|
||||
{
|
||||
!!title &&
|
||||
{
|
||||
!!title &&
|
||||
<div className={[_s.d, _s.flexRow, _s.aiCenter, _s.mlAuto, _s.jcCenter].join(' ')}>
|
||||
<Button
|
||||
color='primary'
|
||||
backgroundColor='none'
|
||||
icon='list'
|
||||
isNarrow
|
||||
noClasses
|
||||
onClick={this.handleClickRefresh}
|
||||
className={[_s.d, _s.mr5, _s.cursorPointer, _s.outlineNone, _s.bgTransparent, _s.px5, _s.py5].join(' ')}
|
||||
iconClassName={_s.cSecondary}
|
||||
icon='repost'
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
<Button
|
||||
isNarrow
|
||||
noClasses
|
||||
onClick={this.handleClickDelete}
|
||||
className={[_s.d, _s.mr5, _s.cursorPointer, _s.outlineNone, _s.bgTransparent, _s.px5, _s.py5].join(' ')}
|
||||
iconClassName={_s.cSecondary}
|
||||
icon='trash'
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -41,6 +70,7 @@ DeckColumnHeader.propTypes = {
|
|||
title: PropTypes.string,
|
||||
subtitle: PropTypes.string,
|
||||
icon: PropTypes.string,
|
||||
index: PropTypes.number,
|
||||
}
|
||||
|
||||
export default DeckColumnHeader
|
||||
export default connect()(DeckColumnHeader)
|
|
@ -2,7 +2,6 @@ import React from 'react'
|
|||
import PropTypes from 'prop-types'
|
||||
import { connect } from 'react-redux'
|
||||
import { setDeckColumnAtIndex } from '../../actions/deck'
|
||||
import { saveSettings } from '../../actions/settings'
|
||||
import { openModal } from '../../actions/modal'
|
||||
import ModalLayout from './modal_layout'
|
||||
import Button from '../button'
|
||||
|
@ -11,6 +10,7 @@ import Text from '../text'
|
|||
class DeckColumnAddModal extends React.PureComponent {
|
||||
|
||||
onAdd = (column) => {
|
||||
console.log("onAdd column: ", column)
|
||||
switch (column) {
|
||||
case 'user':
|
||||
//
|
||||
|
@ -18,15 +18,12 @@ class DeckColumnAddModal extends React.PureComponent {
|
|||
case 'list':
|
||||
//
|
||||
break
|
||||
case 'groups':
|
||||
//
|
||||
break
|
||||
case 'group':
|
||||
//
|
||||
break
|
||||
default:
|
||||
this.props.dispatch(setDeckColumnAtIndex(column))
|
||||
this.props.dispatch(saveSettings())
|
||||
this.props.onClose()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +44,7 @@ class DeckColumnAddModal extends React.PureComponent {
|
|||
<div className={[_s.d, _s.pl10, _s.borderBottom1PX, _s.borderColorSecondary, _s.flexRow, _s.aiCenter, _s.jcCenter].join(' ')}>
|
||||
<DeckColumnAddModalButton icon='home' type='Home' onClick={() => this.onAdd('home')} />
|
||||
<DeckColumnAddModalButton icon='group' type='User' onClick={() => this.onAdd('user')} />
|
||||
<DeckColumnAddModalButton icon='notifications' type='Notifications' onClick={() => this.onAdd('home')} />
|
||||
<DeckColumnAddModalButton icon='notifications' type='Notifications' onClick={() => this.onAdd('notifications')} />
|
||||
</div>
|
||||
<div className={[_s.d, _s.pl10, _s.borderBottom1PX, _s.borderColorSecondary, _s.flexRow, _s.aiCenter, _s.jcCenter].join(' ')}>
|
||||
<DeckColumnAddModalButton icon='list' type='List' onClick={() => this.onAdd('list')} />
|
||||
|
@ -56,7 +53,7 @@ class DeckColumnAddModal extends React.PureComponent {
|
|||
</div>
|
||||
<div className={[_s.d, _s.pl10, _s.pb10, _s.flexRow, _s.aiCenter, _s.jcCenter].join(' ')}>
|
||||
<DeckColumnAddModalButton icon='pro' type='PRO Timeline' onClick={() => this.onAdd('pro')} />
|
||||
<DeckColumnAddModalButton icon='group' type='Groups Timeline' onClick={() => this.onAdd('groups')} />
|
||||
<DeckColumnAddModalButton icon='pencil' type='Compose' onClick={() => this.onAdd('compose')} />
|
||||
<DeckColumnAddModalButton icon='group' type='Group Timeline' onClick={() => this.onAdd('group')} />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -85,7 +82,7 @@ const DeckColumnAddModalButton = ({ icon, type, onClick }) => (
|
|||
|
||||
|
||||
DeckColumnAddModalButton.propTypes = {
|
||||
onSetDeckColumnAtIndex: PropTypes.func.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
export default connect()(DeckColumnAddModal)
|
|
@ -0,0 +1,96 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { connect } from 'react-redux'
|
||||
import { length } from 'stringz'
|
||||
import { openPopover } from '../../actions/popover'
|
||||
import { MAX_POST_CHARACTER_COUNT } from '../../constants'
|
||||
import Heading from '../heading'
|
||||
import Button from '../button'
|
||||
import BackButton from '../back_button'
|
||||
import Text from '../text'
|
||||
import CharacterCounter from '../character_counter'
|
||||
|
||||
class ComposeNavigationBar extends React.PureComponent {
|
||||
|
||||
handleOnPost = () => {
|
||||
//
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
isUploading,
|
||||
isChangingUpload,
|
||||
isSubmitting,
|
||||
anyMedia,
|
||||
text,
|
||||
} = this.props
|
||||
|
||||
const disabledButton = isSubmitting || isUploading || isChangingUpload || length(text) > MAX_POST_CHARACTER_COUNT || (length(text.trim()) === 0 && !anyMedia)
|
||||
const buttonOptions = {
|
||||
backgroundColor: disabledButton ? 'tertiary' : 'brand',
|
||||
color: disabledButton ? 'tertiary' : 'white',
|
||||
isDisabled: disabledButton,
|
||||
onClick: this.handleOnPost,
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={[_s.d, _s.z4, _s.h53PX, _s.w100PC].join(' ')}>
|
||||
<div className={[_s.d, _s.h53PX, _s.bgNavigation, _s.aiCenter, _s.z3, _s.top0, _s.right0, _s.left0, _s.posFixed].join(' ')} >
|
||||
|
||||
<div className={[_s.d, _s.flexRow, _s.saveAreaInsetPT, _s.saveAreaInsetPL, _s.saveAreaInsetPR, _s.w100PC].join(' ')}>
|
||||
|
||||
<BackButton
|
||||
toHome
|
||||
className={[_s.h53PX, _s.pl10, _s.pr10].join(' ')}
|
||||
iconSize='18px'
|
||||
iconClassName={[_s.mr5, _s.fillNavigation].join(' ')}
|
||||
/>
|
||||
|
||||
<div className={[_s.d, _s.h53PX, _s.flexRow, _s.jcCenter, _s.aiCenter, _s.mrAuto].join(' ')}>
|
||||
<Heading size='h1'>
|
||||
Compose
|
||||
</Heading>
|
||||
</div>
|
||||
|
||||
<div className={[_s.d, _s.h53PX, _s.flexRow, _s.mlAuto, _s.aiCenter, _s.jcCenter, _s.mr15].join(' ')}>
|
||||
<CharacterCounter max={MAX_POST_CHARACTER_COUNT} text={text} />
|
||||
|
||||
<Button {...buttonOptions}>
|
||||
<Text color='inherit' weight='bold' size='medium' className={_s.px5}>
|
||||
POST
|
||||
</Text>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, props) => ({
|
||||
isUploading: state.getIn(['compose', 'is_uploading']),
|
||||
isChangingUpload: state.getIn(['compose', 'is_changing_upload']),
|
||||
isSubmitting: state.getIn(['compose', 'is_submitting']),
|
||||
anyMedia: state.getIn(['compose', 'media_attachments']).size > 0,
|
||||
text: state.getIn(['compose', 'text']),
|
||||
})
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
onSubmitCompose() {
|
||||
//
|
||||
},
|
||||
})
|
||||
|
||||
ComposeNavigationBar.propTypes = {
|
||||
isUploading: PropTypes.bool,
|
||||
isChangingUpload: PropTypes.bool,
|
||||
isSubmitting: PropTypes.bool,
|
||||
anyMedia: PropTypes.bool,
|
||||
text: PropTypes.string,
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ComposeNavigationBar)
|
|
@ -11,6 +11,7 @@ import { makeGetAccount } from '../../selectors'
|
|||
import {
|
||||
THEMES,
|
||||
DEFAULT_THEME,
|
||||
MODAL_COMPOSE,
|
||||
POPOVER_NAV_SETTINGS,
|
||||
MODAL_DECK_COLUMN_ADD,
|
||||
} from '../../constants'
|
||||
|
@ -38,6 +39,10 @@ class DeckSidebar extends ImmutablePureComponent {
|
|||
this.props.onOpenNewColumnModal()
|
||||
}
|
||||
|
||||
handleOnOpenComposeModal = () => {
|
||||
this.props.onOpenComposeModal()
|
||||
}
|
||||
|
||||
setAvatarNode = (c) => {
|
||||
this.avatarNode = c
|
||||
}
|
||||
|
@ -70,9 +75,9 @@ class DeckSidebar extends ImmutablePureComponent {
|
|||
|
||||
<div className={[_s.d, _s.px10].join(' ')}>
|
||||
|
||||
<NavigationBarButton icon='pencil' />
|
||||
<NavigationBarButton icon='pencil' onClick={this.handleOnOpenComposeModal} />
|
||||
|
||||
<NavigationBarButton icon='search' />
|
||||
<NavigationBarButton icon='search' to='/search' />
|
||||
|
||||
<Divider isSmall />
|
||||
|
||||
|
@ -117,12 +122,15 @@ const mapDispatchToProps = (dispatch) => ({
|
|||
onOpenNavSettingsPopover(targetRef) {
|
||||
dispatch(openPopover(POPOVER_NAV_SETTINGS, {
|
||||
targetRef,
|
||||
position: 'right-start',
|
||||
position: 'top-start',
|
||||
}))
|
||||
},
|
||||
onOpenNewColumnModal() {
|
||||
dispatch(openModal(MODAL_DECK_COLUMN_ADD))
|
||||
},
|
||||
onOpenComposeModal() {
|
||||
dispatch(openModal(MODAL_COMPOSE))
|
||||
},
|
||||
onChange(key, value) {
|
||||
dispatch(changeSetting(['displayOptions', key], value))
|
||||
dispatch(saveSettings())
|
||||
|
@ -133,6 +141,7 @@ DeckSidebar.propTypes = {
|
|||
account: ImmutablePropTypes.map,
|
||||
onOpenNavSettingsPopover: PropTypes.func.isRequired,
|
||||
onOpenNewColumnModal: PropTypes.func.isRequired,
|
||||
onOpenComposeModal: PropTypes.func.isRequired,
|
||||
theme: PropTypes.string,
|
||||
logoDisabled: PropTypes.bool,
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ export const BREAKPOINT_LARGE = 1280
|
|||
export const BREAKPOINT_MEDIUM = 1160
|
||||
export const BREAKPOINT_SMALL = 1080
|
||||
export const BREAKPOINT_EXTRA_SMALL = 992
|
||||
export const BREAKPOINT_EXTRA_EXTRA_SMALL = 767
|
||||
|
||||
export const MOUSE_IDLE_DELAY = 300
|
||||
|
||||
|
@ -165,6 +166,8 @@ export const TIMELINE_INJECTION_WEIGHT_MULTIPLIER = 100
|
|||
export const TIMELINE_INJECTION_WEIGHT_SUBTRACTOR = 0.005
|
||||
export const TIMELINE_INJECTION_WEIGHT_MIN = 0.01
|
||||
|
||||
export const GAB_DECK_OPTIONS = ['home', 'user.id', 'notifications', 'list.id', 'likes', 'bookmarks', 'pro', 'compose', 'group.id']
|
||||
|
||||
export const TRENDS_RSS_SOURCES = [
|
||||
{'id':'5daf64b18e955e2433b0f5ce','title':'Breitbart'},
|
||||
{'id':'5daf66772fea4d3ba000883b','title':'Gateway Pundit'},
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { defineMessages, injectIntl } from 'react-intl'
|
||||
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,
|
||||
} from '../../../constants'
|
||||
import AutosuggestTextbox from '../../../components/autosuggest_textbox'
|
||||
import Responsive from '../../ui/util/responsive_component'
|
||||
import ResponsiveClassesComponent from '../../ui/util/responsive_classes_component'
|
||||
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'
|
||||
|
||||
class ComposeDestinationHeader extends ImmutablePureComponent {
|
||||
|
||||
handleOnClick = () => {
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
const { account } = 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>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ComposeDestinationHeader.propTypes = {
|
||||
account: ImmutablePropTypes.map,
|
||||
}
|
||||
|
||||
export default ComposeDestinationHeader
|
|
@ -2,6 +2,7 @@ import React from 'react'
|
|||
import PropTypes from 'prop-types'
|
||||
import { CX } from '../../../constants'
|
||||
import Button from '../../../components/button'
|
||||
import Text from '../../../components/text'
|
||||
|
||||
class ComposeExtraButton extends React.PureComponent {
|
||||
|
||||
|
@ -12,11 +13,19 @@ class ComposeExtraButton extends React.PureComponent {
|
|||
onClick,
|
||||
icon,
|
||||
children,
|
||||
small,
|
||||
active,
|
||||
buttonRef
|
||||
buttonRef,
|
||||
isLast,
|
||||
small,
|
||||
} = this.props
|
||||
|
||||
const containerClasses = CX({
|
||||
d: 1,
|
||||
mr5: 1,
|
||||
jcCenter: 1,
|
||||
h40PX: 1,
|
||||
})
|
||||
|
||||
const btnClasses = CX({
|
||||
d: 1,
|
||||
circle: 1,
|
||||
|
@ -26,45 +35,46 @@ class ComposeExtraButton extends React.PureComponent {
|
|||
textAlignCenter: 1,
|
||||
outlineNone: 1,
|
||||
bgTransparent: 1,
|
||||
flexRow: 1,
|
||||
bgSubtle_onHover: !active,
|
||||
bgBrandLight: active,
|
||||
py10: !small,
|
||||
px10: !small,
|
||||
py5: small,
|
||||
px5: small,
|
||||
mr2: !children,
|
||||
py10: 1,
|
||||
px10: 1,
|
||||
})
|
||||
|
||||
const iconClasses = CX({
|
||||
cSecondary: !active,
|
||||
cWhite: active,
|
||||
mr10: 1,
|
||||
py2: small,
|
||||
ml10: small,
|
||||
})
|
||||
|
||||
const iconSize = !!small ? '14px' : '16px'
|
||||
|
||||
const button = (
|
||||
<Button
|
||||
noClasses
|
||||
className={btnClasses}
|
||||
title={title}
|
||||
isDisabled={disabled}
|
||||
onClick={onClick}
|
||||
backgroundColor='none'
|
||||
iconClassName={iconClasses}
|
||||
icon={icon}
|
||||
iconSize={iconSize}
|
||||
buttonRef={!children ? buttonRef : undefined}
|
||||
/>
|
||||
)
|
||||
|
||||
if (!children) {
|
||||
return button
|
||||
}
|
||||
const iconSize = !small ? '18px' : '16px'
|
||||
const textColor = !active ? 'primary' : 'white'
|
||||
|
||||
return (
|
||||
<div className={[_s.d, _s.mr2].join(' ')} ref={buttonRef}>
|
||||
{button}
|
||||
{children}
|
||||
<div className={containerClasses} ref={buttonRef}>
|
||||
<Button
|
||||
noClasses
|
||||
className={btnClasses}
|
||||
title={title}
|
||||
isDisabled={disabled}
|
||||
onClick={onClick}
|
||||
backgroundColor='none'
|
||||
iconClassName={iconClasses}
|
||||
icon={icon}
|
||||
iconSize={iconSize}
|
||||
buttonRef={!children ? buttonRef : undefined}
|
||||
>
|
||||
{ children }
|
||||
{
|
||||
!small &&
|
||||
<Text color={textColor} weight='medium' className={[_s.pr5].join(' ')}>
|
||||
{title}
|
||||
</Text>
|
||||
}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -76,9 +86,9 @@ ComposeExtraButton.propTypes = {
|
|||
disabled: PropTypes.bool,
|
||||
onClick: PropTypes.func,
|
||||
icon: PropTypes.string,
|
||||
small: PropTypes.bool,
|
||||
active: PropTypes.bool,
|
||||
buttonRef: PropTypes.func,
|
||||
small: PropTypes.bool,
|
||||
}
|
||||
|
||||
export default ComposeExtraButton
|
|
@ -0,0 +1,88 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {
|
||||
CX,
|
||||
BREAKPOINT_EXTRA_SMALL,
|
||||
} from '../../../constants'
|
||||
import Responsive from '../../ui/util/responsive_component'
|
||||
import ResponsiveClassesComponent from '../../ui/util/responsive_classes_component'
|
||||
import EmojiPickerButton from './emoji_picker_button'
|
||||
import PollButton from './poll_button'
|
||||
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 StatusVisibilityButton from './status_visibility_button'
|
||||
import UploadButton from './media_upload_button'
|
||||
import { getWindowDimension } from '../../../utils/is_mobile'
|
||||
|
||||
const initialState = getWindowDimension()
|
||||
|
||||
class ComposeExtraButtonList extends React.PureComponent {
|
||||
|
||||
state = {
|
||||
height: initialState.height,
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.handleResize()
|
||||
window.addEventListener('keyup', this.handleKeyUp, false)
|
||||
window.addEventListener('resize', this.handleResize, false)
|
||||
}
|
||||
|
||||
handleResize = () => {
|
||||
const { height } = getWindowDimension()
|
||||
|
||||
this.setState({ height })
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('keyup', this.handleKeyUp)
|
||||
window.removeEventListener('resize', this.handleResize, false)
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
const { isMatch, edit, hidePro } = this.props
|
||||
const { height } = this.state
|
||||
|
||||
const small = height <= 660
|
||||
|
||||
const containerClasses = CX({
|
||||
d: 1,
|
||||
w100PC: 1,
|
||||
bgPrimary: 1,
|
||||
px15: 1,
|
||||
py10: 1,
|
||||
mtAuto: 1,
|
||||
boxShadowBlockY: 1,
|
||||
topLeftRadiusSmall: 1,
|
||||
borderColorSecondary: 1,
|
||||
topRightRadiusSmall: 1,
|
||||
flexRow: small,
|
||||
overflowXScroll: small,
|
||||
noScrollbar: small,
|
||||
})
|
||||
|
||||
return (
|
||||
<div className={containerClasses}>
|
||||
<UploadButton small={small} />
|
||||
<EmojiPickerButton isMatch={isMatch} small={small} />
|
||||
{ !edit && <PollButton small={small} /> }
|
||||
<StatusVisibilityButton small={small} />
|
||||
<SpoilerButton small={small} />
|
||||
{ !hidePro && !edit && <SchedulePostButton small={small} /> }
|
||||
{ !hidePro && !edit && <ExpiresPostButton small={small} /> }
|
||||
{ !hidePro && <RichTextEditorButton small={small} /> }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ComposeExtraButtonList.propTypes = {
|
||||
hidePro: PropTypes.bool,
|
||||
edit: PropTypes.bool,
|
||||
isMatch: PropTypes.bool,
|
||||
}
|
||||
|
||||
export default ComposeExtraButtonList
|
|
@ -11,13 +11,14 @@ 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'
|
||||
import Responsive from '../../ui/util/responsive_component'
|
||||
import ResponsiveClassesComponent from '../../ui/util/responsive_classes_component'
|
||||
import Avatar from '../../../components/avatar'
|
||||
import Button from '../../../components/button'
|
||||
import CharacterCounter from '../../../components/character_counter'
|
||||
import EmojiPickerButton from './emoji_picker_button'
|
||||
import PollButton from './poll_button'
|
||||
import PollForm from './poll_form'
|
||||
|
@ -30,6 +31,10 @@ 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 ComposeDestinationHeader from './compose_destination_header'
|
||||
|
||||
const messages = defineMessages({
|
||||
placeholder: { id: 'compose_form.placeholder', defaultMessage: "What's on your mind?" },
|
||||
|
@ -193,11 +198,12 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
isSubmitting,
|
||||
isPro,
|
||||
hidePro,
|
||||
isStandalone,
|
||||
} = this.props
|
||||
|
||||
const disabled = isSubmitting
|
||||
const text = [this.props.spoilerText, countableText(this.props.text)].join('');
|
||||
const disabledButton = disabled || isUploading || isChangingUpload || length(text) > MAX_POST_CHARACTER_COUNT || (length(text) !== 0 && length(text.trim()) === 0 && !anyMedia);
|
||||
const disabledButton = isSubmitting || isUploading || isChangingUpload || length(text) > MAX_POST_CHARACTER_COUNT || (length(text.trim()) === 0 && !anyMedia)
|
||||
const shouldAutoFocus = autoFocus && !showSearch && !isMobile(window.innerWidth)
|
||||
|
||||
const parentContainerClasses = CX({
|
||||
|
@ -238,220 +244,213 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
displayNone: length(this.props.text) === 0,
|
||||
})
|
||||
|
||||
return (
|
||||
<div className={[_s.d, _s.w100PC].join(' ')}>
|
||||
{
|
||||
shouldCondense &&
|
||||
<div className={parentContainerClasses}>
|
||||
<div className={[_s.d, _s.w100PC].join(' ')}>
|
||||
if (shouldCondense) {
|
||||
return (
|
||||
<div className={[_s.d, _s.w100PC].join(' ')}>
|
||||
<div className={[_s.d, _s.flexRow, _s.w100PC].join(' ')}>
|
||||
<div className={[_s.d, _s.mr10].join(' ')}>
|
||||
<Avatar account={account} size={28} noHover />
|
||||
</div>
|
||||
|
||||
<div className={[_s.d, _s.flexRow, _s.w100PC].join(' ')}>
|
||||
<div className={[_s.d, _s.mr10].join(' ')}>
|
||||
<Avatar account={account} size={28} 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(' ')}
|
||||
ref={this.setForm}
|
||||
onClick={this.handleClick}
|
||||
>
|
||||
|
||||
<div
|
||||
className={childContainerClasses}
|
||||
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'
|
||||
/>
|
||||
|
||||
<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={actionsContainerClasses}>
|
||||
<div className={[_s.d, _s.flexRow, _s.mrAuto].join(' ')}>
|
||||
|
||||
{ /* <EmojiPickerButton small={shouldCondense} isMatch={isMatch} /> */}
|
||||
|
||||
{ /* <UploadButton small={shouldCondense} /> */}
|
||||
|
||||
<div className={commentPublishBtnClasses}>
|
||||
<Button
|
||||
isNarrow
|
||||
onClick={this.handleSubmit}
|
||||
isDisabled={disabledButton}
|
||||
className={_s.px10}
|
||||
>
|
||||
{intl.formatMessage(scheduledAt ? messages.schedulePost : messages.publish)}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<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.publish)}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{
|
||||
(isUploading || anyMedia) &&
|
||||
<div className={[_s.d, _s.w100PC, _s.pl35, _s.mt5].join(' ')}>
|
||||
<UploadForm replyToId={replyToId} isModalOpen={isModalOpen} />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (isModalOpen) {
|
||||
//
|
||||
}
|
||||
|
||||
if (isStandalone || isModalOpen) {
|
||||
return (
|
||||
<div className={[_s.d, _s.w100PC, _s.flexGrow1, _s.bgTertiary].join(' ')}>
|
||||
|
||||
<div className={[_s.d, _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.w100PC, _s.pl35, _s.mt5].join(' ')}>
|
||||
<div className={[_s.d, _s.px15, _s.mt5].join(' ')}>
|
||||
<UploadForm
|
||||
replyToId={replyToId}
|
||||
isModalOpen={isModalOpen}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
!shouldCondense &&
|
||||
<div className={parentContainerClasses}>
|
||||
<div className={[_s.d, _s.flexRow, _s.w100PC].join(' ')}>
|
||||
<div
|
||||
className={childContainerClasses}
|
||||
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 className={actionsContainerClasses}>
|
||||
<div className={[_s.d, _s.flexRow, _s.mrAuto].join(' ')}>
|
||||
|
||||
<UploadButton small={shouldCondense} />
|
||||
|
||||
<EmojiPickerButton small={shouldCondense} isMatch={isMatch} />
|
||||
|
||||
{ /* <GifSelectorButton small={shouldCondense} /> */}
|
||||
|
||||
{
|
||||
!edit &&
|
||||
<PollButton />
|
||||
}
|
||||
|
||||
<StatusVisibilityButton />
|
||||
<SpoilerButton />
|
||||
{
|
||||
!hidePro && !edit &&
|
||||
<SchedulePostButton />
|
||||
}
|
||||
|
||||
{
|
||||
!hidePro && !edit &&
|
||||
<ExpiresPostButton />
|
||||
}
|
||||
|
||||
{
|
||||
!hidePro &&
|
||||
<Responsive min={BREAKPOINT_EXTRA_SMALL}>
|
||||
<RichTextEditorButton />
|
||||
</Responsive>
|
||||
}
|
||||
</div>
|
||||
|
||||
<Responsive min={BREAKPOINT_EXTRA_SMALL}>
|
||||
<CharacterCounter max={MAX_POST_CHARACTER_COUNT} text={text} />
|
||||
</Responsive>
|
||||
|
||||
<Button
|
||||
isOutline
|
||||
isDisabled={disabledButton}
|
||||
backgroundColor='none'
|
||||
color='brand'
|
||||
className={[_s.fs15PX, _s.px15].join(' ')}
|
||||
onClick={this.handleSubmit}
|
||||
>
|
||||
{intl.formatMessage(scheduledAt ? messages.schedulePost : edit ? messages.publishEdit : messages.publish)}
|
||||
</Button>
|
||||
{
|
||||
!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>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div className={[_s.d, _s.w100PC, _s.pt10, _s.px10].join(' ')}>
|
||||
<Button
|
||||
isBlock
|
||||
isDisabled={disabledButton}
|
||||
backgroundColor={disabledButton ? 'secondary' : 'brand'}
|
||||
color={disabledButton ? 'tertiary' : 'white'}
|
||||
className={[_s.fs15PX, _s.px15, _s.flexGrow1, _s.mlAuto].join(' ')}
|
||||
onClick={this.handleSubmit}
|
||||
>
|
||||
<Text color='inherit' weight='medium' align='center'>
|
||||
{intl.formatMessage(scheduledAt ? messages.schedulePost : edit ? messages.publishEdit : messages.publish)}
|
||||
</Text>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<ComposeExtraButtonList isMatch={isMatch} hidePro={hidePro} edit={edit} />
|
||||
</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} />
|
||||
<Responsive min={BREAKPOINT_EXTRA_EXTRA_SMALL}>
|
||||
<PollButton />
|
||||
</Responsive>
|
||||
<Button
|
||||
isOutline
|
||||
isDisabled={disabledButton}
|
||||
backgroundColor='none'
|
||||
color='brand'
|
||||
className={[_s.fs15PX, _s.px15, _s.flexGrow1, _s.maxW212PX, _s.mlAuto].join(' ')}
|
||||
onClick={this.handleSubmit}
|
||||
>
|
||||
<Text color='inherit' weight='medium' align='center'>
|
||||
{intl.formatMessage(scheduledAt ? messages.schedulePost : edit ? messages.publishEdit : messages.publish)}
|
||||
</Text>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -497,6 +496,7 @@ ComposeForm.propTypes = {
|
|||
isPro: PropTypes.bool,
|
||||
hidePro: PropTypes.bool,
|
||||
autoJoinGroup: PropTypes.bool,
|
||||
isStandalone: PropTypes.bool,
|
||||
}
|
||||
|
||||
ComposeForm.defaultProps = {
|
||||
|
|
|
@ -29,9 +29,9 @@ class EmojiPickerButton extends React.PureComponent {
|
|||
title={intl.formatMessage(messages.emoji)}
|
||||
onClick={this.handleClick}
|
||||
icon='happy'
|
||||
small={small}
|
||||
active={active && isMatch}
|
||||
buttonRef={this.setButton}
|
||||
small={small}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
@ -47,21 +47,19 @@ const mapStateToProps = (state) => ({
|
|||
})
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
|
||||
onClick(targetRef) {
|
||||
dispatch(openPopover('EMOJI_PICKER', {
|
||||
targetRef,
|
||||
}))
|
||||
},
|
||||
|
||||
})
|
||||
|
||||
EmojiPickerButton.propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
onClick: PropTypes.func.isRequired,
|
||||
active: PropTypes.bool,
|
||||
small: PropTypes.bool,
|
||||
isMatch: PropTypes.bool,
|
||||
small: PropTypes.bool,
|
||||
}
|
||||
|
||||
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(EmojiPickerButton))
|
|
@ -24,7 +24,14 @@ class UploadButton extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { intl, resetFileKey, unavailable, disabled, acceptContentTypes, small } = this.props
|
||||
const {
|
||||
intl,
|
||||
resetFileKey,
|
||||
unavailable,
|
||||
disabled,
|
||||
acceptContentTypes,
|
||||
small,
|
||||
} = this.props
|
||||
|
||||
if (unavailable) return null
|
||||
|
||||
|
@ -33,8 +40,8 @@ class UploadButton extends ImmutablePureComponent {
|
|||
title={intl.formatMessage(messages.title)}
|
||||
disabled={disabled}
|
||||
onClick={this.handleClick}
|
||||
icon='media'
|
||||
small={small}
|
||||
icon='media'
|
||||
>
|
||||
<label>
|
||||
<span className={_s.displayNone}>{intl.formatMessage(messages.upload)}</span>
|
||||
|
|
|
@ -12,13 +12,19 @@ class PollButton extends React.PureComponent {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { intl, active, unavailable, disabled, small } = this.props
|
||||
const {
|
||||
intl,
|
||||
active,
|
||||
unavailable,
|
||||
disabled,
|
||||
small,
|
||||
} = this.props
|
||||
|
||||
if (unavailable) return null
|
||||
|
||||
return (
|
||||
<ComposeExtraButton
|
||||
title={intl.formatMessage(active ? messages.remove_poll : messages.title)}
|
||||
title={intl.formatMessage(active ? messages.remove_poll : messages.add_poll)}
|
||||
disabled={disabled}
|
||||
onClick={this.handleClick}
|
||||
icon='poll'
|
||||
|
@ -42,7 +48,6 @@ const mapStateToProps = (state) => ({
|
|||
})
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
|
||||
onClick() {
|
||||
dispatch((_, getState) => {
|
||||
if (getState().getIn(['compose', 'poll'])) {
|
||||
|
@ -52,7 +57,6 @@ const mapDispatchToProps = (dispatch) => ({
|
|||
}
|
||||
})
|
||||
},
|
||||
|
||||
})
|
||||
|
||||
PollButton.propTypes = {
|
||||
|
|
|
@ -46,11 +46,9 @@ const mapStateToProps = (state) => ({
|
|||
})
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
|
||||
onChangeRichTextEditorControlsVisibility() {
|
||||
dispatch(changeRichTextEditorControlsVisibility())
|
||||
},
|
||||
|
||||
onOpenProUpgradeModal() {
|
||||
dispatch(openModal('PRO_UPGRADE'))
|
||||
},
|
||||
|
|
|
@ -39,11 +39,9 @@ const mapStateToProps = (state) => ({
|
|||
})
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
|
||||
onClick () {
|
||||
dispatch(changeComposeSpoilerness())
|
||||
},
|
||||
|
||||
})
|
||||
|
||||
SpoilerButton.propTypes = {
|
||||
|
|
|
@ -7,25 +7,13 @@ import ComposeFormContainer from './containers/compose_form_container'
|
|||
class Compose extends React.PureComponent {
|
||||
|
||||
componentWillUnmount() {
|
||||
this.props.onClearCompose()
|
||||
this.props.dispatch(clearCompose())
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div className={[_s.d, _s.bgPrimary, _s.borderBottom1PX, _s.borderColorSecondary].join(' ')}>
|
||||
<ComposeFormContainer isStandalone />
|
||||
</div>
|
||||
)
|
||||
return <ComposeFormContainer isStandalone />
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
onClearCompose:() => dispatch(clearCompose())
|
||||
})
|
||||
|
||||
Compose.propTypes = {
|
||||
onClearCompose: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(Compose)
|
||||
export default connect()(Compose)
|
|
@ -3,18 +3,30 @@ import PropTypes from 'prop-types'
|
|||
import { connect } from 'react-redux'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import {
|
||||
sortableContainer,
|
||||
sortableElement,
|
||||
} from 'react-sortable-hoc'
|
||||
import { me, meUsername} from '../initial_state'
|
||||
import {
|
||||
deckConnect,
|
||||
deckDisconnect,
|
||||
updateDeckColumnAtIndex,
|
||||
} from '../actions/deck'
|
||||
import {
|
||||
saveSettings,
|
||||
} from '../actions/settings'
|
||||
import WrappedBundle from './ui/util/wrapped_bundle'
|
||||
import DeckColumn from '../components/deck_column'
|
||||
import {
|
||||
AccountTimeline,
|
||||
Compose,
|
||||
LikedStatuses,
|
||||
HomeTimeline,
|
||||
Notifications,
|
||||
HashtagTimeline,
|
||||
BookmarkedStatuses,
|
||||
ProTimeline,
|
||||
} from './ui/util/async_components'
|
||||
|
||||
class Deck extends React.PureComponent {
|
||||
|
@ -39,36 +51,135 @@ class Deck extends React.PureComponent {
|
|||
this.props.dispatch(deckDisconnect())
|
||||
}
|
||||
|
||||
onSortEnd = ({oldIndex, newIndex}) => {
|
||||
this.props.dispatch(updateDeckColumnAtIndex(oldIndex, newIndex))
|
||||
}
|
||||
|
||||
onShouldCancelStart = (event) => {
|
||||
if (event.target) {
|
||||
if (!event.target.hasAttribute('data-sort-header')) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
getDeckColumn = (deckColumn, index) => {
|
||||
if (!deckColumn) return null
|
||||
|
||||
let Component = null
|
||||
let componentParams = {}
|
||||
let title, icon = ''
|
||||
|
||||
switch (deckColumn) {
|
||||
case 'notifications':
|
||||
title = 'Notifications'
|
||||
icon = 'notifications'
|
||||
Component = Notifications
|
||||
break
|
||||
case 'home':
|
||||
title = 'Home'
|
||||
icon = 'home'
|
||||
Component = HomeTimeline
|
||||
break
|
||||
case 'compose':
|
||||
title = 'Compose'
|
||||
icon = 'pencil'
|
||||
Component = Compose
|
||||
break
|
||||
case 'likes':
|
||||
title = 'Likes'
|
||||
icon = 'like'
|
||||
Component = LikedStatuses
|
||||
componentParams = { params: { username: meUsername }}
|
||||
break
|
||||
case 'bookmarks':
|
||||
title = 'Bookmarks'
|
||||
icon = 'bookmark'
|
||||
Component = BookmarkedStatuses
|
||||
componentParams = { params: { username: meUsername }}
|
||||
break
|
||||
case 'pro':
|
||||
title = 'Pro Timeline'
|
||||
icon = 'pro'
|
||||
Component = ProTimeline
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
if (!Component) {
|
||||
if (deckColumn.indexOf('user.') > -1) {
|
||||
|
||||
} else if (deckColumn.indexOf('list.') > -1) {
|
||||
|
||||
} else if (deckColumn.indexOf('group.') > -1) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (!Component) return null
|
||||
|
||||
return (
|
||||
<SortableItem
|
||||
key={`deck-column-${index}`}
|
||||
index={index}
|
||||
sortIndex={index}
|
||||
>
|
||||
<DeckColumn title={title} icon={icon} index={index}>
|
||||
<WrappedBundle component={Component} componentParams={componentParams} />
|
||||
</DeckColumn>
|
||||
</SortableItem>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
const { gabDeckOrder } = this.props
|
||||
|
||||
console.log("gabDeckOrder:", gabDeckOrder)
|
||||
|
||||
const isEmpty = gabDeckOrder.size === 0
|
||||
// : todo : max: 12
|
||||
|
||||
return (
|
||||
<div className={[_s.d, _s.flexRow].join(' ')}>
|
||||
<DeckColumn title='Compose' icon='pencil'>
|
||||
<WrappedBundle component={Compose} />
|
||||
</DeckColumn>
|
||||
<DeckColumn />
|
||||
{/*<DeckColumn title='Home' icon='home'>
|
||||
<WrappedBundle component={HomeTimeline} />
|
||||
</DeckColumn>
|
||||
<DeckColumn title='Notifications' icon='notifications'>
|
||||
<WrappedBundle component={Notifications} />
|
||||
</DeckColumn>
|
||||
<DeckColumn title='Cashtag' icon='list' subtitle='$BTC'>
|
||||
<WrappedBundle component={HashtagTimeline} componentParams={{ params: { id: 'btc' } }} />
|
||||
</DeckColumn>
|
||||
<DeckColumn title='Jonny' icon='group' subtitle='@admin'>
|
||||
<WrappedBundle component={AccountTimeline} componentParams={{ account }} />
|
||||
</DeckColumn>
|
||||
</DeckColumn>*/}
|
||||
</div>
|
||||
<SortableContainer
|
||||
axis='x'
|
||||
lockAxis='x'
|
||||
onSortEnd={this.onSortEnd}
|
||||
shouldCancelStart={this.onShouldCancelStart}
|
||||
>
|
||||
{
|
||||
isEmpty &&
|
||||
<React.Fragment>
|
||||
<DeckColumn title='Compose' icon='pencil'>
|
||||
<WrappedBundle component={Compose} />
|
||||
</DeckColumn>
|
||||
<DeckColumn />
|
||||
</React.Fragment>
|
||||
}
|
||||
{
|
||||
!isEmpty &&
|
||||
gabDeckOrder.map((deckOption, i) => this.getDeckColumn(deckOption, i))
|
||||
}
|
||||
</SortableContainer>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const SortableItem = sortableElement(({children}) => (
|
||||
<div>
|
||||
{children}
|
||||
</div>
|
||||
))
|
||||
|
||||
const SortableContainer = sortableContainer(({children}) => (
|
||||
<div className={[_s.d, _s.flexRow, _s.listStyleNone].join(' ')}>
|
||||
{children}
|
||||
</div>
|
||||
))
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
gabDeckOrder: state.getIn(['settings', 'gabDeckOrder']),
|
||||
})
|
||||
|
|
|
@ -336,6 +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>
|
||||
<Icon id='check' size='14px' className={_s.cWhite} />
|
||||
</Responsive>
|
||||
</React.Fragment>
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { me } from '../initial_state'
|
||||
import DefaultSidebar from '../components/sidebar/default_sidebar'
|
||||
import ComposeNavigationBar from '../components/navigation_bar/compose_navigation_bar_xs'
|
||||
import Responsive from '../features/ui/util/responsive_component'
|
||||
import WrappedBundle from '../features/ui/util/wrapped_bundle'
|
||||
import {
|
||||
SidebarXS,
|
||||
} from '../features/ui/util/async_components'
|
||||
|
||||
class ComposeLayout extends React.PureComponent {
|
||||
|
||||
render() {
|
||||
const { children, isXS } = this.props
|
||||
|
||||
if (!isXS) return null
|
||||
|
||||
return (
|
||||
<div className={[_s.d, _s.w100PC, _s.minH100VH, _s.bgTertiary].join(' ')}>
|
||||
<WrappedBundle component={SidebarXS} />
|
||||
|
||||
<ComposeNavigationBar />
|
||||
|
||||
<main role='main' className={[_s.d, _s.w100PC, _s.flexGrow1].join(' ')}>
|
||||
{ children }
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ComposeLayout.propTypes = {
|
||||
children: PropTypes.node,
|
||||
}
|
||||
|
||||
export default ComposeLayout
|
|
@ -1,33 +1,47 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import PageTitle from '../features/ui/util/page_title'
|
||||
import DefaultLayout from '../layouts/default_layout'
|
||||
import ComposeLayout from '../layouts/compose_layout'
|
||||
import { BREAKPOINT_EXTRA_SMALL } from '../constants'
|
||||
import { getWindowDimension } from '../utils/is_mobile'
|
||||
|
||||
const initialState = getWindowDimension()
|
||||
|
||||
class ComposePage extends React.PureComponent {
|
||||
|
||||
state = {
|
||||
width: initialState.width,
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.handleResize()
|
||||
window.addEventListener('keyup', this.handleKeyUp, false)
|
||||
window.addEventListener('resize', this.handleResize, false)
|
||||
}
|
||||
|
||||
handleResize = () => {
|
||||
const { width } = getWindowDimension()
|
||||
|
||||
this.setState({ width })
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('keyup', this.handleKeyUp)
|
||||
window.removeEventListener('resize', this.handleResize, false)
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
children,
|
||||
page,
|
||||
title,
|
||||
} = this.props
|
||||
const { children } = this.props
|
||||
const { width } = this.state
|
||||
|
||||
const isXS = width <= BREAKPOINT_EXTRA_SMALL
|
||||
if (!isXS) throw 'This page does not exist'
|
||||
|
||||
return (
|
||||
<DefaultLayout
|
||||
noComposeButton
|
||||
showBackBtn
|
||||
title={title}
|
||||
page={page}
|
||||
actions={[
|
||||
{
|
||||
title: 'Post',
|
||||
onClick: this.onOpenCommunityPageSettingsModal,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<PageTitle path={title} />
|
||||
<ComposeLayout title='Compose' isXS={isXS}>
|
||||
<PageTitle path='Compose' />
|
||||
{children}
|
||||
</DefaultLayout>
|
||||
</ComposeLayout>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,11 @@ import { STORE_HYDRATE } from '../actions/store'
|
|||
import { EMOJI_USE } from '../actions/emojis'
|
||||
import { LIST_DELETE_SUCCESS, LIST_FETCH_FAIL } from '../actions/lists'
|
||||
import { TIMELINE_INJECTION_HIDE } from '../actions/timeline_injections'
|
||||
import {
|
||||
DECK_SET_COLUMN_AT_INDEX,
|
||||
DECK_DELETE_COLUMN_AT_INDEX,
|
||||
DECK_CHANGE_COLUMN_AT_INDEX,
|
||||
} from '../actions/deck'
|
||||
import {
|
||||
COMMENT_SORTING_TYPE_OLDEST,
|
||||
TIMELINE_INJECTION_WEIGHT_DEFAULT,
|
||||
|
@ -14,7 +19,7 @@ import {
|
|||
TIMELINE_INJECTION_SHOP,
|
||||
TIMELINE_INJECTION_USER_SUGGESTIONS,
|
||||
} from '../constants'
|
||||
import { Map as ImmutableMap, fromJS } from 'immutable'
|
||||
import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable'
|
||||
import uuid from '../utils/uuid'
|
||||
|
||||
const initialState = ImmutableMap({
|
||||
|
@ -23,6 +28,7 @@ const initialState = ImmutableMap({
|
|||
skinTone: 1,
|
||||
isCompact: false,
|
||||
commentSorting: COMMENT_SORTING_TYPE_OLDEST,
|
||||
gabDeckOrder: ImmutableList([]),
|
||||
|
||||
// every dismiss reduces by half or set to zero for pwa, shop, pro
|
||||
injections: ImmutableMap({
|
||||
|
@ -55,37 +61,50 @@ const initialState = ImmutableMap({
|
|||
onlyMedia: false,
|
||||
}),
|
||||
}),
|
||||
});
|
||||
})
|
||||
|
||||
const defaultColumns = fromJS([
|
||||
{ id: 'COMPOSE', uuid: uuid(), params: {} },
|
||||
{ id: 'HOME', uuid: uuid(), params: {} },
|
||||
{ id: 'NOTIFICATIONS', uuid: uuid(), params: {} },
|
||||
]);
|
||||
])
|
||||
|
||||
const hydrate = (state, settings) => state.mergeDeep(settings).update('columns', (val = defaultColumns) => val);
|
||||
const hydrate = (state, settings) => state.mergeDeep(settings).update('columns', (val = defaultColumns) => val)
|
||||
|
||||
const updateFrequentEmojis = (state, emoji) => state.update('frequentlyUsedEmojis', ImmutableMap(), map => map.update(emoji.id, 0, count => count + 1)).set('saved', false);
|
||||
const updateFrequentEmojis = (state, emoji) => state.update('frequentlyUsedEmojis', ImmutableMap(), map => map.update(emoji.id, 0, count => count + 1)).set('saved', false)
|
||||
|
||||
const filterDeadListColumns = (state, listId) => state.update('columns', columns => columns.filterNot(column => column.get('id') === 'LIST' && column.get('params').get('id') === listId));
|
||||
const filterDeadListColumns = (state, listId) => state.update('columns', columns => columns.filterNot(column => column.get('id') === 'LIST' && column.get('params').get('id') === listId))
|
||||
|
||||
export default function settings(state = initialState, action) {
|
||||
switch(action.type) {
|
||||
case STORE_HYDRATE:
|
||||
return hydrate(state, action.state.get('settings'));
|
||||
return hydrate(state, action.state.get('settings'))
|
||||
case SETTING_CHANGE:
|
||||
return state
|
||||
.setIn(action.path, action.value)
|
||||
.set('saved', false);
|
||||
.set('saved', false)
|
||||
case EMOJI_USE:
|
||||
return updateFrequentEmojis(state, action.emoji);
|
||||
return updateFrequentEmojis(state, action.emoji)
|
||||
case SETTING_SAVE:
|
||||
return state.set('saved', true);
|
||||
return state.set('saved', true)
|
||||
case LIST_FETCH_FAIL:
|
||||
return action.error.response.status === 404 ? filterDeadListColumns(state, action.id) : state;
|
||||
return action.error.response.status === 404 ? filterDeadListColumns(state, action.id) : state
|
||||
case LIST_DELETE_SUCCESS:
|
||||
return filterDeadListColumns(state, action.id);
|
||||
return filterDeadListColumns(state, action.id)
|
||||
case DECK_SET_COLUMN_AT_INDEX:
|
||||
// : todo : max: 12
|
||||
const sizeOfDeck = state.get('gabDeckOrder', ImmutableList()).size
|
||||
const newIndex = Math.max(action.index || 0, sizeOfDeck)
|
||||
return state.setIn(['gabDeckOrder', newIndex + 1], action.column).set('saved', false)
|
||||
case DECK_DELETE_COLUMN_AT_INDEX:
|
||||
return state.deleteIn(['gabDeckOrder', action.index])
|
||||
case DECK_CHANGE_COLUMN_AT_INDEX:
|
||||
return state.update('gabDeckOrder', idsList => idsList.withMutations((list) => {
|
||||
let soruce = list.get(action.oldIndex)
|
||||
let destination = list.get(action.newIndex)
|
||||
return list.set(action.newIndex, soruce).set(action.oldIndex, destination)
|
||||
}))
|
||||
default:
|
||||
return state;
|
||||
return state
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -395,6 +395,7 @@ pre {
|
|||
|
||||
.cursorText { cursor: text; }
|
||||
.cursorPointer { cursor: pointer; }
|
||||
.cursorEWResize { cursor: ew-resize; }
|
||||
.cursorNotAllowed { cursor: not-allowed; }
|
||||
|
||||
.backgroundCandy {
|
||||
|
@ -536,6 +537,11 @@ pre {
|
|||
.calcH53PX { height: calc(100vh - 53px); }
|
||||
.calcH80VH106PX { height: calc(80vh - 106px); }
|
||||
|
||||
.calcMaxH370PX { max-height: calc(100vh - 370px); }
|
||||
@media (min-height: 0px) and (max-height:660px) {
|
||||
.calcMaxH370PX { max-height: calc(100vh - 140px); }
|
||||
}
|
||||
|
||||
.minH100VH { min-height: 100vh; }
|
||||
.minH50VH { min-height: 50vh; }
|
||||
.minH200PX { min-height: 200px; }
|
||||
|
@ -908,6 +914,7 @@ pre {
|
|||
.boxShadow1 { box-shadow: 1px 1px 1px 1px rgba(0, 0, 0, .25); }
|
||||
.boxShadowPopover { box-shadow: 0 0 15px -5px rgba(0,0,0,0.15); }
|
||||
.boxShadowBlock { box-shadow: 0 1px 2px rgba(0,0,0,0.2); }
|
||||
.boxShadowBlockY { box-shadow: 0 -1px 2px rgba(0,0,0,0.2); }
|
||||
.boxShadowDot { box-shadow: inset 0 0 0 3px #fff, inset 0 0 0 6px #000; }
|
||||
.boxShadowToast { box-shadow: 0px 0px 10px -2px rgba(0, 0, 0, .2), 0px 0px 2px -1px rgba(0, 0, 0, 0.4); }
|
||||
|
||||
|
|
82
yarn.lock
82
yarn.lock
|
@ -2143,11 +2143,16 @@ core-js-compat@^3.6.2:
|
|||
browserslist "^4.8.5"
|
||||
semver "7.0.0"
|
||||
|
||||
core-js@^2.4.0, core-js@^2.4.1:
|
||||
core-js@^2.4.0:
|
||||
version "2.6.11"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c"
|
||||
integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==
|
||||
|
||||
core-js@^3.6.4:
|
||||
version "3.8.1"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.8.1.tgz#f51523668ac8a294d1285c3b9db44025fda66d47"
|
||||
integrity sha512-9Id2xHY1W7m8hCl8NkhQn5CufmF/WuR30BTRewvCXc1aZd3kMECwNZ69ndLbekKfakw9Rf2Xyc+QR6E7Gg+obg==
|
||||
|
||||
core-js@^3.6.5:
|
||||
version "3.6.5"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.5.tgz#7395dc273af37fb2e50e9bd3d9fe841285231d1a"
|
||||
|
@ -2232,6 +2237,13 @@ cross-env@^5.1.4:
|
|||
dependencies:
|
||||
cross-spawn "^6.0.5"
|
||||
|
||||
cross-fetch@^3.0.4:
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.0.6.tgz#3a4040bc8941e653e0e9cf17f29ebcd177d3365c"
|
||||
integrity sha512-KBPUbqgFjzWlVcURG+Svp9TlhA5uliYtiNx/0r8nv0pdypeQCRJ9IaSIc3q/x3q8t3F75cHuwxVql1HFGHCNJQ==
|
||||
dependencies:
|
||||
node-fetch "2.6.1"
|
||||
|
||||
cross-spawn@6.0.5, cross-spawn@^6.0.0, cross-spawn@^6.0.5:
|
||||
version "6.0.5"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
|
||||
|
@ -2746,12 +2758,12 @@ double-ended-queue@^2.1.0-0:
|
|||
resolved "https://registry.yarnpkg.com/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz#103d3527fd31528f40188130c841efdd78264e5c"
|
||||
integrity sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw=
|
||||
|
||||
draft-js@^0.11.4:
|
||||
version "0.11.5"
|
||||
resolved "https://registry.yarnpkg.com/draft-js/-/draft-js-0.11.5.tgz#b5dd30c30c9316801ab9766d45a8f88b1cd43b2c"
|
||||
integrity sha512-RlHcoDtblwCD6Bw2ay3D0dTLA6lH8862OcdJrHGnYrY5JjlitzSzGvGQwqqumVvbGUNDSZLD3FyNpSs4z5WIUg==
|
||||
draft-js@^0.11.7:
|
||||
version "0.11.7"
|
||||
resolved "https://registry.yarnpkg.com/draft-js/-/draft-js-0.11.7.tgz#be293aaa255c46d8a6647f3860aa4c178484a206"
|
||||
integrity sha512-ne7yFfN4sEL82QPQEn80xnADR8/Q6ALVworbC5UOSzOvjffmYfFsr3xSZtxbIirti14R7Y33EZC5rivpLgIbsg==
|
||||
dependencies:
|
||||
fbjs "^1.0.0"
|
||||
fbjs "^2.0.0"
|
||||
immutable "~3.7.4"
|
||||
object-assign "^4.1.1"
|
||||
|
||||
|
@ -2827,13 +2839,6 @@ encodeurl@~1.0.2:
|
|||
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
|
||||
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
|
||||
|
||||
encoding@^0.1.11:
|
||||
version "0.1.12"
|
||||
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb"
|
||||
integrity sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=
|
||||
dependencies:
|
||||
iconv-lite "~0.4.13"
|
||||
|
||||
end-of-stream@^1.0.0, end-of-stream@^1.1.0:
|
||||
version "1.4.4"
|
||||
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
|
||||
|
@ -3298,14 +3303,14 @@ fbjs-css-vars@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz#216551136ae02fe255932c3ec8775f18e2c078b8"
|
||||
integrity sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==
|
||||
|
||||
fbjs@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-1.0.0.tgz#52c215e0883a3c86af2a7a776ed51525ae8e0a5a"
|
||||
integrity sha512-MUgcMEJaFhCaF1QtWGnmq9ZDRAzECTCRAF7O6UZIlAlkTs1SasiX9aP0Iw7wfD2mJ7wDTNfg2w7u5fSCwJk1OA==
|
||||
fbjs@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-2.0.0.tgz#01fb812138d7e31831ed3e374afe27b9169ef442"
|
||||
integrity sha512-8XA8ny9ifxrAWlyhAbexXcs3rRMtxWcs3M0lctLfB49jRDHiaxj+Mo0XxbwE7nKZYzgCFoq64FS+WFd4IycPPQ==
|
||||
dependencies:
|
||||
core-js "^2.4.1"
|
||||
core-js "^3.6.4"
|
||||
cross-fetch "^3.0.4"
|
||||
fbjs-css-vars "^1.0.0"
|
||||
isomorphic-fetch "^2.1.1"
|
||||
loose-envify "^1.0.0"
|
||||
object-assign "^4.1.0"
|
||||
promise "^7.1.1"
|
||||
|
@ -3901,7 +3906,7 @@ https-browserify@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
|
||||
integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=
|
||||
|
||||
iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@~0.4.13:
|
||||
iconv-lite@0.4.24, iconv-lite@^0.4.24:
|
||||
version "0.4.24"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
|
||||
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
|
||||
|
@ -4347,7 +4352,7 @@ is-resolvable@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88"
|
||||
integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==
|
||||
|
||||
is-stream@^1.0.1, is-stream@^1.1.0:
|
||||
is-stream@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
|
||||
integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
|
||||
|
@ -4408,14 +4413,6 @@ isobject@^3.0.0, isobject@^3.0.1:
|
|||
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
|
||||
integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
|
||||
|
||||
isomorphic-fetch@^2.1.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9"
|
||||
integrity sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=
|
||||
dependencies:
|
||||
node-fetch "^1.0.1"
|
||||
whatwg-fetch ">=0.10.0"
|
||||
|
||||
js-base64@^2.1.9:
|
||||
version "2.5.2"
|
||||
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.5.2.tgz#313b6274dda718f714d00b3330bbae6e38e90209"
|
||||
|
@ -5080,13 +5077,10 @@ nice-try@^1.0.4:
|
|||
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
|
||||
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
|
||||
|
||||
node-fetch@^1.0.1:
|
||||
version "1.7.3"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
|
||||
integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==
|
||||
dependencies:
|
||||
encoding "^0.1.11"
|
||||
is-stream "^1.0.1"
|
||||
node-fetch@2.6.1:
|
||||
version "2.6.1"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
||||
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
||||
|
||||
node-forge@0.9.0:
|
||||
version "0.9.0"
|
||||
|
@ -6148,7 +6142,7 @@ promise@^7.1.1:
|
|||
dependencies:
|
||||
asap "~2.0.3"
|
||||
|
||||
prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2:
|
||||
prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2:
|
||||
version "15.7.2"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
|
||||
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
|
||||
|
@ -6492,6 +6486,15 @@ react-router@^4.3.1:
|
|||
prop-types "^15.6.1"
|
||||
warning "^4.0.1"
|
||||
|
||||
react-sortable-hoc@^1.11.0:
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/react-sortable-hoc/-/react-sortable-hoc-1.11.0.tgz#fe4022362bbafc4b836f5104b9676608a40a278f"
|
||||
integrity sha512-v1CDCvdfoR3zLGNp6qsBa4J1BWMEVH25+UKxF/RvQRh+mrB+emqtVHMgZ+WreUiKJoEaiwYoScaueIKhMVBHUg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.2.0"
|
||||
invariant "^2.2.4"
|
||||
prop-types "^15.5.7"
|
||||
|
||||
react-stickynode@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/react-stickynode/-/react-stickynode-3.0.4.tgz#1e9c096cec3613cc8294807eba319ced074c8b21"
|
||||
|
@ -8211,11 +8214,6 @@ websocket.js@^0.1.12:
|
|||
dependencies:
|
||||
backoff "^2.4.1"
|
||||
|
||||
whatwg-fetch@>=0.10.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb"
|
||||
integrity sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==
|
||||
|
||||
which-module@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
|
||||
|
|
Loading…
Reference in New Issue