Progress
This commit is contained in:
parent
0f01c1bc97
commit
3d0a85cde4
54
app/javascript/gabsocial/components/account_action_button.js
Normal file
54
app/javascript/gabsocial/components/account_action_button.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import classNames from 'classnames/bind'
|
||||||
|
|
||||||
|
const cx = classNames.bind(_s)
|
||||||
|
|
||||||
|
// : todo :
|
||||||
|
|
||||||
|
export default class AccountActionButton extends PureComponent {
|
||||||
|
static propTypes = {
|
||||||
|
children: PropTypes.any,
|
||||||
|
size: PropTypes.oneOf(Object.keys(SIZES)),
|
||||||
|
center: PropTypes.bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
size: SIZES.h1,
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { children, size, center } = this.props
|
||||||
|
|
||||||
|
const classes = cx({
|
||||||
|
default: 1,
|
||||||
|
text: 1,
|
||||||
|
textAlignCenter: center,
|
||||||
|
|
||||||
|
colorPrimary: [SIZES.h1, SIZES.h3].indexOf(size) > -1,
|
||||||
|
colorSecondary: [SIZES.h2, SIZES.h4, SIZES.h5].indexOf(size) > -1,
|
||||||
|
|
||||||
|
fontSize24PX: size === SIZES.h1,
|
||||||
|
fontSize19PX: size === SIZES.h2,
|
||||||
|
fontSize16PX: size === SIZES.h3,
|
||||||
|
fontSize13PX: size === SIZES.h4,
|
||||||
|
fontSize12PX: size === SIZES.h5,
|
||||||
|
|
||||||
|
mt5: [SIZES.h2, SIZES.h4].indexOf(size) > -1,
|
||||||
|
|
||||||
|
lineHeight2: size === SIZES.h5,
|
||||||
|
py2: size === SIZES.h5,
|
||||||
|
|
||||||
|
// fontWeightNormal: weight === WEIGHTS.normal,
|
||||||
|
fontWeightMedium: [SIZES.h1, SIZES.h5].indexOf(size) > -1,
|
||||||
|
fontWeightBold: [SIZES.h3, SIZES.h4].indexOf(size) > -1,
|
||||||
|
})
|
||||||
|
|
||||||
|
return React.createElement(
|
||||||
|
size,
|
||||||
|
{
|
||||||
|
className: classes,
|
||||||
|
role: 'heading',
|
||||||
|
},
|
||||||
|
children,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import { defineMessages, injectIntl } from 'react-intl'
|
import { defineMessages, injectIntl } from 'react-intl'
|
||||||
import IconButton from './icon_button'
|
import Button from './button'
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
title: { id: 'bundle_column_error.title', defaultMessage: 'Network error' },
|
title: { id: 'bundle_column_error.title', defaultMessage: 'Network error' },
|
||||||
@ -25,7 +25,7 @@ class BundleColumnError extends PureComponent {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='error-column'>
|
<div className='error-column'>
|
||||||
<IconButton title={formatMessage(messages.retry)} icon='refresh' onClick={this.handleRetry} size={64} />
|
<Button title={formatMessage(messages.retry)} icon='refresh' onClick={this.handleRetry} size={64} />
|
||||||
{formatMessage(messages.body)}
|
{formatMessage(messages.body)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
import IconButton from './icon_button';
|
import Button from './button';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
error: { id: 'bundle_modal_error.message', defaultMessage: 'Something went wrong while loading this component.' },
|
error: { id: 'bundle_modal_error.message', defaultMessage: 'Something went wrong while loading this component.' },
|
||||||
@ -29,7 +29,7 @@ class BundleModalError extends PureComponent {
|
|||||||
return (
|
return (
|
||||||
<div className='modal-root__modal error-modal'>
|
<div className='modal-root__modal error-modal'>
|
||||||
<div className='error-modal__body'>
|
<div className='error-modal__body'>
|
||||||
<IconButton title={formatMessage(messages.retry)} icon='refresh' onClick={this.handleRetry} size={64} />
|
<Button title={formatMessage(messages.retry)} icon='refresh' onClick={this.handleRetry} size={64} />
|
||||||
{formatMessage(messages.error)}
|
{formatMessage(messages.error)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -10,8 +10,9 @@ const COLORS = {
|
|||||||
secondary: 'secondary',
|
secondary: 'secondary',
|
||||||
tertiary: 'tertiary',
|
tertiary: 'tertiary',
|
||||||
white: 'white',
|
white: 'white',
|
||||||
|
black: 'black',
|
||||||
brand: 'brand',
|
brand: 'brand',
|
||||||
error: 'error',
|
danger: 'danger',
|
||||||
none: 'none',
|
none: 'none',
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,12 +88,11 @@ export default class Button extends PureComponent {
|
|||||||
<Icon
|
<Icon
|
||||||
id={icon}
|
id={icon}
|
||||||
width={iconWidth}
|
width={iconWidth}
|
||||||
height={iconWidth}
|
height={iconHeight}
|
||||||
className={iconClassName}
|
className={iconClassName}
|
||||||
/>
|
/>
|
||||||
) : undefined
|
) : undefined
|
||||||
|
|
||||||
// : todo :
|
|
||||||
const classes = noClasses ? className : cx(className, {
|
const classes = noClasses ? className : cx(className, {
|
||||||
default: 1,
|
default: 1,
|
||||||
noUnderline: 1,
|
noUnderline: 1,
|
||||||
@ -101,18 +101,22 @@ export default class Button extends PureComponent {
|
|||||||
textAlignCenter: 1,
|
textAlignCenter: 1,
|
||||||
outlineNone: 1,
|
outlineNone: 1,
|
||||||
flexRow: !!children && !!icon,
|
flexRow: !!children && !!icon,
|
||||||
|
cursorNotAllowed: disabled,
|
||||||
|
opacity05: disabled,
|
||||||
|
|
||||||
backgroundColorPrimary: backgroundColor === COLORS.white,
|
backgroundColorPrimary: backgroundColor === COLORS.white,
|
||||||
|
backgroundColorBlack: backgroundColor === COLORS.black,
|
||||||
backgroundColorBrand: backgroundColor === COLORS.brand,
|
backgroundColorBrand: backgroundColor === COLORS.brand,
|
||||||
backgroundTransparent: backgroundColor === COLORS.none,
|
backgroundTransparent: backgroundColor === COLORS.none,
|
||||||
backgroundSubtle2: backgroundColor === COLORS.tertiary,
|
backgroundSubtle2: backgroundColor === COLORS.tertiary,
|
||||||
backgroundSubtle: backgroundColor === COLORS.secondary,
|
backgroundSubtle: backgroundColor === COLORS.secondary,
|
||||||
|
backgroundColorDanger: backgroundColor === COLORS.danger,
|
||||||
|
|
||||||
colorPrimary: color === COLORS.primary,
|
colorPrimary: !!children && color === COLORS.primary,
|
||||||
colorSecondary: color === COLORS.secondary,
|
colorSecondary: !!children && color === COLORS.secondary,
|
||||||
colorTertiary: color === COLORS.tertiary,
|
colorTertiary: !!children && color === COLORS.tertiary,
|
||||||
colorWhite: color === COLORS.white,
|
colorWhite: !!children && color === COLORS.white,
|
||||||
colorBrand: color === COLORS.brand,
|
colorBrand: !!children && color === COLORS.brand,
|
||||||
|
|
||||||
borderColorBrand: color === COLORS.brand && outline,
|
borderColorBrand: color === COLORS.brand && outline,
|
||||||
border1PX: outline,
|
border1PX: outline,
|
||||||
@ -128,12 +132,16 @@ export default class Button extends PureComponent {
|
|||||||
|
|
||||||
underline_onHover: underlineOnHover,
|
underline_onHover: underlineOnHover,
|
||||||
|
|
||||||
backgroundSubtle2Dark_onHover: backgroundColor === COLORS.tertiary,
|
backgroundSubtle2Dark_onHover: backgroundColor === COLORS.tertiary || backgroundColor === COLORS.secondary,
|
||||||
|
backgroundColorBlackOpaque_onHover: backgroundColor === COLORS.black,
|
||||||
backgroundColorBrandDark_onHover: backgroundColor === COLORS.brand,
|
backgroundColorBrandDark_onHover: backgroundColor === COLORS.brand,
|
||||||
|
|
||||||
backgroundColorBrand_onHover: color === COLORS.brand && outline,
|
backgroundColorBrand_onHover: color === COLORS.brand && outline,
|
||||||
colorWhite_onHover: color === COLORS.brand && outline,
|
colorWhite_onHover: !!children && color === COLORS.brand && outline,
|
||||||
|
|
||||||
|
fillColorWhite: !!icon && color === COLORS.white,
|
||||||
|
fillColorBrand: !!icon && color === COLORS.brand,
|
||||||
|
fillColorWhite_onHover: !!icon && color === COLORS.brand && outline,
|
||||||
})
|
})
|
||||||
|
|
||||||
const tagName = !!href ? 'a' : !!to ? 'NavLink' : 'button'
|
const tagName = !!href ? 'a' : !!to ? 'NavLink' : 'button'
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
import IconButton from './icon_button';
|
import Button from './button';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unhide {domain}' },
|
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unhide {domain}' },
|
||||||
@ -30,7 +30,7 @@ class Domain extends PureComponent {
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div className='domain__buttons'>
|
<div className='domain__buttons'>
|
||||||
<IconButton
|
<Button
|
||||||
active
|
active
|
||||||
icon='unlock'
|
icon='unlock'
|
||||||
title={intl.formatMessage(messages.unblockDomain, {
|
title={intl.formatMessage(messages.unblockDomain, {
|
||||||
|
@ -14,9 +14,19 @@ export default class FloatingActionButton extends Component {
|
|||||||
render() {
|
render() {
|
||||||
const { onClick, message } = this.props;
|
const { onClick, message } = this.props;
|
||||||
|
|
||||||
|
// const shouldHideFAB = path => path.match(/^\/posts\/|^\/search|^\/getting-started/);
|
||||||
|
// const floatingActionButton = shouldHideFAB(this.context.router.history.location.pathname) ? null : <button key='floating-action-button' onClick={this.handleOpenComposeModal} className='floating-action-button' aria-label={intl.formatMessage(messages.publish)}></button>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button onClick={onClick} className='floating-action-button' aria-label={message}>
|
<Button
|
||||||
|
onClick={onClick}
|
||||||
|
color='white'
|
||||||
|
className={[_s.positionFixed, _s.z4, _s.bottom0, _s.right0].join(' ')}
|
||||||
|
title={message}
|
||||||
|
aria-label={message}
|
||||||
|
>
|
||||||
<Icon id='compose' />
|
<Icon id='compose' />
|
||||||
|
[...]
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,96 +0,0 @@
|
|||||||
import classNames from 'classnames';
|
|
||||||
import spring from 'react-motion/lib/spring';
|
|
||||||
import Motion from '../../features/ui/util/optional_motion';
|
|
||||||
import Icon from '../icon';
|
|
||||||
|
|
||||||
export default class IconButton extends PureComponent {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
className: PropTypes.string,
|
|
||||||
title: PropTypes.string.isRequired,
|
|
||||||
icon: PropTypes.string.isRequired,
|
|
||||||
onClick: PropTypes.func,
|
|
||||||
size: PropTypes.number,
|
|
||||||
active: PropTypes.bool,
|
|
||||||
pressed: PropTypes.bool,
|
|
||||||
expanded: PropTypes.bool,
|
|
||||||
style: PropTypes.object,
|
|
||||||
disabled: PropTypes.bool,
|
|
||||||
inverted: PropTypes.bool,
|
|
||||||
animate: PropTypes.bool,
|
|
||||||
overlay: PropTypes.bool,
|
|
||||||
tabIndex: PropTypes.string,
|
|
||||||
text: PropTypes.string,
|
|
||||||
width: PropTypes.string,
|
|
||||||
height: PropTypes.string,
|
|
||||||
};
|
|
||||||
|
|
||||||
static defaultProps = {
|
|
||||||
size: 18,
|
|
||||||
active: false,
|
|
||||||
disabled: false,
|
|
||||||
animate: false,
|
|
||||||
overlay: false,
|
|
||||||
tabIndex: '0',
|
|
||||||
};
|
|
||||||
|
|
||||||
handleClick = (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
if (!this.props.disabled) {
|
|
||||||
this.props.onClick(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const style = {
|
|
||||||
// fontSize: `${this.props.size}px`,
|
|
||||||
// width: `${this.props.size * 1.28571429}px`,
|
|
||||||
// height: `${this.props.size * 1.28571429}px`,
|
|
||||||
// lineHeight: `${this.props.size}px`,
|
|
||||||
...this.props.style,
|
|
||||||
};
|
|
||||||
|
|
||||||
const {
|
|
||||||
active,
|
|
||||||
animate,
|
|
||||||
className,
|
|
||||||
disabled,
|
|
||||||
expanded,
|
|
||||||
icon,
|
|
||||||
inverted,
|
|
||||||
overlay,
|
|
||||||
pressed,
|
|
||||||
tabIndex,
|
|
||||||
title,
|
|
||||||
text,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const classes = classNames(className, 'icon-button', {
|
|
||||||
active,
|
|
||||||
disabled,
|
|
||||||
inverted,
|
|
||||||
overlayed: overlay,
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
aria-label={title}
|
|
||||||
aria-pressed={pressed}
|
|
||||||
aria-expanded={expanded}
|
|
||||||
title={title}
|
|
||||||
className={classes}
|
|
||||||
onClick={this.handleClick}
|
|
||||||
style={style}
|
|
||||||
tabIndex={tabIndex}
|
|
||||||
disabled={disabled}
|
|
||||||
>
|
|
||||||
<Icon id={icon} fixedWidth aria-hidden='true' width={width} height={height} />
|
|
||||||
{!!text && text}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
export { default } from './icon_button'
|
|
@ -20,6 +20,8 @@ export default class Input extends PureComponent {
|
|||||||
small: PropTypes.bool,
|
small: PropTypes.bool,
|
||||||
readOnly: PropTypes.string,
|
readOnly: PropTypes.string,
|
||||||
inputRef: PropTypes.func,
|
inputRef: PropTypes.func,
|
||||||
|
id: PropTypes.string,
|
||||||
|
hideLabel: PropTypes.bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -36,20 +38,25 @@ export default class Input extends PureComponent {
|
|||||||
title,
|
title,
|
||||||
small,
|
small,
|
||||||
readOnly,
|
readOnly,
|
||||||
inputRef
|
inputRef,
|
||||||
|
id,
|
||||||
|
hideLabel
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
const inputClasses = cx({
|
const inputClasses = cx({
|
||||||
default: 1,
|
default: 1,
|
||||||
text: 1,
|
text: 1,
|
||||||
outlineNone: 1,
|
outlineNone: 1,
|
||||||
lineHeight125: 1,
|
lineHeight125: !small,
|
||||||
|
lineHeight1: small,
|
||||||
displayBlock: 1,
|
displayBlock: 1,
|
||||||
py10: 1,
|
py10: !small,
|
||||||
|
py5: small,
|
||||||
backgroundTransparent: !readOnly,
|
backgroundTransparent: !readOnly,
|
||||||
backgroundSubtle2: readOnly,
|
backgroundSubtle2: readOnly,
|
||||||
colorSecondary: readOnly,
|
colorSecondary: readOnly,
|
||||||
fontSize15PX: 1,
|
fontSize15PX: !small,
|
||||||
|
fontSize13PX: small,
|
||||||
flexGrow1: 1,
|
flexGrow1: 1,
|
||||||
circle: 1,
|
circle: 1,
|
||||||
px5: !!prependIcon,
|
px5: !!prependIcon,
|
||||||
@ -57,12 +64,19 @@ export default class Input extends PureComponent {
|
|||||||
pr15: !hasClear,
|
pr15: !hasClear,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const titleClasses = cx({
|
||||||
|
default: 1,
|
||||||
|
mb10: 1,
|
||||||
|
pl15: 1,
|
||||||
|
displayNone: hideLabel,
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
{
|
{
|
||||||
!!title &&
|
!!title &&
|
||||||
<div className={[_s.default, _s.mb10, _s.pl15].join(' ')}>
|
<div className={titleClasses}>
|
||||||
<Text size='small' weight='medium' color='secondary'>
|
<Text htmlFor={id} size='small' weight='medium' color='secondary' tagName='label'>
|
||||||
{title}
|
{title}
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
@ -74,6 +88,7 @@ export default class Input extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
<input
|
<input
|
||||||
|
id={id}
|
||||||
className={inputClasses}
|
className={inputClasses}
|
||||||
type='text'
|
type='text'
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
|
@ -7,7 +7,7 @@ import { decode } from 'blurhash';
|
|||||||
import { autoPlayGif, displayMedia } from '../../initial_state';
|
import { autoPlayGif, displayMedia } from '../../initial_state';
|
||||||
import { isIOS } from '../../utils/is_mobile';
|
import { isIOS } from '../../utils/is_mobile';
|
||||||
import { isPanoramic, isPortrait, isNonConformingRatio, minimumAspectRatio, maximumAspectRatio } from '../../utils/media_aspect_ratio';
|
import { isPanoramic, isPortrait, isNonConformingRatio, minimumAspectRatio, maximumAspectRatio } from '../../utils/media_aspect_ratio';
|
||||||
import IconButton from '../icon_button';
|
import Button from '../button';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
toggle_visible: { id: 'media_gallery.toggle_visible', defaultMessage: 'Toggle visibility' },
|
toggle_visible: { id: 'media_gallery.toggle_visible', defaultMessage: 'Toggle visibility' },
|
||||||
@ -503,7 +503,7 @@ class MediaGallery extends PureComponent {
|
|||||||
));
|
));
|
||||||
|
|
||||||
if (visible) {
|
if (visible) {
|
||||||
spoilerButton = <IconButton title={intl.formatMessage(messages.toggle_visible)} icon='eye-slash' overlay onClick={this.handleOpen} />;
|
spoilerButton = <Button title={intl.formatMessage(messages.toggle_visible)} icon='eye-slash' overlay onClick={this.handleOpen} />;
|
||||||
} else {
|
} else {
|
||||||
spoilerButton = (
|
spoilerButton = (
|
||||||
<button type='button' onClick={this.handleOpen} className='spoiler-button__overlay'>
|
<button type='button' onClick={this.handleOpen} className='spoiler-button__overlay'>
|
||||||
|
@ -5,7 +5,7 @@ import StatusContent from '../status_content';
|
|||||||
import Avatar from '../avatar';
|
import Avatar from '../avatar';
|
||||||
import RelativeTimestamp from '../relative_timestamp';
|
import RelativeTimestamp from '../relative_timestamp';
|
||||||
import DisplayName from '../display_name';
|
import DisplayName from '../display_name';
|
||||||
import IconButton from '../icon_button';
|
import Button from '../button';
|
||||||
|
|
||||||
export default class ActionsModal extends ImmutablePureComponent {
|
export default class ActionsModal extends ImmutablePureComponent {
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ export default class ActionsModal extends ImmutablePureComponent {
|
|||||||
className={classNames({ active })}
|
className={classNames({ active })}
|
||||||
data-method={isLogout ? 'delete' : null}
|
data-method={isLogout ? 'delete' : null}
|
||||||
>
|
>
|
||||||
{icon && <IconButton title={text} icon={icon} role='presentation' tabIndex='-1' inverted />}
|
{icon && <Button title={text} icon={icon} role='presentation' tabIndex='-1' inverted />}
|
||||||
<div>
|
<div>
|
||||||
<div className={classNames({ 'actions-modal__item-label': !!meta })}>{text}</div>
|
<div className={classNames({ 'actions-modal__item-label': !!meta })}>{text}</div>
|
||||||
<div>{meta}</div>
|
<div>{meta}</div>
|
||||||
|
@ -0,0 +1,86 @@
|
|||||||
|
import { defineMessages, injectIntl } from 'react-intl'
|
||||||
|
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||||
|
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||||
|
import { closeModal } from '../../actions/modal'
|
||||||
|
import { changeSetting, saveSettings } from '../../actions/settings'
|
||||||
|
import ModalLayout from './modal_layout'
|
||||||
|
import Button from '../button'
|
||||||
|
import SettingSwitch from '../setting_switch'
|
||||||
|
import Text from '../text'
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
title: { id: 'hashtag_timeline_settings', defaultMessage: 'Hashtag Timeline Settings' },
|
||||||
|
saveAndClose: { id: 'saveClose', defaultMessage: 'Save & Close' },
|
||||||
|
onlyMedia: { id: 'community.column_settings.media_only', defaultMessage: 'Media Only' },
|
||||||
|
showInSidebar: { id: 'show_in_sidebar', defaultMessage: 'Show in Sidebar' },
|
||||||
|
})
|
||||||
|
|
||||||
|
const mapStateToProps = state => ({
|
||||||
|
settings: state.getIn(['settings', 'community']),
|
||||||
|
})
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
onChange(key, checked) {
|
||||||
|
dispatch(changeSetting(['community', ...key], checked))
|
||||||
|
},
|
||||||
|
onSave() {
|
||||||
|
dispatch(saveSettings())
|
||||||
|
dispatch(closeModal())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default
|
||||||
|
@connect(mapStateToProps, mapDispatchToProps)
|
||||||
|
@injectIntl
|
||||||
|
class HashtagTimelineSettingsModal extends ImmutablePureComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
|
settings: ImmutablePropTypes.map.isRequired,
|
||||||
|
onChange: PropTypes.func.isRequired,
|
||||||
|
onSave: PropTypes.func.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSaveAndClose = () => {
|
||||||
|
this.props.onSave()
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { intl, settings, onChange } = this.props
|
||||||
|
|
||||||
|
// : todo :
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalLayout
|
||||||
|
width='320'
|
||||||
|
title={intl.formatMessage(messages.title)}
|
||||||
|
>
|
||||||
|
|
||||||
|
<div className={[_s.default, _s.pb10].join(' ')}>
|
||||||
|
<SettingSwitch
|
||||||
|
prefix='community_timeline'
|
||||||
|
settings={settings}
|
||||||
|
settingPath={['shows', 'inSidebar']}
|
||||||
|
onChange={onChange}
|
||||||
|
label={intl.formatMessage(messages.showInSidebar)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
centered
|
||||||
|
backgroundColor='brand'
|
||||||
|
color='white'
|
||||||
|
className={_s.justifyContentCenter}
|
||||||
|
onClick={this.handleSaveAndClose}
|
||||||
|
>
|
||||||
|
<Text color='inherit' weight='bold' align='center'>
|
||||||
|
{intl.formatMessage(messages.saveAndClose)}
|
||||||
|
</Text>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
</ModalLayout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
|||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import Video from '../../features/video';
|
import Video from '../../features/video';
|
||||||
import ExtendedVideoPlayer from '../extended_video_player';
|
import ExtendedVideoPlayer from '../extended_video_player';
|
||||||
import IconButton from '../icon_button';
|
import Button from '../button';
|
||||||
import ImageLoader from '../image_loader';
|
import ImageLoader from '../image_loader';
|
||||||
import Icon from '../icon';
|
import Icon from '../icon';
|
||||||
|
|
||||||
@ -221,7 +221,7 @@ class MediaModal extends ImmutablePureComponent {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={navigationClassName}>
|
<div className={navigationClassName}>
|
||||||
<IconButton className='media-modal__close' title={intl.formatMessage(messages.close)} icon='times' onClick={onClose} size={40} />
|
<Button className='media-modal__close' title={intl.formatMessage(messages.close)} icon='times' onClick={onClose} size={40} />
|
||||||
|
|
||||||
{leftNav}
|
{leftNav}
|
||||||
{rightNav}
|
{rightNav}
|
||||||
|
@ -23,6 +23,7 @@ import ConfirmationModal from './confirmation_modal'
|
|||||||
import GroupCreateModal from './group_create_modal'
|
import GroupCreateModal from './group_create_modal'
|
||||||
import GroupDeleteModal from './group_delete_modal'
|
import GroupDeleteModal from './group_delete_modal'
|
||||||
import GroupEditorModal from './group_editor_modal'
|
import GroupEditorModal from './group_editor_modal'
|
||||||
|
import HashtagTimelineSettingsModal from './hashtag_timeline_settings_modal'
|
||||||
import HomeTimelineSettingsModal from './home_timeline_settings_modal'
|
import HomeTimelineSettingsModal from './home_timeline_settings_modal'
|
||||||
import HotkeysModal from './hotkeys_modal'
|
import HotkeysModal from './hotkeys_modal'
|
||||||
import ListCreateModal from './list_create_modal'
|
import ListCreateModal from './list_create_modal'
|
||||||
@ -48,6 +49,7 @@ const MODAL_COMPONENTS = {
|
|||||||
GROUP_CREATE: () => Promise.resolve({ default: GroupCreateModal }),
|
GROUP_CREATE: () => Promise.resolve({ default: GroupCreateModal }),
|
||||||
GROUP_DELETE: () => Promise.resolve({ default: GroupDeleteModal }),
|
GROUP_DELETE: () => Promise.resolve({ default: GroupDeleteModal }),
|
||||||
GROUP_EDITOR: () => Promise.resolve({ default: GroupEditorModal }),
|
GROUP_EDITOR: () => Promise.resolve({ default: GroupEditorModal }),
|
||||||
|
HASHTAG_TIMELINE_SETTINGS: () => Promise.resolve({ default: HashtagTimelineSettingsModal }),
|
||||||
HOME_TIMELINE_SETTINGS: () => Promise.resolve({ default: HomeTimelineSettingsModal }),
|
HOME_TIMELINE_SETTINGS: () => Promise.resolve({ default: HomeTimelineSettingsModal }),
|
||||||
HOTKEYS: () => Promise.resolve({ default: HotkeysModal }),
|
HOTKEYS: () => Promise.resolve({ default: HotkeysModal }),
|
||||||
LIST_CREATE: () => Promise.resolve({ default: ListCreateModal }),
|
LIST_CREATE: () => Promise.resolve({ default: ListCreateModal }),
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
import { defineMessages, injectIntl } from 'react-intl'
|
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
|
||||||
import Button from '../button'
|
|
||||||
import Text from '../text'
|
|
||||||
import ModalLayout from './modal_layout'
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
title: { id: 'promo.gab_pro', defaultMessage: 'Upgrade to GabPRO' },
|
|
||||||
text: { id: 'pro_upgrade_modal.text', defaultMessage: 'Gab is fully funded by people like you. Please consider supporting us on our mission to defend free expression online for all people.' },
|
|
||||||
benefits: { id: 'pro_upgrade_modal.benefits', defaultMessage: 'Here are just some of the benefits that thousands of GabPRO members receive:' },
|
|
||||||
})
|
|
||||||
|
|
||||||
export default
|
|
||||||
@injectIntl
|
|
||||||
class HomeTimelineSettingsModal extends ImmutablePureComponent {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
intl: PropTypes.object.isRequired,
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { intl } = this.props
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Text>
|
|
||||||
{intl.formatMessage(messages.text)}
|
|
||||||
</Text>
|
|
||||||
<Text>
|
|
||||||
{intl.formatMessage(messages.benefits)}
|
|
||||||
</Text>
|
|
||||||
|
|
||||||
<div className={[_s.default, _s.my10].join(' ')}>
|
|
||||||
<Text>• Schedule Posts</Text>
|
|
||||||
<Text>• Get Verified</Text>
|
|
||||||
<Text>• Create Groups</Text>
|
|
||||||
<Text>• Larger Video and Image Uploads</Text>
|
|
||||||
<Text>• Receive the PRO Badge</Text>
|
|
||||||
<Text>• Remove in-feed promotions</Text>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
centered
|
|
||||||
backgroundColor='brand'
|
|
||||||
color='white'
|
|
||||||
icon='pro'
|
|
||||||
href='https://pro.gab.com'
|
|
||||||
className={_s.justifyContentCenter}
|
|
||||||
iconClassName={[_s.mr5, _s.fillColorWhite].join(' ')}
|
|
||||||
>
|
|
||||||
<Text color='inherit' weight='bold' align='center'>
|
|
||||||
{intl.formatMessage(messages.title)}
|
|
||||||
</Text>
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,7 +2,7 @@ import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'
|
|||||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||||
import StatusRevisionListContainer from '../../containers/status_revision_list_container'
|
import StatusRevisionListContainer from '../../containers/status_revision_list_container'
|
||||||
import IconButton from '../icon_button'
|
import Button from '../button'
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
close: { id: 'lightbox.close', defaultMessage: 'Close' },
|
close: { id: 'lightbox.close', defaultMessage: 'Close' },
|
||||||
@ -28,7 +28,7 @@ class StatusRevisionModal extends ImmutablePureComponent {
|
|||||||
<h3 className='status-revisions__header__title'>
|
<h3 className='status-revisions__header__title'>
|
||||||
<FormattedMessage id='status_revisions.heading' defaultMessage='Revision History' />
|
<FormattedMessage id='status_revisions.heading' defaultMessage='Revision History' />
|
||||||
</h3>
|
</h3>
|
||||||
<IconButton className='status-revisions__close' title={intl.formatMessage(messages.close)} icon='times' onClick={onClose} size={20} />
|
<Button className='status-revisions__close' title={intl.formatMessage(messages.close)} icon='times' onClick={onClose} size={20} />
|
||||||
</div>
|
</div>
|
||||||
<div className='status-revisions__content'>
|
<div className='status-revisions__content'>
|
||||||
<StatusRevisionListContainer id={status.get('id')} />
|
<StatusRevisionListContainer id={status.get('id')} />
|
||||||
|
@ -12,7 +12,11 @@ export default class ProgressPanel extends PureComponent {
|
|||||||
subtitle="We are 100% funded by you"
|
subtitle="We are 100% funded by you"
|
||||||
hasBackground
|
hasBackground
|
||||||
>
|
>
|
||||||
<ProgressBar progress={monthlyExpensesComplete}/>
|
<ProgressBar
|
||||||
|
progress={monthlyExpensesComplete}
|
||||||
|
title={`${Math.min(parseFloat(monthlyExpensesComplete), 100)}% covered this month`}
|
||||||
|
href='https://shop.dissenter.com/category/donations'
|
||||||
|
/>
|
||||||
</PanelLayout>
|
</PanelLayout>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,81 +1,149 @@
|
|||||||
|
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||||
|
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||||
|
import { defineMessages, injectIntl } from 'react-intl'
|
||||||
import PopoverLayout from './popover_layout'
|
import PopoverLayout from './popover_layout'
|
||||||
import List from '../list'
|
import List from '../list'
|
||||||
|
|
||||||
export default class StatusOptionsPopover extends PureComponent {
|
const messages = defineMessages({
|
||||||
_makeMenu = (publicStatus) => {
|
delete: { id: 'status.delete', defaultMessage: 'Delete' },
|
||||||
// const { status, intl: { formatMessage }, withDismiss, withGroupAdmin } = this.props
|
edit: { id: 'status.edit', defaultMessage: 'Edit' },
|
||||||
// const mutingConversation = status.get('muted')
|
mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' },
|
||||||
|
mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' },
|
||||||
|
block: { id: 'account.block', defaultMessage: 'Block @{name}' },
|
||||||
|
reply: { id: 'status.reply', defaultMessage: 'Reply' },
|
||||||
|
comment: { id: 'status.comment', defaultMessage: 'Comment' },
|
||||||
|
more: { id: 'status.more', defaultMessage: 'More' },
|
||||||
|
share: { id: 'status.share', defaultMessage: 'Share' },
|
||||||
|
replyAll: { id: 'status.replyAll', defaultMessage: 'Reply to thread' },
|
||||||
|
repost: { id: 'repost', defaultMessage: 'Repost' },
|
||||||
|
quote: { id: 'status.quote', defaultMessage: 'Quote' },
|
||||||
|
repost_private: { id: 'status.repost_private', defaultMessage: 'Repost to original audience' },
|
||||||
|
cancel_repost_private: { id: 'status.cancel_repost_private', defaultMessage: 'Un-repost' },
|
||||||
|
cannot_repost: { id: 'status.cannot_repost', defaultMessage: 'This post cannot be reposted' },
|
||||||
|
cannot_quote: { id: 'status.cannot_quote', defaultMessage: 'This post cannot be quoted' },
|
||||||
|
like: { id: 'status.like', defaultMessage: 'Like' },
|
||||||
|
open: { id: 'status.open', defaultMessage: 'Expand this status' },
|
||||||
|
report: { id: 'status.report', defaultMessage: 'Report @{name}' },
|
||||||
|
muteConversation: { id: 'status.mute_conversation', defaultMessage: 'Mute conversation' },
|
||||||
|
unmuteConversation: { id: 'status.unmute_conversation', defaultMessage: 'Unmute conversation' },
|
||||||
|
pin: { id: 'status.pin', defaultMessage: 'Pin on profile' },
|
||||||
|
unpin: { id: 'status.unpin', defaultMessage: 'Unpin from profile' },
|
||||||
|
embed: { id: 'status.embed', defaultMessage: 'Embed' },
|
||||||
|
admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
|
||||||
|
admin_status: { id: 'status.admin_status', defaultMessage: 'Open this status in the moderation interface' },
|
||||||
|
group_remove_account: { id: 'status.remove_account_from_group', defaultMessage: 'Remove account from group' },
|
||||||
|
group_remove_post: { id: 'status.remove_post_from_group', defaultMessage: 'Remove status from group' },
|
||||||
|
})
|
||||||
|
|
||||||
|
export default
|
||||||
|
@injectIntl
|
||||||
|
class StatusOptionsPopover extends ImmutablePureComponent {
|
||||||
|
static propTypes = {
|
||||||
|
status: ImmutablePropTypes.map.isRequired,
|
||||||
|
onOpenUnauthorizedModal: PropTypes.func.isRequired,
|
||||||
|
onOpenStatusSharePopover: PropTypes.func.isRequired,
|
||||||
|
onReply: PropTypes.func,
|
||||||
|
onQuote: PropTypes.func,
|
||||||
|
onFavorite: PropTypes.func,
|
||||||
|
onRepost: PropTypes.func,
|
||||||
|
onDelete: PropTypes.func,
|
||||||
|
onMention: PropTypes.func,
|
||||||
|
onMute: PropTypes.func,
|
||||||
|
onBlock: PropTypes.func,
|
||||||
|
onReport: PropTypes.func,
|
||||||
|
onEmbed: PropTypes.func,
|
||||||
|
onMuteConversation: PropTypes.func,
|
||||||
|
onPin: PropTypes.func,
|
||||||
|
withDismiss: PropTypes.bool,
|
||||||
|
withGroupAdmin: PropTypes.bool,
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
getItems = () => {
|
||||||
|
const { status, intl, withDismiss, withGroupAdmin } = this.props
|
||||||
|
const mutingConversation = status.get('muted')
|
||||||
|
|
||||||
let menu = [];
|
let menu = [];
|
||||||
|
|
||||||
// menu.push({ text: formatMessage(messages.open), action: this.handleOpen });
|
menu.push({
|
||||||
|
icon: 'circle',
|
||||||
|
hideArrow: true,
|
||||||
|
title: formatMessage(messages.open),
|
||||||
|
onClick: this.handleOpen
|
||||||
|
});
|
||||||
|
|
||||||
// if (publicStatus) {
|
if (publicStatus) {
|
||||||
// menu.push({ text: formatMessage(messages.copy), action: this.handleCopy });
|
menu.push({
|
||||||
// menu.push({ text: formatMessage(messages.embed), action: this.handleEmbed });
|
icon: 'circle',
|
||||||
// }
|
hideArrow: true,
|
||||||
|
title: formatMessage(messages.copy),
|
||||||
|
onClick: this.handleCopy,
|
||||||
|
})
|
||||||
|
menu.push({
|
||||||
|
icon: 'circle',
|
||||||
|
hideArrow: true,
|
||||||
|
title: formatMessage(messages.embed),
|
||||||
|
onClick: this.handleEmbed,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// if (!me) return menu
|
if (!me) return menu
|
||||||
|
|
||||||
// menu.push(null);
|
if (status.getIn(['account', 'id']) === me || withDismiss) {
|
||||||
|
menu.push({
|
||||||
|
icon: 'circle',
|
||||||
|
hideArrow: true,
|
||||||
|
title: formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation),
|
||||||
|
onClick: this.handleConversationMuteClick,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// if (status.getIn(['account', 'id']) === me || withDismiss) {
|
if (status.getIn(['account', 'id']) === me) {
|
||||||
// menu.push({ text: formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation), action: this.handleConversationMuteClick });
|
if (publicStatus) {
|
||||||
// menu.push(null);
|
menu.push({
|
||||||
// }
|
icon: 'circle',
|
||||||
|
hideArrow: true,
|
||||||
|
title: formatMessage(status.get('pinned') ? messages.unpin : messages.pin),
|
||||||
|
onClick: this.handlePinClick,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
if (status.get('visibility') === 'private') {
|
||||||
|
menu.push({
|
||||||
|
title: formatMessage(status.get('reblogged') ? messages.cancel_repost_private : messages.repost_private),
|
||||||
|
onClick: this.handleRepostClick
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
menu.push({ text: formatMessage(messages.delete), action: this.handleDeleteClick });
|
||||||
|
menu.push({ text: formatMessage(messages.edit), action: this.handleEditClick });
|
||||||
|
} else {
|
||||||
|
menu.push({ text: formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
|
||||||
|
menu.push({ text: formatMessage(messages.mute, { name: status.getIn(['account', 'username']) }), action: this.handleMuteClick });
|
||||||
|
menu.push({ text: formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick });
|
||||||
|
menu.push({ text: formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport });
|
||||||
|
|
||||||
// if (status.getIn(['account', 'id']) === me) {
|
if (isStaff) {
|
||||||
// if (publicStatus) {
|
menu.push({ text: formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }), href: `/admin/accounts/${status.getIn(['account', 'id'])}` });
|
||||||
// menu.push({ text: formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick });
|
menu.push({ text: formatMessage(messages.admin_status), href: `/admin/accounts/${status.getIn(['account', 'id'])}/statuses/${status.get('id')}` });
|
||||||
// } else {
|
}
|
||||||
// if (status.get('visibility') === 'private') {
|
|
||||||
// menu.push({ text: formatMessage(status.get('reblogged') ? messages.cancel_repost_private : messages.repost_private), action: this.handleRepostClick });
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// menu.push({ text: formatMessage(messages.delete), action: this.handleDeleteClick });
|
|
||||||
// menu.push({ text: formatMessage(messages.edit), action: this.handleEditClick });
|
|
||||||
// } else {
|
|
||||||
// menu.push({ text: formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
|
|
||||||
// menu.push(null);
|
|
||||||
// menu.push({ text: formatMessage(messages.mute, { name: status.getIn(['account', 'username']) }), action: this.handleMuteClick });
|
|
||||||
// menu.push({ text: formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick });
|
|
||||||
// menu.push({ text: formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport });
|
|
||||||
|
|
||||||
// if (isStaff) {
|
if (withGroupAdmin) {
|
||||||
// menu.push(null);
|
menu.push({ text: formatMessage(messages.group_remove_account), action: this.handleGroupRemoveAccount });
|
||||||
// menu.push({ text: formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }), href: `/admin/accounts/${status.getIn(['account', 'id'])}` });
|
menu.push({ text: formatMessage(messages.group_remove_post), action: this.handleGroupRemovePost });
|
||||||
// menu.push({ text: formatMessage(messages.admin_status), href: `/admin/accounts/${status.getIn(['account', 'id'])}/statuses/${status.get('id')}` });
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
// if (withGroupAdmin) {
|
|
||||||
// menu.push(null);
|
|
||||||
// menu.push({ text: formatMessage(messages.group_remove_account), action: this.handleGroupRemoveAccount });
|
|
||||||
// menu.push({ text: formatMessage(messages.group_remove_post), action: this.handleGroupRemovePost });
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const items = this.getItems()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PopoverLayout className={_s.width240PX}>
|
<PopoverLayout className={_s.width240PX}>
|
||||||
<List
|
<List
|
||||||
scrollKey='profile_options'
|
scrollKey='profile_options'
|
||||||
items={[
|
items={items}
|
||||||
{
|
|
||||||
title: 'Help',
|
|
||||||
href: 'https://help.gab.com',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Settings',
|
|
||||||
href: '/settings',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Log Out',
|
|
||||||
href: '/auth/log_out',
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
small
|
small
|
||||||
/>
|
/>
|
||||||
</PopoverLayout>
|
</PopoverLayout>
|
||||||
|
@ -3,12 +3,12 @@ import spring from 'react-motion/lib/spring'
|
|||||||
import detectPassiveEvents from 'detect-passive-events'
|
import detectPassiveEvents from 'detect-passive-events'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import Overlay from 'react-overlays/lib/Overlay'
|
import Overlay from 'react-overlays/lib/Overlay'
|
||||||
import { changeComposeVisibility } from '../../../actions/compose'
|
import { changeComposeVisibility } from '../../actions/compose'
|
||||||
import { openModal, closeModal } from '../../../actions/modal'
|
import { openModal, closeModal } from '../../actions/modal'
|
||||||
import { isUserTouching } from '../../../utils/is_mobile'
|
import { isUserTouching } from '../../utils/is_mobile'
|
||||||
import Motion from '../../ui/util/optional_motion'
|
import Motion from '../../features/ui/util/optional_motion'
|
||||||
import Icon from '../../../components/icon'
|
import Icon from '../icon'
|
||||||
import ComposeExtraButton from './compose_extra_button'
|
import ComposeExtraButton from '../../features/compose/components/compose_extra_button'
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
public_short: { id: 'privacy.public.short', defaultMessage: 'Public' },
|
public_short: { id: 'privacy.public.short', defaultMessage: 'Public' },
|
||||||
@ -246,25 +246,13 @@ class PrivacyDropdown extends PureComponent {
|
|||||||
const valueOption = this.options.find(item => item.value === value)
|
const valueOption = this.options.find(item => item.value === value)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames('privacy-dropdown', placement, { active: open })} onKeyDown={this.handleKeyDown}>
|
<PrivacyDropdownMenu
|
||||||
<div className={classNames('privacy-dropdown__value', { active: this.options.indexOf(valueOption) === 0 })}>
|
items={this.options}
|
||||||
<ComposeExtraButton
|
value={value}
|
||||||
icon={valueOption.icon}
|
onClose={this.handleClose}
|
||||||
title={intl.formatMessage(messages.visibility)}
|
onChange={this.handleChange}
|
||||||
onClick={this.handleToggle}
|
placement={placement}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
|
|
||||||
<Overlay show={open} placement={placement} target={this}>
|
|
||||||
<PrivacyDropdownMenu
|
|
||||||
items={this.options}
|
|
||||||
value={value}
|
|
||||||
onClose={this.handleClose}
|
|
||||||
onChange={this.handleChange}
|
|
||||||
placement={placement}
|
|
||||||
/>
|
|
||||||
</Overlay>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -105,14 +105,11 @@ class ProfileHeader extends ImmutablePureComponent {
|
|||||||
this.props.onFollow(this.props.account)
|
this.props.onFollow(this.props.account)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUnrequest = () => {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
handleBlock = () => {
|
handleBlock = () => {
|
||||||
// this.props.onBlock(this.props.account)
|
this.props.onBlock(this.props.account)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// : todo :
|
||||||
makeInfo() {
|
makeInfo() {
|
||||||
const { account, intl } = this.props;
|
const { account, intl } = this.props;
|
||||||
|
|
||||||
@ -135,28 +132,6 @@ class ProfileHeader extends ImmutablePureComponent {
|
|||||||
return info;
|
return info;
|
||||||
};
|
};
|
||||||
|
|
||||||
getActionBtn() {
|
|
||||||
const { account, intl } = this.props;
|
|
||||||
|
|
||||||
let actionBtn = null;
|
|
||||||
|
|
||||||
if (!account || !me) return actionBtn;
|
|
||||||
|
|
||||||
if (me !== account.get('id')) {
|
|
||||||
if (!account.get('relationship')) { // Wait until the relationship is loaded
|
|
||||||
//
|
|
||||||
} else if (account.getIn(['relationship', 'requested'])) {
|
|
||||||
actionBtn = <Button className='logo-button' text={intl.formatMessage(messages.requested)} onClick={this.props.onFollow} />;
|
|
||||||
} else if (!account.getIn(['relationship', 'blocking'])) {
|
|
||||||
actionBtn = <Button disabled={account.getIn(['relationship', 'blocked_by'])} className={classNames('logo-button', { 'button--destructive': account.getIn(['relationship', 'following']) })} text={intl.formatMessage(account.getIn(['relationship', 'following']) ? messages.unfollow : messages.follow)} onClick={this.props.onFollow} />;
|
|
||||||
} else if (account.getIn(['relationship', 'blocking'])) {
|
|
||||||
actionBtn = <Button className='logo-button' text={intl.formatMessage(messages.unblock, { name: account.get('username') })} onClick={this.props.onBlock} />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return actionBtn
|
|
||||||
}
|
|
||||||
|
|
||||||
setOpenMoreNodeRef = (n) => {
|
setOpenMoreNodeRef = (n) => {
|
||||||
this.openMoreNode = n
|
this.openMoreNode = n
|
||||||
}
|
}
|
||||||
@ -195,37 +170,78 @@ class ProfileHeader extends ImmutablePureComponent {
|
|||||||
|
|
||||||
const avatarSize = headerMissing ? '75' : '150'
|
const avatarSize = headerMissing ? '75' : '150'
|
||||||
|
|
||||||
let buttonText;
|
let buttonText = ''
|
||||||
let buttonOptions;
|
let buttonOptions = {}
|
||||||
|
|
||||||
if (!account) {
|
if (!account) {
|
||||||
|
console.log("no account")
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (account.get('id') !== me && account.get('relationship', null) !== null) {
|
if (!account.get('relationship')) {
|
||||||
const following = account.getIn(['relationship', 'following'])
|
console.log("no relationship")
|
||||||
const requested = account.getIn(['relationship', 'requested'])
|
// Wait until the relationship is loaded
|
||||||
const blocking = account.getIn(['relationship', 'blocking'])
|
} else if (account.getIn(['relationship', 'requested'])) {
|
||||||
|
buttonText = intl.formatMessage(messages.requested)
|
||||||
if (requested || blocking) {
|
buttonOptions = {
|
||||||
buttonText = intl.formatMessage(requested ? messages.requested : messages.unblock)
|
narrow: true,
|
||||||
buttonOptions = {
|
onClick: this.handleFollow,
|
||||||
narrow: true,
|
color: 'primary',
|
||||||
onClick: requested ? this.handleUnrequest : this.handleBlock,
|
backgroundColor: 'tertiary',
|
||||||
color: 'primary',
|
|
||||||
backgroundColor: 'tertiary',
|
|
||||||
}
|
|
||||||
} else if (!account.get('moved') || following) {
|
|
||||||
buttonOptions = {
|
|
||||||
narrow: true,
|
|
||||||
outline: !following,
|
|
||||||
color: !following ? 'brand' : 'white',
|
|
||||||
backgroundColor: !following ? 'none' : 'brand',
|
|
||||||
onClick: this.handleFollow,
|
|
||||||
}
|
|
||||||
buttonText = intl.formatMessage(following ? messages.unfollow : messages.follow)
|
|
||||||
}
|
}
|
||||||
|
} else if (!account.getIn(['relationship', 'blocking'])) {
|
||||||
|
const isFollowing = account.getIn(['relationship', 'following'])
|
||||||
|
const isBlockedBy = account.getIn(['relationship', 'blocked_by'])
|
||||||
|
buttonText = intl.formatMessage(isFollowing ? messages.unfollow : messages.follow)
|
||||||
|
buttonOptions = {
|
||||||
|
narrow: true,
|
||||||
|
onClick: this.handleFollow,
|
||||||
|
color: 'primary',
|
||||||
|
backgroundColor: 'tertiary',
|
||||||
|
disabled: isBlockedBy,
|
||||||
|
}
|
||||||
|
} else if (account.getIn(['relationship', 'blocking'])) {
|
||||||
|
buttonText = intl.formatMessage(messages.unblock)
|
||||||
|
buttonOptions = {
|
||||||
|
narrow: true,
|
||||||
|
onClick: this.handleBlock,
|
||||||
|
color: 'primary',
|
||||||
|
backgroundColor: 'tertiary',
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log("no nothin")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if (account.get('id') !== me && account.get('relationship', null) !== null) {
|
||||||
|
// const following = account.getIn(['relationship', 'following'])
|
||||||
|
// const requested = account.getIn(['relationship', 'requested'])
|
||||||
|
// const blocking = account.getIn(['relationship', 'blocking'])
|
||||||
|
|
||||||
|
// if (requested || blocking) {
|
||||||
|
// buttonText = intl.formatMessage(requested ? messages.requested : messages.unblock)
|
||||||
|
// buttonOptions = {
|
||||||
|
// narrow: true,
|
||||||
|
// onClick: requested ? this.handleFollow : this.handleBlock,
|
||||||
|
// color: 'primary',
|
||||||
|
// backgroundColor: 'tertiary',
|
||||||
|
// }
|
||||||
|
// } else if (!account.get('moved') || following) {
|
||||||
|
// buttonOptions = {
|
||||||
|
// narrow: true,
|
||||||
|
// outline: !following,
|
||||||
|
// color: !following ? 'brand' : 'white',
|
||||||
|
// backgroundColor: !following ? 'none' : 'brand',
|
||||||
|
// onClick: this.handleFollow,
|
||||||
|
// }
|
||||||
|
// buttonText = intl.formatMessage(following ? messages.unfollow : messages.follow)
|
||||||
|
// } else {
|
||||||
|
// console.log("SHOW ELSE")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("buttonOptions:", buttonText, buttonOptions)
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={[_s.default, _s.z1, _s.width100PC].join(' ')}>
|
<div className={[_s.default, _s.z1, _s.width100PC].join(' ')}>
|
||||||
|
|
||||||
@ -287,7 +303,7 @@ class ProfileHeader extends ImmutablePureComponent {
|
|||||||
icon='ellipsis'
|
icon='ellipsis'
|
||||||
iconWidth='18px'
|
iconWidth='18px'
|
||||||
iconHeight='18px'
|
iconHeight='18px'
|
||||||
iconClassName={_s.fillColorBrand}
|
iconClassName={_s.inheritFill}
|
||||||
color='brand'
|
color='brand'
|
||||||
backgroundColor='none'
|
backgroundColor='none'
|
||||||
className={[_s.justifyContentCenter, _s.alignItemsCenter, _s.mr10, _s.px10].join(' ')}
|
className={[_s.justifyContentCenter, _s.alignItemsCenter, _s.mr10, _s.px10].join(' ')}
|
||||||
@ -302,7 +318,7 @@ class ProfileHeader extends ImmutablePureComponent {
|
|||||||
icon='chat'
|
icon='chat'
|
||||||
iconWidth='18px'
|
iconWidth='18px'
|
||||||
iconHeight='18px'
|
iconHeight='18px'
|
||||||
iconClassName={_s.fillColorBrand}
|
iconClassName={_s.inheritFill}
|
||||||
color='brand'
|
color='brand'
|
||||||
backgroundColor='none'
|
backgroundColor='none'
|
||||||
className={[_s.justifyContentCenter, _s.alignItemsCenter, _s.mr10, _s.px10].join(' ')}
|
className={[_s.justifyContentCenter, _s.alignItemsCenter, _s.mr10, _s.px10].join(' ')}
|
||||||
|
@ -1,28 +1,59 @@
|
|||||||
|
import classNames from 'classnames/bind'
|
||||||
|
import Button from './button'
|
||||||
import Text from './text'
|
import Text from './text'
|
||||||
|
|
||||||
|
const cx = classNames.bind(_s)
|
||||||
|
|
||||||
export default class ProgressBar extends PureComponent {
|
export default class ProgressBar extends PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
progress: PropTypes.number,
|
progress: PropTypes.number,
|
||||||
|
small: PropTypes.bool,
|
||||||
|
title: PropTypes.string,
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { progress } = this.props
|
const {
|
||||||
|
progress,
|
||||||
|
small,
|
||||||
|
title,
|
||||||
|
href
|
||||||
|
} = this.props
|
||||||
|
|
||||||
const completed = Math.min(parseFloat(progress), 100)
|
const completed = Math.min(parseFloat(progress), 100)
|
||||||
const style = {
|
const style = {
|
||||||
width: `${completed}%`,
|
width: `${completed}%`,
|
||||||
}
|
}
|
||||||
const title = `${completed}% covered this month`
|
|
||||||
|
const containerOptions = {
|
||||||
|
href,
|
||||||
|
className: cx({
|
||||||
|
default: 1,
|
||||||
|
backgroundPanel: !small,
|
||||||
|
backgroundSubtle2: small,
|
||||||
|
noUnderline: 1,
|
||||||
|
circle: 1,
|
||||||
|
overflowHidden: 1,
|
||||||
|
cursorPointer: 1,
|
||||||
|
height22PX: !small,
|
||||||
|
height4PX: small,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<a href='https://shop.dissenter.com/category/donations' className={[_s.default, _s.backgroundPanel, _s.noUnderline, _s.circle, _s.overflowHidden, _s.height22PX, _s.cursorPointer].join(' ')}>
|
<Button
|
||||||
<div className={[_s.default, _s.backgroundColorBrandDark, _s.circle, _s.height22PX].join(' ')} style={style} />
|
noClasses
|
||||||
<div className={[_s.default, _s.positionAbsolute, _s.width100PC, _s.height22PX, _s.alignItemsCenter, _s.justifyContentCenter].join(' ')}>
|
{...containerOptions}
|
||||||
<Text size='small' weight='bold' color='white'>
|
>
|
||||||
{title}
|
<div className={[_s.default, _s.backgroundColorBrandDark, _s.circle, _s.height100PC].join(' ')} style={style} />
|
||||||
</Text>
|
<div className={[_s.default, _s.positionAbsolute, _s.width100PC, _s.height100PC, _s.alignItemsCenter, _s.justifyContentCenter].join(' ')}>
|
||||||
|
{
|
||||||
|
!!title &&
|
||||||
|
<Text size='small' weight='bold' color='white'>
|
||||||
|
{title}
|
||||||
|
</Text>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</Button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -149,7 +149,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||||||
const favoriteCount = status.get('favourites_count') // : todo :
|
const favoriteCount = status.get('favourites_count') // : todo :
|
||||||
|
|
||||||
const shareButton = ('share' in navigator) && status.get('visibility') === 'public' && (
|
const shareButton = ('share' in navigator) && status.get('visibility') === 'public' && (
|
||||||
<IconButton className='status-action-bar-button' title={formatMessage(messages.share)} icon='share-alt' onClick={this.handleShareClick} />
|
<Button className='status-action-bar-button' title={formatMessage(messages.share)} icon='share-alt' onClick={this.handleShareClick} />
|
||||||
)
|
)
|
||||||
|
|
||||||
const hasInteractions = favoriteCount > 0 || replyCount > 0 || repostCount > 0
|
const hasInteractions = favoriteCount > 0 || replyCount > 0 || repostCount > 0
|
||||||
@ -189,7 +189,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||||||
{
|
{
|
||||||
favoriteCount > 0 &&
|
favoriteCount > 0 &&
|
||||||
<button className={interactionBtnClasses}>
|
<button className={interactionBtnClasses}>
|
||||||
<Text color='secondary'>
|
<Text color='secondary' size='small'>
|
||||||
{favoriteCount}
|
{favoriteCount}
|
||||||
Likes
|
Likes
|
||||||
</Text>
|
</Text>
|
||||||
@ -198,7 +198,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||||||
{
|
{
|
||||||
replyCount > 0 &&
|
replyCount > 0 &&
|
||||||
<button className={interactionBtnClasses}>
|
<button className={interactionBtnClasses}>
|
||||||
<Text color='secondary'>
|
<Text color='secondary' size='small'>
|
||||||
{replyCount}
|
{replyCount}
|
||||||
Comments
|
Comments
|
||||||
</Text>
|
</Text>
|
||||||
@ -207,7 +207,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||||||
{
|
{
|
||||||
repostCount > 0 &&
|
repostCount > 0 &&
|
||||||
<button className={interactionBtnClasses}>
|
<button className={interactionBtnClasses}>
|
||||||
<Text color='secondary'>
|
<Text color='secondary' size='small'>
|
||||||
{repostCount}
|
{repostCount}
|
||||||
Reposts
|
Reposts
|
||||||
</Text>
|
</Text>
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import { Fragment } from 'react';
|
import { Fragment } from 'react'
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||||
import { FormattedMessage, injectIntl, defineMessages } from 'react-intl';
|
import { FormattedMessage, injectIntl, defineMessages } from 'react-intl'
|
||||||
import classNames from 'classnames/bind'
|
import classNames from 'classnames/bind'
|
||||||
import { isRtl } from '../../utils/rtl';
|
import { isRtl } from '../../utils/rtl'
|
||||||
import Button from '../button'
|
import Button from '../button'
|
||||||
import Icon from '../icon';
|
import Icon from '../icon'
|
||||||
|
import Text from '../text'
|
||||||
|
|
||||||
const MAX_HEIGHT = 200;
|
const MAX_HEIGHT = 200
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
showMore: { id: 'status.show_more', defaultMessage: 'Show more' },
|
showMore: { id: 'status.show_more', defaultMessage: 'Show more' },
|
||||||
@ -23,7 +24,7 @@ class StatusContent extends ImmutablePureComponent {
|
|||||||
|
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
router: PropTypes.object,
|
router: PropTypes.object,
|
||||||
};
|
}
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
status: ImmutablePropTypes.map.isRequired,
|
status: ImmutablePropTypes.map.isRequired,
|
||||||
@ -34,36 +35,36 @@ class StatusContent extends ImmutablePureComponent {
|
|||||||
collapsable: PropTypes.bool,
|
collapsable: PropTypes.bool,
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
isComment: PropTypes.bool,
|
isComment: PropTypes.bool,
|
||||||
};
|
}
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
hidden: true,
|
hidden: true,
|
||||||
collapsed: null, // `collapsed: null` indicates that an element doesn't need collapsing, while `true` or `false` indicates that it does (and is/isn't).
|
collapsed: null, // `collapsed: null` indicates that an element doesn't need collapsing, while `true` or `false` indicates that it does (and is/isn't).
|
||||||
};
|
}
|
||||||
|
|
||||||
_updateStatusLinks () {
|
_updateStatusLinks() {
|
||||||
const node = this.node;
|
const node = this.node
|
||||||
|
|
||||||
if (!node) return;
|
if (!node) return
|
||||||
|
|
||||||
const links = node.querySelectorAll('a');
|
const links = node.querySelectorAll('a')
|
||||||
|
|
||||||
for (var i = 0; i < links.length; ++i) {
|
for (var i = 0; i < links.length; ++i) {
|
||||||
let link = links[i];
|
let link = links[i]
|
||||||
if (link.classList.contains('status-link')) {
|
if (link.classList.contains('status-link')) {
|
||||||
continue;
|
continue
|
||||||
}
|
}
|
||||||
link.classList.add('status-link');
|
link.classList.add('status-link')
|
||||||
|
|
||||||
let mention = this.props.status.get('mentions').find(item => link.href === `${item.get('url')}`);
|
let mention = this.props.status.get('mentions').find(item => link.href === `${item.get('url')}`)
|
||||||
|
|
||||||
if (mention) {
|
if (mention) {
|
||||||
link.addEventListener('click', this.onMentionClick.bind(this, mention), false);
|
link.addEventListener('click', this.onMentionClick.bind(this, mention), false)
|
||||||
link.setAttribute('title', mention.get('acct'));
|
link.setAttribute('title', mention.get('acct'))
|
||||||
} else if (link.textContent[0] === '#' || (link.previousSibling && link.previousSibling.textContent && link.previousSibling.textContent[link.previousSibling.textContent.length - 1] === '#')) {
|
} else if (link.textContent[0] === '#' || (link.previousSibling && link.previousSibling.textContent && link.previousSibling.textContent[link.previousSibling.textContent.length - 1] === '#')) {
|
||||||
link.addEventListener('click', this.onHashtagClick.bind(this, link.text), false);
|
link.addEventListener('click', this.onHashtagClick.bind(this, link.text), false)
|
||||||
} else {
|
} else {
|
||||||
link.setAttribute('title', link.href);
|
link.setAttribute('title', link.href)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,59 +75,59 @@ class StatusContent extends ImmutablePureComponent {
|
|||||||
&& node.clientHeight > MAX_HEIGHT
|
&& node.clientHeight > MAX_HEIGHT
|
||||||
&& this.props.status.get('spoiler_text').length === 0
|
&& this.props.status.get('spoiler_text').length === 0
|
||||||
) {
|
) {
|
||||||
this.setState({ collapsed: true });
|
this.setState({ collapsed: true })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount() {
|
||||||
this._updateStatusLinks();
|
this._updateStatusLinks()
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate () {
|
componentDidUpdate() {
|
||||||
this._updateStatusLinks();
|
this._updateStatusLinks()
|
||||||
}
|
}
|
||||||
|
|
||||||
onMentionClick = (mention, e) => {
|
onMentionClick = (mention, e) => {
|
||||||
if (this.context.router && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
if (this.context.router && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
||||||
e.preventDefault();
|
e.preventDefault()
|
||||||
this.context.router.history.push(`/${mention.get('acct')}`);
|
this.context.router.history.push(`/${mention.get('acct')}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onHashtagClick = (hashtag, e) => {
|
onHashtagClick = (hashtag, e) => {
|
||||||
hashtag = hashtag.replace(/^#/, '').toLowerCase();
|
hashtag = hashtag.replace(/^#/, '').toLowerCase()
|
||||||
|
|
||||||
if (this.context.router && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
if (this.context.router && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
||||||
e.preventDefault();
|
e.preventDefault()
|
||||||
this.context.router.history.push(`/tags/${hashtag}`);
|
this.context.router.history.push(`/tags/${hashtag}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMouseDown = (e) => {
|
handleMouseDown = (e) => {
|
||||||
this.startXY = [e.clientX, e.clientY];
|
this.startXY = [e.clientX, e.clientY]
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMouseUp = (e) => {
|
handleMouseUp = (e) => {
|
||||||
if (!this.startXY) return;
|
if (!this.startXY) return
|
||||||
|
|
||||||
const [ startX, startY ] = this.startXY;
|
const [startX, startY] = this.startXY
|
||||||
const [ deltaX, deltaY ] = [Math.abs(e.clientX - startX), Math.abs(e.clientY - startY)];
|
const [deltaX, deltaY] = [Math.abs(e.clientX - startX), Math.abs(e.clientY - startY)]
|
||||||
|
|
||||||
if (e.target.localName === 'button' ||
|
if (e.target.localName === 'button' ||
|
||||||
e.target.localName === 'a' ||
|
e.target.localName === 'a' ||
|
||||||
(e.target.parentNode && (e.target.parentNode.localName === 'button' || e.target.parentNode.localName === 'a'))) {
|
(e.target.parentNode && (e.target.parentNode.localName === 'button' || e.target.parentNode.localName === 'a'))) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deltaX + deltaY < 5 && e.button === 0 && this.props.onClick) {
|
if (deltaX + deltaY < 5 && e.button === 0 && this.props.onClick) {
|
||||||
this.props.onClick();
|
this.props.onClick()
|
||||||
}
|
}
|
||||||
|
|
||||||
this.startXY = null;
|
this.startXY = null
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSpoilerClick = (e) => {
|
handleSpoilerClick = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault()
|
||||||
|
|
||||||
if (this.props.onExpandedToggle) {
|
if (this.props.onExpandedToggle) {
|
||||||
// The parent manages the state
|
// The parent manages the state
|
||||||
@ -137,77 +138,125 @@ class StatusContent extends ImmutablePureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleReadMore = (e) => {
|
handleReadMore = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault()
|
||||||
this.setState({ collapsed: false });
|
this.setState({ collapsed: false })
|
||||||
}
|
}
|
||||||
|
|
||||||
setRef = (c) => {
|
setRef = (c) => {
|
||||||
this.node = c;
|
this.node = c
|
||||||
}
|
}
|
||||||
|
|
||||||
getHtmlContent = () => {
|
getHtmlContent = () => {
|
||||||
const { status, reblogContent } = this.props;
|
const { status, reblogContent } = this.props
|
||||||
|
|
||||||
const properContent = status.get('contentHtml');
|
const properContent = status.get('contentHtml')
|
||||||
|
|
||||||
return reblogContent
|
return reblogContent
|
||||||
? `${reblogContent} <div class='status__quote'>${properContent}</div>`
|
? `${reblogContent} <div class='status__quote'>${properContent}</div>`
|
||||||
: properContent;
|
: properContent
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
const { status, intl, isComment } = this.props
|
const { status, intl, isComment } = this.props
|
||||||
const { collapsed } = this.state
|
const { collapsed } = this.state
|
||||||
|
|
||||||
console.log("status content", status.get('content'))
|
if (status.get('content').length === 0) return null
|
||||||
|
|
||||||
if (status.get('content').length === 0) return null;
|
const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden
|
||||||
|
|
||||||
const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden;
|
const content = { __html: this.getHtmlContent() }
|
||||||
|
const spoilerContent = { __html: status.get('spoilerHtml') }
|
||||||
const content = { __html: this.getHtmlContent() };
|
const directionStyle = {
|
||||||
const spoilerContent = { __html: status.get('spoilerHtml') };
|
direction: isRtl(status.get('search_index')) ? 'rtl' : 'ltr',
|
||||||
const directionStyle = { direction: 'ltr' };
|
|
||||||
// const classNames = '';
|
|
||||||
// classnames('status__content', {
|
|
||||||
// 'status__content--with-action': this.props.onClick && this.context.router,
|
|
||||||
// 'status__content--with-spoiler': status.get('spoiler_text').length > 0,
|
|
||||||
// 'status__content--collapsed': this.state.collapsed === true,
|
|
||||||
// // _s.px15, _s.mb15
|
|
||||||
// });
|
|
||||||
|
|
||||||
if (isRtl(status.get('search_index'))) {
|
|
||||||
directionStyle.direction = 'rtl';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status.get('spoiler_text').length > 0) {
|
if (status.get('spoiler_text').length > 0) {
|
||||||
let mentionsPlaceholder = '';
|
let mentionsPlaceholder = null
|
||||||
|
|
||||||
const mentionLinks = status.get('mentions').map(item => (
|
const mentionLinks = status.get('mentions').map(item => (
|
||||||
<Button to={`/${item.get('acct')}`} href={`/${item.get('acct')}`} key={item.get('id')} className='mention'>
|
<Button
|
||||||
@<span>{item.get('username')}</span>
|
text
|
||||||
|
backgroundColor='none'
|
||||||
|
to={`/${item.get('acct')}`}
|
||||||
|
href={`/${item.get('acct')}`}
|
||||||
|
key={item.get('id')}
|
||||||
|
className={['mention', _s.mr5, _s.mb5].join(' ')}
|
||||||
|
>
|
||||||
|
@{item.get('username')}
|
||||||
</Button>
|
</Button>
|
||||||
)).reduce((aggregate, item) => [...aggregate, item, ' '], []);
|
)).reduce((aggregate, item) => [...aggregate, item, ' '], [])
|
||||||
|
|
||||||
const toggleText = intl.formatMessage(hidden ? messages.showMore : messages.showLess);
|
|
||||||
|
|
||||||
if (hidden) {
|
if (hidden) {
|
||||||
mentionsPlaceholder = <div>{mentionLinks}</div>;
|
mentionsPlaceholder = (
|
||||||
|
<div className={[_s.statusContent, _s.default, _s.alignItemsStart, _s.flexRow, _s.flexWrap].join(' ')}>
|
||||||
|
{mentionLinks}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const toggleText = intl.formatMessage(hidden ? messages.showMore : messages.showLess)
|
||||||
|
|
||||||
|
const spoilerContainerClasses = cx({
|
||||||
|
default: 1,
|
||||||
|
py10: 1,
|
||||||
|
borderBottom1PX: !hidden,
|
||||||
|
borderColorSecondary: !hidden,
|
||||||
|
mb10: !hidden,
|
||||||
|
})
|
||||||
|
|
||||||
|
const statusContentClasses = cx({
|
||||||
|
statusContent: 1,
|
||||||
|
displayNone: hidden,
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={[].join(' ')} ref={this.setRef} tabIndex='0' style={directionStyle} onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp}>
|
<div
|
||||||
<p style={{ marginBottom: hidden && status.get('mentions').isEmpty() ? '0px' : null }}>
|
className={[].join(' ')}
|
||||||
<span dangerouslySetInnerHTML={spoilerContent} lang={status.get('language')} />
|
ref={this.setRef}
|
||||||
{' '}
|
tabIndex='0'
|
||||||
<button tabIndex='0' className={`status__content__spoiler-link ${hidden ? 'status__content__spoiler-link--show-more' : 'status__content__spoiler-link--show-less'}`} onClick={this.handleSpoilerClick}>{toggleText}</button>
|
className={[_s.px15, _s.statusContent].join(' ')}
|
||||||
</p>
|
style={directionStyle}
|
||||||
|
onMouseDown={this.handleMouseDown}
|
||||||
|
onMouseUp={this.handleMouseUp}
|
||||||
|
>
|
||||||
|
|
||||||
|
<div className={spoilerContainerClasses}>
|
||||||
|
<div className={[_s.default, _s.flexRow, _s.mr5].join(' ')}>
|
||||||
|
<Icon id='warning' height='14px' width='14px' className={[_s.fillColorBlack, _s.mt2, _s.mr5].join(' ')}/>
|
||||||
|
<div
|
||||||
|
className={_s.statusContent}
|
||||||
|
dangerouslySetInnerHTML={spoilerContent}
|
||||||
|
lang={status.get('language')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={[_s.default, _s.mt10, _s.alignItemsStart].join(' ')}>
|
||||||
|
<Button
|
||||||
|
narrow
|
||||||
|
radiusSmall
|
||||||
|
backgroundColor='tertiary'
|
||||||
|
color='primary'
|
||||||
|
tabIndex='0'
|
||||||
|
onClick={this.handleSpoilerClick}
|
||||||
|
>
|
||||||
|
<Text size='small' color='inherit'>
|
||||||
|
{toggleText}
|
||||||
|
</Text>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{mentionsPlaceholder}
|
{mentionsPlaceholder}
|
||||||
|
|
||||||
<div tabIndex={!hidden ? 0 : null} className={`status__content__text ${!hidden ? 'status__content__text--visible' : ''}`} style={directionStyle} dangerouslySetInnerHTML={content} lang={status.get('language')} />
|
<div
|
||||||
|
tabIndex={!hidden ? 0 : null}
|
||||||
|
className={statusContentClasses}
|
||||||
|
style={directionStyle}
|
||||||
|
dangerouslySetInnerHTML={content}
|
||||||
|
lang={status.get('language')}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
} else if (this.props.onClick) {
|
} else if (this.props.onClick) {
|
||||||
const hasMarginBottom = !!status.get('card') || !!status.get('poll') || status.get('media_attachments').size > 0
|
const hasMarginBottom = !!status.get('card') || !!status.get('poll') || status.get('media_attachments').size > 0
|
||||||
|
|
||||||
@ -256,7 +305,7 @@ class StatusContent extends ImmutablePureComponent {
|
|||||||
dangerouslySetInnerHTML={content}
|
dangerouslySetInnerHTML={content}
|
||||||
lang={status.get('language')}
|
lang={status.get('language')}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -12,24 +12,30 @@ import Icon from './icon'
|
|||||||
import Button from './button'
|
import Button from './button'
|
||||||
import Avatar from './avatar'
|
import Avatar from './avatar'
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
onOpenStatusOptionsPopover(targetRef, status) {
|
||||||
|
dispatch(openPopover('STATUS_OPTIONS', {
|
||||||
|
targetRef,
|
||||||
|
status,
|
||||||
|
position: 'top',
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
export default
|
export default
|
||||||
@connect(null, null)
|
@connect(null, mapDispatchToProps)
|
||||||
class StatusHeader extends ImmutablePureComponent {
|
class StatusHeader extends ImmutablePureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
status: ImmutablePropTypes.map,
|
status: ImmutablePropTypes.map,
|
||||||
|
onOpenStatusOptionsPopover: PropTypes.func.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
handleStatusOptionsClick() {
|
handleOpenStatusOptionsPopover = () => {
|
||||||
console.log("handleStatusOptionsClick:", this.props)
|
this.props.onOpenStatusOptionsPopover(this.statusOptionsButton, this.props.status)
|
||||||
this.props.dispatch(openPopover('STATUS_OPTIONS', {
|
|
||||||
targetRef: this.statusOptionsButton,
|
|
||||||
position: 'top',
|
|
||||||
status: this.props.status,
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleOpenStatusEdits() {
|
handleOpenStatusEdits = () => {
|
||||||
// : todo :
|
// : todo :
|
||||||
this.props.dispatch(openPopover('REPOST', {
|
this.props.dispatch(openPopover('REPOST', {
|
||||||
targetRef: this.statusOptionsButton,
|
targetRef: this.statusOptionsButton,
|
||||||
@ -150,7 +156,7 @@ class StatusHeader extends ImmutablePureComponent {
|
|||||||
iconHeight='20px'
|
iconHeight='20px'
|
||||||
iconClassName={_s.fillColorSecondary}
|
iconClassName={_s.fillColorSecondary}
|
||||||
className={_s.marginLeftAuto}
|
className={_s.marginLeftAuto}
|
||||||
onClick={this.handleStatusOptionsClick}
|
onClick={this.handleOpenStatusOptionsPopover}
|
||||||
buttonRef={this.setStatusOptionsButton}
|
buttonRef={this.setStatusOptionsButton}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -54,7 +54,7 @@ export default class Switch extends PureComponent {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={[_s.default, _s.flexRow, _s.py5, _s.alignItemsCenter].join(' ')}>
|
<div className={[_s.default, _s.flexRow, _s.py5, _s.alignItemsCenter].join(' ')}>
|
||||||
<Text {...labelProps}>
|
<Text {...labelProps} className={_s.mr10}>
|
||||||
{label}
|
{label}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import { NavLink, withRouter } from 'react-router-dom'
|
import { withRouter } from 'react-router-dom'
|
||||||
import classNames from 'classnames/bind'
|
import classNames from 'classnames/bind'
|
||||||
|
import Button from './button'
|
||||||
|
import Icon from './icon'
|
||||||
import Text from './text'
|
import Text from './text'
|
||||||
|
|
||||||
const cx = classNames.bind(_s)
|
const cx = classNames.bind(_s)
|
||||||
@ -10,8 +12,11 @@ class TabBarItem extends PureComponent {
|
|||||||
static propTypes = {
|
static propTypes = {
|
||||||
location: PropTypes.object.isRequired,
|
location: PropTypes.object.isRequired,
|
||||||
title: PropTypes.string,
|
title: PropTypes.string,
|
||||||
|
onClick: PropTypes.func,
|
||||||
|
icon: PropTypes.string,
|
||||||
to: PropTypes.string,
|
to: PropTypes.string,
|
||||||
large: PropTypes.bool,
|
large: PropTypes.bool,
|
||||||
|
active: PropTypes.bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
@ -31,7 +36,15 @@ class TabBarItem extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { title, to, location, large } = this.props
|
const {
|
||||||
|
title,
|
||||||
|
to,
|
||||||
|
onClick,
|
||||||
|
location,
|
||||||
|
large,
|
||||||
|
icon,
|
||||||
|
// active
|
||||||
|
} = this.props
|
||||||
const { active } = this.state
|
const { active } = this.state
|
||||||
|
|
||||||
const isCurrent = active === -1 ? to === location.pathname : active
|
const isCurrent = active === -1 ? to === location.pathname : active
|
||||||
@ -46,6 +59,7 @@ class TabBarItem extends PureComponent {
|
|||||||
justifyContentCenter: 1,
|
justifyContentCenter: 1,
|
||||||
borderBottom2PX: 1,
|
borderBottom2PX: 1,
|
||||||
py5: 1,
|
py5: 1,
|
||||||
|
backgroundTransparent: 1,
|
||||||
borderColorTransparent: !isCurrent,
|
borderColorTransparent: !isCurrent,
|
||||||
borderColorBrand: isCurrent,
|
borderColorBrand: isCurrent,
|
||||||
mr5: large,
|
mr5: large,
|
||||||
@ -68,14 +82,31 @@ class TabBarItem extends PureComponent {
|
|||||||
weight: isCurrent ? 'bold' : large ? 'medium' : 'normal',
|
weight: isCurrent ? 'bold' : large ? 'medium' : 'normal',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const iconOptions = {
|
||||||
|
id: icon,
|
||||||
|
width: !!large ? 20 : 14,
|
||||||
|
height: !!large ? 20 : 14,
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NavLink to={to} className={containerClasses}>
|
<Button
|
||||||
|
onClick={onClick}
|
||||||
|
to={to}
|
||||||
|
className={containerClasses}
|
||||||
|
noClasses
|
||||||
|
>
|
||||||
<span className={textParentClasses}>
|
<span className={textParentClasses}>
|
||||||
|
{ !!title &&
|
||||||
<Text {...textOptions}>
|
<Text {...textOptions}>
|
||||||
{title}
|
{title}
|
||||||
</Text>
|
</Text>
|
||||||
|
}
|
||||||
|
|
||||||
|
{ !!icon &&
|
||||||
|
<Icon {...iconOptions} />
|
||||||
|
}
|
||||||
</span>
|
</span>
|
||||||
</NavLink>
|
</Button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -42,6 +42,7 @@ export default class Text extends PureComponent {
|
|||||||
weight: PropTypes.oneOf(Object.keys(WEIGHTS)),
|
weight: PropTypes.oneOf(Object.keys(WEIGHTS)),
|
||||||
align: PropTypes.oneOf(Object.keys(ALIGNMENTS)),
|
align: PropTypes.oneOf(Object.keys(ALIGNMENTS)),
|
||||||
underline: PropTypes.bool,
|
underline: PropTypes.bool,
|
||||||
|
htmlFor: PropTypes.string,
|
||||||
}
|
}
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
@ -60,7 +61,8 @@ export default class Text extends PureComponent {
|
|||||||
size,
|
size,
|
||||||
weight,
|
weight,
|
||||||
underline,
|
underline,
|
||||||
align
|
align,
|
||||||
|
htmlFor
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
const classes = cx(className, {
|
const classes = cx(className, {
|
||||||
@ -93,6 +95,7 @@ export default class Text extends PureComponent {
|
|||||||
return React.createElement(
|
return React.createElement(
|
||||||
tagName,
|
tagName,
|
||||||
{
|
{
|
||||||
|
htmlFor,
|
||||||
className: classes,
|
className: classes,
|
||||||
},
|
},
|
||||||
children,
|
children,
|
||||||
|
@ -1,62 +0,0 @@
|
|||||||
import { defineMessages, injectIntl } from 'react-intl';
|
|
||||||
import { openModal } from '../../../../actions/modal';
|
|
||||||
import { meUsername } from '../../../../initial_state';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
profile: { id: 'account.profile', defaultMessage: 'Profile' },
|
|
||||||
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
|
|
||||||
follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
|
|
||||||
blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
|
|
||||||
domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Hidden domains' },
|
|
||||||
mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
|
|
||||||
filters: { id: 'navigation_bar.filters', defaultMessage: 'Muted words' },
|
|
||||||
logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
|
|
||||||
keyboard_shortcuts: { id: 'navigation_bar.keyboard_shortcuts', defaultMessage: 'Hotkeys' },
|
|
||||||
});
|
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
|
||||||
onOpenHotkeys() {
|
|
||||||
dispatch(openModal('HOTKEYS'));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
class ActionBar extends PureComponent {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
intl: PropTypes.object.isRequired,
|
|
||||||
size: PropTypes.number,
|
|
||||||
};
|
|
||||||
|
|
||||||
handleHotkeyClick = () => {
|
|
||||||
this.props.onOpenHotkeys();
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { intl } = this.props;
|
|
||||||
const size = this.props.size || 16;
|
|
||||||
|
|
||||||
let menu = [];
|
|
||||||
|
|
||||||
menu.push({ text: intl.formatMessage(messages.profile), to: `/${meUsername}` });
|
|
||||||
menu.push(null);
|
|
||||||
menu.push({ text: intl.formatMessage(messages.follow_requests), to: '/follow_requests' });
|
|
||||||
menu.push({ text: intl.formatMessage(messages.mutes), to: '/mutes' });
|
|
||||||
menu.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' });
|
|
||||||
menu.push({ text: intl.formatMessage(messages.domain_blocks), to: '/domain_blocks' });
|
|
||||||
menu.push({ text: intl.formatMessage(messages.filters), href: '/filters' });
|
|
||||||
menu.push(null);
|
|
||||||
menu.push({ text: intl.formatMessage(messages.keyboard_shortcuts), action: this.handleHotkeyClick });
|
|
||||||
menu.push({ text: intl.formatMessage(messages.preferences), href: '/settings/preferences' });
|
|
||||||
menu.push({ text: intl.formatMessage(messages.logout), href: '/auth/sign_out', isLogout: true });
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{'marginTop':'-6px'}}>
|
|
||||||
<div>
|
|
||||||
{ /* <DropdownMenuContainer items={menu} icon='chevron-down' size={size} direction='right' /> */ }
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default injectIntl(connect(null, mapDispatchToProps)(ActionBar));
|
|
@ -1 +0,0 @@
|
|||||||
export { default } from './action_bar'
|
|
@ -1,16 +0,0 @@
|
|||||||
.character-counter {
|
|
||||||
cursor: default;
|
|
||||||
font-family: $font-sans-serif, sans-serif;
|
|
||||||
color: $gab-secondary-text;
|
|
||||||
|
|
||||||
@include text-sizing(14px, 600);
|
|
||||||
|
|
||||||
&--over {
|
|
||||||
color: $warning-red;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__wrapper {
|
|
||||||
align-self: center;
|
|
||||||
margin-right: 4px;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
export { default } from './character_counter'
|
|
@ -1,5 +1,5 @@
|
|||||||
import classNames from 'classnames/bind'
|
import classNames from 'classnames/bind'
|
||||||
import Icon from '../../../components/icon'
|
import Button from '../../../components/button'
|
||||||
|
|
||||||
const cx = classNames.bind(_s)
|
const cx = classNames.bind(_s)
|
||||||
|
|
||||||
@ -13,18 +13,6 @@ export default class ComposeExtraButton extends PureComponent {
|
|||||||
active: PropTypes.bool,
|
active: PropTypes.bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
state = {
|
|
||||||
hovering: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
handleOnMouseEnter = () => {
|
|
||||||
this.setState({ hovering: true })
|
|
||||||
}
|
|
||||||
|
|
||||||
handleOnMouseLeave = () => {
|
|
||||||
this.setState({ hovering: false })
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
title,
|
title,
|
||||||
@ -35,7 +23,6 @@ export default class ComposeExtraButton extends PureComponent {
|
|||||||
small,
|
small,
|
||||||
active
|
active
|
||||||
} = this.props
|
} = this.props
|
||||||
const { hovering } = this.state
|
|
||||||
|
|
||||||
const containerClasses = cx({
|
const containerClasses = cx({
|
||||||
default: 1,
|
default: 1,
|
||||||
@ -44,13 +31,6 @@ export default class ComposeExtraButton extends PureComponent {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const btnClasses = cx({
|
const btnClasses = cx({
|
||||||
default: 1,
|
|
||||||
circle: 1,
|
|
||||||
flexRow: 1,
|
|
||||||
cursorPointer: 1,
|
|
||||||
outlineNone: 1,
|
|
||||||
backgroundSubtle: !hovering && !active,
|
|
||||||
backgroundSubtle2: hovering && !active,
|
|
||||||
backgroundColorBrandLight: active,
|
backgroundColorBrandLight: active,
|
||||||
py10: !small,
|
py10: !small,
|
||||||
px10: !small,
|
px10: !small,
|
||||||
@ -58,18 +38,6 @@ export default class ComposeExtraButton extends PureComponent {
|
|||||||
px5: small,
|
px5: small,
|
||||||
})
|
})
|
||||||
|
|
||||||
const titleClasses = cx({
|
|
||||||
default: 1,
|
|
||||||
ml5: 1,
|
|
||||||
text: 1,
|
|
||||||
lineHeight15: 1,
|
|
||||||
fontSize12PX: 1,
|
|
||||||
fontWeightMedium: 1,
|
|
||||||
colorSecondary: !active,
|
|
||||||
colorWhite: active,
|
|
||||||
displayNone: !hovering,
|
|
||||||
})
|
|
||||||
|
|
||||||
const iconClasses = cx({
|
const iconClasses = cx({
|
||||||
fillColorSecondary: !active,
|
fillColorSecondary: !active,
|
||||||
fillColorWhite: active,
|
fillColorWhite: active,
|
||||||
@ -78,23 +46,18 @@ export default class ComposeExtraButton extends PureComponent {
|
|||||||
const iconSize = !!small ? '12px' : '18px'
|
const iconSize = !!small ? '12px' : '18px'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={containerClasses}>
|
<div className={containerClasses} data-tip={title}>
|
||||||
<button
|
<Button
|
||||||
className={btnClasses}
|
className={btnClasses}
|
||||||
title={title}
|
title={title}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
onMouseEnter={() => this.handleOnMouseEnter()}
|
backgroundColor='secondary'
|
||||||
onMouseLeave={() => this.handleOnMouseLeave()}
|
iconClassName={iconClasses}
|
||||||
>
|
icon={icon}
|
||||||
<Icon id={icon} width={iconSize} height={iconSize} className={iconClasses} />
|
iconWidth={iconSize}
|
||||||
{
|
iconHeight={iconSize}
|
||||||
(!small && !!title) &&
|
/>
|
||||||
<span className={titleClasses}>
|
|
||||||
{title}
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
</button>
|
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -10,7 +10,7 @@ import AutosuggestTextbox from '../../../../components/autosuggest_textbox';
|
|||||||
import PollButton from '../../components/poll_button';
|
import PollButton from '../../components/poll_button';
|
||||||
import UploadButton from '../../components/upload_button';
|
import UploadButton from '../../components/upload_button';
|
||||||
import SpoilerButton from '../../components/spoiler_button';
|
import SpoilerButton from '../../components/spoiler_button';
|
||||||
import PrivacyDropdown from '../../components/privacy_dropdown';
|
import PostPrivacyButton from '../../../../components/post_privacy_button';
|
||||||
import EmojiPickerButton from '../../components/emoji_picker_button'
|
import EmojiPickerButton from '../../components/emoji_picker_button'
|
||||||
import EmojiPickerDropdown from '../../containers/emoji_picker_dropdown_container';
|
import EmojiPickerDropdown from '../../containers/emoji_picker_dropdown_container';
|
||||||
import PollFormContainer from '../../containers/poll_form_container';
|
import PollFormContainer from '../../containers/poll_form_container';
|
||||||
@ -230,7 +230,8 @@ class ComposeForm extends ImmutablePureComponent {
|
|||||||
quoteOfId,
|
quoteOfId,
|
||||||
edit,
|
edit,
|
||||||
scheduledAt,
|
scheduledAt,
|
||||||
spoiler
|
spoiler,
|
||||||
|
replyToId
|
||||||
} = this.props
|
} = this.props
|
||||||
const disabled = this.props.isSubmitting;
|
const disabled = this.props.isSubmitting;
|
||||||
const text = [this.props.spoilerText, countableText(this.props.text)].join('');
|
const text = [this.props.spoilerText, countableText(this.props.text)].join('');
|
||||||
@ -239,7 +240,7 @@ class ComposeForm extends ImmutablePureComponent {
|
|||||||
|
|
||||||
const containerClasses = cx({
|
const containerClasses = cx({
|
||||||
default: 1,
|
default: 1,
|
||||||
flexGrow1: 1,
|
flexNormal: 1,
|
||||||
flexRow: shouldCondense,
|
flexRow: shouldCondense,
|
||||||
radiusSmall: shouldCondense,
|
radiusSmall: shouldCondense,
|
||||||
backgroundSubtle: shouldCondense,
|
backgroundSubtle: shouldCondense,
|
||||||
@ -326,10 +327,10 @@ class ComposeForm extends ImmutablePureComponent {
|
|||||||
>
|
>
|
||||||
|
|
||||||
<div className='compose-form__modifiers'>
|
<div className='compose-form__modifiers'>
|
||||||
<UploadForm />
|
<UploadForm replyToId={replyToId} />
|
||||||
{
|
{
|
||||||
!edit &&
|
!edit &&
|
||||||
<PollFormContainer />
|
<PollFormContainer replyToId={replyToId} />
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -345,7 +346,7 @@ class ComposeForm extends ImmutablePureComponent {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
!shouldCondense &&
|
!shouldCondense &&
|
||||||
<PrivacyDropdown />
|
<PostPrivacyButton />
|
||||||
}
|
}
|
||||||
<SpoilerButton small={shouldCondense} />
|
<SpoilerButton small={shouldCondense} />
|
||||||
<SchedulePostDropdown small={shouldCondense} position={isModalOpen ? 'top' : undefined} />
|
<SchedulePostDropdown small={shouldCondense} position={isModalOpen ? 'top' : undefined} />
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
import ActionBar from '../action_bar';
|
|
||||||
import Avatar from '../../../../components/avatar';
|
import Avatar from '../../../../components/avatar';
|
||||||
import Button from '../../../../components/button'
|
import Button from '../../../../components/button'
|
||||||
import IconButton from '../../../../components/icon_button';
|
|
||||||
import { me } from '../../../../initial_state';
|
import { me } from '../../../../initial_state';
|
||||||
|
|
||||||
const mapStateToProps = state => {
|
const mapStateToProps = state => {
|
||||||
@ -43,8 +41,7 @@ class NavigationBar extends ImmutablePureComponent {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='navigation-bar__actions'>
|
<div className='navigation-bar__actions'>
|
||||||
<IconButton className='close' title='' icon='close' onClick={this.props.onClose} />
|
<Button className='close' title='' icon='close' onClick={this.props.onClose} />
|
||||||
<ActionBar account={account} />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1,65 @@
|
|||||||
|
import { defineMessages, injectIntl } from 'react-intl'
|
||||||
|
import { addPoll, removePoll } from '../../../actions/compose'
|
||||||
|
import ComposeExtraButton from './compose_extra_button'
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
add_poll: { id: 'poll_button.add_poll', defaultMessage: 'Add poll' },
|
||||||
|
title: { id: 'poll_button.title', defaultMessage: 'Poll' },
|
||||||
|
remove_poll: { id: 'poll_button.remove_poll', defaultMessage: 'Remove poll' },
|
||||||
|
})
|
||||||
|
|
||||||
|
const mapStateToProps = state => ({
|
||||||
|
unavailable: state.getIn(['compose', 'is_uploading']) || (state.getIn(['compose', 'media_attachments']).size > 0),
|
||||||
|
active: state.getIn(['compose', 'poll']) !== null,
|
||||||
|
})
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
|
||||||
|
onClick() {
|
||||||
|
dispatch((_, getState) => {
|
||||||
|
if (getState().getIn(['compose', 'poll'])) {
|
||||||
|
dispatch(removePoll())
|
||||||
|
} else {
|
||||||
|
dispatch(addPoll())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
export default
|
||||||
|
@connect(mapStateToProps, mapDispatchToProps)
|
||||||
|
@injectIntl
|
||||||
|
class PollButton extends PureComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
disabled: PropTypes.bool,
|
||||||
|
unavailable: PropTypes.bool,
|
||||||
|
active: PropTypes.bool,
|
||||||
|
onClick: PropTypes.func.isRequired,
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
|
small: PropTypes.bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleClick = () => {
|
||||||
|
this.props.onClick()
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { intl, active, unavailable, disabled, small } = this.props
|
||||||
|
|
||||||
|
if (unavailable) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ComposeExtraButton
|
||||||
|
title={intl.formatMessage(active ? messages.remove_poll : messages.title)}
|
||||||
|
disabled={disabled}
|
||||||
|
onClick={this.handleClick}
|
||||||
|
icon='poll'
|
||||||
|
small={small}
|
||||||
|
active={active}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -2,6 +2,8 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
|||||||
import DisplayName from '../../../components/display_name';
|
import DisplayName from '../../../components/display_name';
|
||||||
import StatusContent from '../../../components/status_content';
|
import StatusContent from '../../../components/status_content';
|
||||||
|
|
||||||
|
// : todo : do we need this? make work inside of status/status content
|
||||||
|
|
||||||
export default class QuotedStatusPreview extends PureComponent {
|
export default class QuotedStatusPreview extends PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
status: ImmutablePropTypes.map,
|
status: ImmutablePropTypes.map,
|
||||||
|
@ -3,7 +3,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
|||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
import { NavLink } from 'react-router-dom';
|
import { NavLink } from 'react-router-dom';
|
||||||
import Avatar from '../../../../components/avatar';
|
import Avatar from '../../../../components/avatar';
|
||||||
import IconButton from '../../../../components/icon_button';
|
import Button from '../../../../components/button';
|
||||||
import DisplayName from '../../../../components/display_name';
|
import DisplayName from '../../../../components/display_name';
|
||||||
import { isRtl } from '../../../../utils/rtl';
|
import { isRtl } from '../../../../utils/rtl';
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ class ReplyIndicator extends ImmutablePureComponent {
|
|||||||
<div className='reply-indicator'>
|
<div className='reply-indicator'>
|
||||||
<div className='reply-indicator__header'>
|
<div className='reply-indicator__header'>
|
||||||
<div className='reply-indicator__cancel'>
|
<div className='reply-indicator__cancel'>
|
||||||
<IconButton title={intl.formatMessage(messages.cancel)} icon='times' onClick={this.handleClick} inverted />
|
<Button title={intl.formatMessage(messages.cancel)} icon='times' onClick={this.handleClick} inverted />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<NavLink to={`/${status.getIn(['account', 'acct'])}`} className='reply-indicator__display-name'>
|
<NavLink to={`/${status.getIn(['account', 'acct'])}`} className='reply-indicator__display-name'>
|
||||||
|
@ -0,0 +1,51 @@
|
|||||||
|
import { injectIntl, defineMessages } from 'react-intl'
|
||||||
|
import { changeComposeSensitivity } from '../../../actions/compose'
|
||||||
|
import Switch from '../../../components/switch'
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
markAsSensitive: { id: 'compose_form.sensitive.hide', defaultMessage: 'Mark media as sensitive' },
|
||||||
|
})
|
||||||
|
|
||||||
|
const mapStateToProps = state => ({
|
||||||
|
active: state.getIn(['compose', 'sensitive']),
|
||||||
|
disabled: state.getIn(['compose', 'spoiler']),
|
||||||
|
})
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
|
||||||
|
onClick () {
|
||||||
|
dispatch(changeComposeSensitivity())
|
||||||
|
},
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
export default
|
||||||
|
@injectIntl
|
||||||
|
@connect(mapStateToProps, mapDispatchToProps)
|
||||||
|
class SensitiveMediaButton extends PureComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
active: PropTypes.bool,
|
||||||
|
disabled: PropTypes.bool,
|
||||||
|
onClick: PropTypes.func.isRequired,
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { active, disabled, onClick, intl } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={[_s.default, _s.alignItemsStart, _s.px5].join(' ')}>
|
||||||
|
<Switch
|
||||||
|
id='mark-sensitive'
|
||||||
|
type='checkbox'
|
||||||
|
checked={active}
|
||||||
|
onChange={onClick}
|
||||||
|
disabled={disabled}
|
||||||
|
label={intl.formatMessage(messages.markAsSensitive)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
178
app/javascript/gabsocial/features/compose/components/upload.js
Normal file
178
app/javascript/gabsocial/features/compose/components/upload.js
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||||
|
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||||
|
import { defineMessages, injectIntl } from 'react-intl'
|
||||||
|
import classNames from 'classnames/bind'
|
||||||
|
import { undoUploadCompose, changeUploadCompose } from '../../../actions/compose'
|
||||||
|
import { submitCompose } from '../../../actions/compose';
|
||||||
|
import Button from '../../../components/button'
|
||||||
|
import Image from '../../../components/image'
|
||||||
|
import Input from '../../../components/input'
|
||||||
|
|
||||||
|
const cx = classNames.bind(_s)
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
description: { id: 'upload_form.description', defaultMessage: 'Describe for the visually impaired' },
|
||||||
|
delete: { id: 'upload_form.undo', defaultMessage: 'Delete' },
|
||||||
|
})
|
||||||
|
|
||||||
|
const mapStateToProps = (state, { id, otherProps }) => {
|
||||||
|
console.log("otherProps:", otherProps)
|
||||||
|
return {
|
||||||
|
media: state.getIn(['compose', 'media_attachments']).find(item => item.get('id') === id),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
|
||||||
|
onUndo: id => {
|
||||||
|
dispatch(undoUploadCompose(id));
|
||||||
|
},
|
||||||
|
|
||||||
|
onDescriptionChange: (id, description) => {
|
||||||
|
dispatch(changeUploadCompose(id, { description }));
|
||||||
|
},
|
||||||
|
|
||||||
|
onSubmit (router) {
|
||||||
|
dispatch(submitCompose(router));
|
||||||
|
},
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
export default
|
||||||
|
@injectIntl
|
||||||
|
@connect(mapStateToProps, mapDispatchToProps)
|
||||||
|
class Upload extends ImmutablePureComponent {
|
||||||
|
|
||||||
|
static contextTypes = {
|
||||||
|
router: PropTypes.object,
|
||||||
|
}
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
media: ImmutablePropTypes.map.isRequired,
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
|
onUndo: PropTypes.func.isRequired,
|
||||||
|
onDescriptionChange: PropTypes.func.isRequired,
|
||||||
|
onSubmit: PropTypes.func.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
state = {
|
||||||
|
hovered: false,
|
||||||
|
focused: false,
|
||||||
|
dirtyDescription: null,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleKeyDown = (e) => {
|
||||||
|
if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
|
||||||
|
this.handleSubmit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit = () => {
|
||||||
|
this.handleInputBlur()
|
||||||
|
this.props.onSubmit(this.context.router.history)
|
||||||
|
}
|
||||||
|
|
||||||
|
handleUndoClick = e => {
|
||||||
|
e.stopPropagation()
|
||||||
|
this.props.onUndo(this.props.media.get('id'))
|
||||||
|
}
|
||||||
|
|
||||||
|
handleInputChange = e => {
|
||||||
|
this.setState({ dirtyDescription: e.target.value })
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMouseEnter = () => {
|
||||||
|
this.setState({ hovered: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMouseLeave = () => {
|
||||||
|
this.setState({ hovered: false })
|
||||||
|
}
|
||||||
|
|
||||||
|
handleInputFocus = () => {
|
||||||
|
this.setState({ focused: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
handleClick = () => {
|
||||||
|
this.setState({ focused: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
handleInputBlur = () => {
|
||||||
|
const { dirtyDescription } = this.state
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
focused: false,
|
||||||
|
dirtyDescription: null,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (dirtyDescription !== null) {
|
||||||
|
this.props.onDescriptionChange(this.props.media.get('id'), dirtyDescription)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 descriptionContainerClasses = cx({
|
||||||
|
default: 1,
|
||||||
|
positionAbsolute: 1,
|
||||||
|
right0: 1,
|
||||||
|
bottom0: 1,
|
||||||
|
left0: 1,
|
||||||
|
my5: 1,
|
||||||
|
ml5: 1,
|
||||||
|
mr5: 1,
|
||||||
|
displayNone: !active,
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log("media:", media)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
tabIndex='0'
|
||||||
|
className={[_s.default, _s.width50PC, _s.px5, _s.py5].join(' ')}
|
||||||
|
onMouseEnter={this.handleMouseEnter}
|
||||||
|
onMouseLeave={this.handleMouseLeave}
|
||||||
|
onClick={this.handleClick}
|
||||||
|
role='button'
|
||||||
|
>
|
||||||
|
<div className={[_s.default, _s.radiusSmall, _s.overflowHidden, _s.height158PX].join(' ')}>
|
||||||
|
<Image
|
||||||
|
className={[_s.default, _s.height158PX].join(' ')}
|
||||||
|
src={media.get('preview_url')}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
backgroundColor='black'
|
||||||
|
color='white'
|
||||||
|
title={intl.formatMessage(messages.delete)}
|
||||||
|
onClick={this.handleUndoClick}
|
||||||
|
icon='close'
|
||||||
|
iconWidth='10px'
|
||||||
|
iconHeight='10px'
|
||||||
|
iconClassName={_s.inherit}
|
||||||
|
className={[_s.top0, _s.right0, _s.positionAbsolute, _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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1 +0,0 @@
|
|||||||
export { default } from './upload'
|
|
@ -1,126 +0,0 @@
|
|||||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl'
|
|
||||||
import classNames from 'classnames'
|
|
||||||
import Button from '../../../../components/button'
|
|
||||||
import Image from '../../../../components/image'
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
description: { id: 'upload_form.description', defaultMessage: 'Describe for the visually impaired' },
|
|
||||||
delete: { id: 'upload_form.undo', defaultMessage: 'Delete' },
|
|
||||||
})
|
|
||||||
|
|
||||||
export default
|
|
||||||
@injectIntl
|
|
||||||
class Upload extends ImmutablePureComponent {
|
|
||||||
|
|
||||||
static contextTypes = {
|
|
||||||
router: PropTypes.object,
|
|
||||||
}
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
media: ImmutablePropTypes.map.isRequired,
|
|
||||||
intl: PropTypes.object.isRequired,
|
|
||||||
onUndo: PropTypes.func.isRequired,
|
|
||||||
onDescriptionChange: PropTypes.func.isRequired,
|
|
||||||
onSubmit: PropTypes.func.isRequired,
|
|
||||||
}
|
|
||||||
|
|
||||||
state = {
|
|
||||||
hovered: false,
|
|
||||||
focused: false,
|
|
||||||
dirtyDescription: null,
|
|
||||||
}
|
|
||||||
|
|
||||||
handleKeyDown = (e) => {
|
|
||||||
if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
|
|
||||||
this.handleSubmit()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleSubmit = () => {
|
|
||||||
this.handleInputBlur()
|
|
||||||
this.props.onSubmit(this.context.router.history)
|
|
||||||
}
|
|
||||||
|
|
||||||
handleUndoClick = e => {
|
|
||||||
e.stopPropagation()
|
|
||||||
this.props.onUndo(this.props.media.get('id'))
|
|
||||||
}
|
|
||||||
|
|
||||||
handleInputChange = e => {
|
|
||||||
this.setState({ dirtyDescription: e.target.value })
|
|
||||||
}
|
|
||||||
|
|
||||||
handleMouseEnter = () => {
|
|
||||||
this.setState({ hovered: true })
|
|
||||||
}
|
|
||||||
|
|
||||||
handleMouseLeave = () => {
|
|
||||||
this.setState({ hovered: false })
|
|
||||||
}
|
|
||||||
|
|
||||||
handleInputFocus = () => {
|
|
||||||
this.setState({ focused: true })
|
|
||||||
}
|
|
||||||
|
|
||||||
handleClick = () => {
|
|
||||||
this.setState({ focused: true })
|
|
||||||
}
|
|
||||||
|
|
||||||
handleInputBlur = () => {
|
|
||||||
const { dirtyDescription } = this.state
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
focused: false,
|
|
||||||
dirtyDescription: null,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (dirtyDescription !== null) {
|
|
||||||
this.props.onDescriptionChange(this.props.media.get('id'), dirtyDescription)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 focusX = media.getIn(['meta', 'focus', 'x'])
|
|
||||||
const focusY = media.getIn(['meta', 'focus', 'y'])
|
|
||||||
const x = ((focusX / 2) + .5) * 100
|
|
||||||
const y = ((focusY / -2) + .5) * 100
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='compose-form-upload' tabIndex='0' onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} onClick={this.handleClick} role='button'>
|
|
||||||
<div className='compose-form__upload-thumbnail' style={{ backgroundImage: `url(${media.get('preview_url')})`, backgroundPosition: `${x}% ${y}%` }}>
|
|
||||||
<div className={classNames('compose-form__upload__actions', { active })}>
|
|
||||||
<Button
|
|
||||||
title={intl.formatMessage(messages.delete)}
|
|
||||||
onClick={this.handleUndoClick}
|
|
||||||
icon='cancel'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={classNames('compose-form-upload__description', { active })}>
|
|
||||||
<label>
|
|
||||||
<span style={{ display: 'none' }}>
|
|
||||||
{intl.formatMessage(messages.description)}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<textarea
|
|
||||||
placeholder={intl.formatMessage(messages.description)}
|
|
||||||
value={description}
|
|
||||||
maxLength={420}
|
|
||||||
onFocus={this.handleInputFocus}
|
|
||||||
onChange={this.handleInputChange}
|
|
||||||
onBlur={this.handleInputBlur}
|
|
||||||
onKeyDown={this.handleKeyDown}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,78 +0,0 @@
|
|||||||
.compose-form-upload {
|
|
||||||
flex: 1 1 0;
|
|
||||||
min-width: 40%;
|
|
||||||
margin: 5px;
|
|
||||||
|
|
||||||
&__actions {
|
|
||||||
background: linear-gradient(180deg, rgba($base-shadow-color, 0.8) 0, rgba($base-shadow-color, 0.35) 80%, transparent);
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity .1s ease;
|
|
||||||
|
|
||||||
@include flex(space-between, flex-start);
|
|
||||||
|
|
||||||
.icon-button {
|
|
||||||
flex: 0 1 auto;
|
|
||||||
color: $gab-secondary-text;
|
|
||||||
padding: 10px;
|
|
||||||
font-family: inherit;
|
|
||||||
|
|
||||||
@include text-sizing(14px, 500);
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus,
|
|
||||||
&:active {
|
|
||||||
color: $gab-text-highlight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__description {
|
|
||||||
z-index: 2;
|
|
||||||
box-sizing: border-box;
|
|
||||||
background: linear-gradient(0deg, rgba($base-shadow-color, 0.8) 0, rgba($base-shadow-color, 0.35) 80%, transparent);
|
|
||||||
padding: 10px;
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity .1s ease;
|
|
||||||
|
|
||||||
@include abs-position(auto, 0, 0, 0);
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
background: rgba(0, 0, 0, 0.3);
|
|
||||||
box-sizing: border-box;
|
|
||||||
background: transparent;
|
|
||||||
color: $gab-secondary-text;
|
|
||||||
border: 1px solid $gab-secondary-text;
|
|
||||||
outline: none;
|
|
||||||
padding: 10px;
|
|
||||||
margin: 0;
|
|
||||||
width: 100%;
|
|
||||||
font-family: inherit;
|
|
||||||
|
|
||||||
@include text-sizing(14px, 500);
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::placeholder {
|
|
||||||
color: $gab-secondary-text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__thumbnail {
|
|
||||||
border-radius: 4px;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
@include size(100%, 140px);
|
|
||||||
@include background-image("");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +1,13 @@
|
|||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||||
import UploadProgress from '../upload_progress';
|
import ProgressBar from '../../../../components/progress_bar'
|
||||||
import UploadContainer from '../../containers/upload_container';
|
import Upload from '../upload'
|
||||||
import SensitiveButtonContainer from '../../containers/sensitive_button_container';
|
import SensitiveMediaButton from '../sensitive_media_button'
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
mediaIds: state.getIn(['compose', 'media_attachments']).map(item => item.get('id')),
|
mediaIds: state.getIn(['compose', 'media_attachments']).map(item => item.get('id')),
|
||||||
|
isUploading: state.getIn(['compose', 'is_uploading']),
|
||||||
|
uploadProgress: state.getIn(['compose', 'progress']),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default
|
export default
|
||||||
@ -14,24 +16,38 @@ class UploadForm extends ImmutablePureComponent {
|
|||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
mediaIds: ImmutablePropTypes.list.isRequired,
|
mediaIds: ImmutablePropTypes.list.isRequired,
|
||||||
|
isUploading: PropTypes.bool,
|
||||||
|
uploadProgress: PropTypes.number,
|
||||||
};
|
};
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { mediaIds } = this.props;
|
const {
|
||||||
|
mediaIds,
|
||||||
|
isUploading,
|
||||||
|
uploadProgress
|
||||||
|
} = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='compose-form-upload-wrapper'>
|
<div className={_s.default}>
|
||||||
<UploadProgress />
|
<div className={[_s.default, _s.flexRow, _s.flexWrap].join(' ')}>
|
||||||
|
{
|
||||||
<div className='compose-form-uploads-wrapper'>
|
mediaIds.map(id => (
|
||||||
{mediaIds.map(id => (
|
<Upload id={id} key={id} />
|
||||||
<UploadContainer id={id} key={id} />
|
))
|
||||||
))}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{!mediaIds.isEmpty() && <SensitiveButtonContainer />}
|
{
|
||||||
|
!mediaIds.isEmpty() &&
|
||||||
|
<SensitiveMediaButton />
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
isUploading &&
|
||||||
|
<ProgressBar small progress={uploadProgress} />
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1 +0,0 @@
|
|||||||
export { default } from './upload_progress'
|
|
@ -1,48 +0,0 @@
|
|||||||
import { FormattedMessage } from 'react-intl';
|
|
||||||
import spring from 'react-motion/lib/spring';
|
|
||||||
import Motion from '../../../ui/util/optional_motion';
|
|
||||||
import Icon from '../../../../components/icon';
|
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
|
||||||
active: state.getIn(['compose', 'is_uploading']),
|
|
||||||
progress: state.getIn(['compose', 'progress']),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default
|
|
||||||
@connect(mapStateToProps)
|
|
||||||
class UploadProgress extends PureComponent {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
active: PropTypes.bool,
|
|
||||||
progress: PropTypes.number,
|
|
||||||
};
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { active, progress } = this.props;
|
|
||||||
|
|
||||||
if (!active) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='upload-progress'>
|
|
||||||
<div className='upload-progress__icon'>
|
|
||||||
<Icon id='upload' />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='upload-progress__message'>
|
|
||||||
<FormattedMessage id='upload_progress.label' defaultMessage='Uploading...' />
|
|
||||||
|
|
||||||
<div className='upload-progress__backdrop'>
|
|
||||||
<Motion defaultStyle={{ width: 0 }} style={{ width: spring(progress) }}>
|
|
||||||
{({ width }) =>
|
|
||||||
<div className='upload-progress__tracker' style={{ width: `${width}%` }} />
|
|
||||||
}
|
|
||||||
</Motion>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
.upload-progress {
|
|
||||||
padding: 10px;
|
|
||||||
color: $lighter-text-color;
|
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
.fa {
|
|
||||||
font-size: 34px;
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
|
||||||
text-transform: uppercase;
|
|
||||||
display: block;
|
|
||||||
|
|
||||||
@include text-sizing(12px, 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
&__message {
|
|
||||||
flex: 1 1 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__backdrop {
|
|
||||||
border-radius: 6px;
|
|
||||||
background: $ui-base-lighter-color;
|
|
||||||
position: relative;
|
|
||||||
margin-top: 5px;
|
|
||||||
|
|
||||||
@include size(100%, 6px);
|
|
||||||
}
|
|
||||||
|
|
||||||
&__tracker {
|
|
||||||
height: 6px;
|
|
||||||
background: $ui-highlight-color;
|
|
||||||
border-radius: 6px;
|
|
||||||
|
|
||||||
@include abs-position(0, auto, auto, 0);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
import classNames from 'classnames';
|
|
||||||
import { injectIntl, defineMessages, FormattedMessage } from 'react-intl';
|
|
||||||
import { changeComposeSensitivity } from '../../../actions/compose';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
marked: { id: 'compose_form.sensitive.marked', defaultMessage: 'Media is marked as sensitive' },
|
|
||||||
unmarked: { id: 'compose_form.sensitive.unmarked', defaultMessage: 'Media is not marked as sensitive' },
|
|
||||||
});
|
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
|
||||||
active: state.getIn(['compose', 'sensitive']),
|
|
||||||
disabled: state.getIn(['compose', 'spoiler']),
|
|
||||||
});
|
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
|
||||||
|
|
||||||
onClick () {
|
|
||||||
dispatch(changeComposeSensitivity());
|
|
||||||
},
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
class SensitiveButton extends PureComponent {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
active: PropTypes.bool,
|
|
||||||
disabled: PropTypes.bool,
|
|
||||||
onClick: PropTypes.func.isRequired,
|
|
||||||
intl: PropTypes.object.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { active, disabled, onClick, intl } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='compose-form__sensitive-button'>
|
|
||||||
<label className={classNames('icon-button', { active })} title={intl.formatMessage(active ? messages.marked : messages.unmarked)}>
|
|
||||||
<input
|
|
||||||
name='mark-sensitive'
|
|
||||||
type='checkbox'
|
|
||||||
checked={active}
|
|
||||||
onChange={onClick}
|
|
||||||
disabled={disabled}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<span className={classNames('checkbox', { active })} />
|
|
||||||
|
|
||||||
<FormattedMessage id='compose_form.sensitive.hide' defaultMessage='Mark media as sensitive' />
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(SensitiveButton));
|
|
@ -1,26 +0,0 @@
|
|||||||
import Upload from '../components/upload';
|
|
||||||
import { undoUploadCompose, changeUploadCompose } from '../../../actions/compose';
|
|
||||||
import { openModal } from '../../../actions/modal';
|
|
||||||
import { submitCompose } from '../../../actions/compose';
|
|
||||||
|
|
||||||
const mapStateToProps = (state, { id }) => ({
|
|
||||||
media: state.getIn(['compose', 'media_attachments']).find(item => item.get('id') === id),
|
|
||||||
});
|
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
|
||||||
|
|
||||||
onUndo: id => {
|
|
||||||
dispatch(undoUploadCompose(id));
|
|
||||||
},
|
|
||||||
|
|
||||||
onDescriptionChange: (id, description) => {
|
|
||||||
dispatch(changeUploadCompose(id, { description }));
|
|
||||||
},
|
|
||||||
|
|
||||||
onSubmit (router) {
|
|
||||||
dispatch(submitCompose(router));
|
|
||||||
},
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(Upload);
|
|
@ -6,7 +6,6 @@ import { makeGetAccount } from '../../../../selectors';
|
|||||||
import Button from '../../../../components/button'
|
import Button from '../../../../components/button'
|
||||||
import Avatar from '../../../../components/avatar';
|
import Avatar from '../../../../components/avatar';
|
||||||
import DisplayName from '../../../../components/display_name';
|
import DisplayName from '../../../../components/display_name';
|
||||||
import IconButton from '../../../../components/icon_button';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
authorize: { id: 'follow_request.authorize', defaultMessage: 'Authorize' },
|
authorize: { id: 'follow_request.authorize', defaultMessage: 'Authorize' },
|
||||||
@ -64,10 +63,10 @@ class AccountAuthorize extends ImmutablePureComponent {
|
|||||||
|
|
||||||
<div className='account--panel'>
|
<div className='account--panel'>
|
||||||
<div className='account--panel__button'>
|
<div className='account--panel__button'>
|
||||||
<IconButton title={intl.formatMessage(messages.authorize)} icon='check' onClick={onAuthorize} />
|
<Button title={intl.formatMessage(messages.authorize)} icon='check' onClick={onAuthorize} />
|
||||||
</div>
|
</div>
|
||||||
<div className='account--panel__button'>
|
<div className='account--panel__button'>
|
||||||
<IconButton title={intl.formatMessage(messages.reject)} icon='times' onClick={onReject} />
|
<Button title={intl.formatMessage(messages.reject)} icon='times' onClick={onReject} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,7 @@ import { defineMessages, injectIntl } from 'react-intl';
|
|||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import { removeFromListAdder, addToListAdder } from '../../../../actions/lists';
|
import { removeFromListAdder, addToListAdder } from '../../../../actions/lists';
|
||||||
import IconButton from '../../../../components/icon_button';
|
import Button from '../../../../components/button';
|
||||||
import Icon from '../../../../components/icon';
|
import Icon from '../../../../components/icon';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
@ -51,9 +51,9 @@ class List extends ImmutablePureComponent {
|
|||||||
<div className='list__btn-block'>
|
<div className='list__btn-block'>
|
||||||
{
|
{
|
||||||
added ?
|
added ?
|
||||||
<IconButton icon='times' title={intl.formatMessage(messages.remove)} onClick={onRemove} />
|
<Button icon='times' title={intl.formatMessage(messages.remove)} onClick={onRemove} />
|
||||||
:
|
:
|
||||||
<IconButton icon='plus' title={intl.formatMessage(messages.add)} onClick={onAdd} />
|
<Button icon='plus' title={intl.formatMessage(messages.add)} onClick={onAdd} />
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,7 +5,7 @@ import { createSelector } from 'reselect';
|
|||||||
import { setupListAdder, resetListAdder } from '../../actions/lists';
|
import { setupListAdder, resetListAdder } from '../../actions/lists';
|
||||||
import List from './components/list';
|
import List from './components/list';
|
||||||
import Account from '../../components/account';
|
import Account from '../../components/account';
|
||||||
import IconButton from '../../components/icon_button';
|
import Button from '../../components/button';
|
||||||
// import NewListForm from '../lists_directory/components/new_list_form';
|
// import NewListForm from '../lists_directory/components/new_list_form';
|
||||||
|
|
||||||
const getOrderedLists = createSelector([state => state.get('lists')], lists => {
|
const getOrderedLists = createSelector([state => state.get('lists')], lists => {
|
||||||
@ -70,7 +70,7 @@ class ListAdder extends ImmutablePureComponent {
|
|||||||
<h3 className='compose-modal__header__title'>
|
<h3 className='compose-modal__header__title'>
|
||||||
{intl.formatMessage(messages.headerTitle)}
|
{intl.formatMessage(messages.headerTitle)}
|
||||||
</h3>
|
</h3>
|
||||||
<IconButton className='compose-modal__close' title={intl.formatMessage(messages.close)} icon='times' onClick={this.onClickClose} size={20} />
|
<Button className='compose-modal__close' title={intl.formatMessage(messages.close)} icon='times' onClick={this.onClickClose} size={20} />
|
||||||
</div>
|
</div>
|
||||||
<div className='compose-modal__content'>
|
<div className='compose-modal__content'>
|
||||||
<div className='list-adder'>
|
<div className='list-adder'>
|
||||||
|
@ -6,7 +6,7 @@ import { removeFromListEditor, addToListEditor } from '../../../actions/lists';
|
|||||||
import { makeGetAccount } from '../../../selectors';
|
import { makeGetAccount } from '../../../selectors';
|
||||||
import Avatar from '../../../components/avatar';
|
import Avatar from '../../../components/avatar';
|
||||||
import DisplayName from '../../../components/display_name';
|
import DisplayName from '../../../components/display_name';
|
||||||
import IconButton from '../../../components/icon_button';
|
import Button from '../../../components/button';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
remove: { id: 'lists.account.remove', defaultMessage: 'Remove from list' },
|
remove: { id: 'lists.account.remove', defaultMessage: 'Remove from list' },
|
||||||
@ -52,9 +52,9 @@ class Account extends ImmutablePureComponent {
|
|||||||
let button;
|
let button;
|
||||||
|
|
||||||
if (added) {
|
if (added) {
|
||||||
button = <IconButton icon='times' title={intl.formatMessage(messages.remove)} onClick={onRemove} />;
|
button = <Button icon='times' title={intl.formatMessage(messages.remove)} onClick={onRemove} />;
|
||||||
} else {
|
} else {
|
||||||
button = <IconButton icon='plus' title={intl.formatMessage(messages.add)} onClick={onAdd} />;
|
button = <Button icon='plus' title={intl.formatMessage(messages.add)} onClick={onAdd} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1,22 +1,32 @@
|
|||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||||
import { injectIntl, defineMessages } from 'react-intl';
|
import { injectIntl, defineMessages } from 'react-intl'
|
||||||
import { setupListEditor, resetListEditor } from '../../actions/lists';
|
import isObject from 'lodash.isobject'
|
||||||
import Account from './components/account';
|
import { setupListEditor, resetListEditor } from '../../actions/lists'
|
||||||
import ListEditorSearch from './components/list_editor_search';
|
import Account from './components/account'
|
||||||
import EditListForm from './components/edit_list_form/edit_list_form';
|
import ListEditorSearch from './components/list_editor_search'
|
||||||
import IconButton from '../../components/icon_button';
|
import EditListForm from './components/edit_list_form/edit_list_form'
|
||||||
|
import Button from '../../components/button'
|
||||||
import Input from '../../components/input'
|
import Input from '../../components/input'
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = (state, { params }) => {
|
||||||
accountIds: state.getIn(['listEditor', 'accounts', 'items']),
|
|
||||||
searchAccountIds: state.getIn(['listEditor', 'suggestions', 'items']),
|
console.log("params:", params)
|
||||||
});
|
|
||||||
|
const listId = isObject(params) ? params['id'] : null
|
||||||
|
|
||||||
|
return {
|
||||||
|
listId,
|
||||||
|
title: state.getIn(['listEditor', 'title']),
|
||||||
|
accountIds: state.getIn(['listEditor', 'accounts', 'items']),
|
||||||
|
searchAccountIds: state.getIn(['listEditor', 'suggestions', 'items']),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
onInitialize: listId => dispatch(setupListEditor(listId)),
|
onInitialize: listId => dispatch(setupListEditor(listId)),
|
||||||
onReset: () => dispatch(resetListEditor()),
|
onReset: () => dispatch(resetListEditor()),
|
||||||
});
|
})
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
close: { id: 'lightbox.close', defaultMessage: 'Close' },
|
close: { id: 'lightbox.close', defaultMessage: 'Close' },
|
||||||
@ -24,7 +34,8 @@ const messages = defineMessages({
|
|||||||
addToList: { id: 'lists.account.add', defaultMessage: 'Add to list' },
|
addToList: { id: 'lists.account.add', defaultMessage: 'Add to list' },
|
||||||
removeFromList: { id: 'lists.account.remove', defaultMessage: 'Remove from list' },
|
removeFromList: { id: 'lists.account.remove', defaultMessage: 'Remove from list' },
|
||||||
editList: { id: 'lists.edit', defaultMessage: 'Edit list' },
|
editList: { id: 'lists.edit', defaultMessage: 'Edit list' },
|
||||||
});
|
editListTitle: { id: 'lists.new.edit_title_placeholder', defaultMessage: 'Edit list title' },
|
||||||
|
})
|
||||||
|
|
||||||
export default
|
export default
|
||||||
@connect(mapStateToProps, mapDispatchToProps)
|
@connect(mapStateToProps, mapDispatchToProps)
|
||||||
@ -32,6 +43,7 @@ export default
|
|||||||
class ListEdit extends ImmutablePureComponent {
|
class ListEdit extends ImmutablePureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
title: PropTypes.string,
|
||||||
listId: PropTypes.string.isRequired,
|
listId: PropTypes.string.isRequired,
|
||||||
onClose: PropTypes.func.isRequired,
|
onClose: PropTypes.func.isRequired,
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
@ -39,63 +51,79 @@ class ListEdit extends ImmutablePureComponent {
|
|||||||
onReset: PropTypes.func.isRequired,
|
onReset: PropTypes.func.isRequired,
|
||||||
accountIds: ImmutablePropTypes.list.isRequired,
|
accountIds: ImmutablePropTypes.list.isRequired,
|
||||||
searchAccountIds: ImmutablePropTypes.list.isRequired,
|
searchAccountIds: ImmutablePropTypes.list.isRequired,
|
||||||
};
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { onInitialize, listId } = this.props;
|
const { onInitialize, listId } = this.props
|
||||||
if (listId) onInitialize(listId);
|
console.log("listId:", listId)
|
||||||
|
if (listId) {
|
||||||
|
onInitialize(listId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.props.onReset();
|
this.props.onReset()
|
||||||
}
|
}
|
||||||
|
|
||||||
onClickClose = () => {
|
onClickClose = () => {
|
||||||
this.props.onClose('LIST_ADDER');
|
this.props.onClose('LIST_ADDER')
|
||||||
};
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { accountIds, searchAccountIds, intl } = this.props;
|
const {
|
||||||
|
title,
|
||||||
|
accountIds,
|
||||||
|
searchAccountIds,
|
||||||
|
intl
|
||||||
|
} = this.props
|
||||||
|
|
||||||
|
console.log("title:", title)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Input
|
<Input
|
||||||
title={intl.formatMessage(messages.editList)}
|
title={intl.formatMessage(messages.editListTitle)}
|
||||||
|
placeholder='My new list title...'
|
||||||
|
value={title}
|
||||||
|
// onChange={onChange}
|
||||||
|
// onSubmit={onSubmit}
|
||||||
|
// disabled={disabled}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
<div className='compose-modal__header'>
|
||||||
|
<h3 className='compose-modal__header__title'>
|
||||||
|
{intl.formatMessage(messages.editList)}
|
||||||
|
</h3>
|
||||||
|
<Button className='compose-modal__close' title={intl.formatMessage(messages.close)} icon='times' onClick={this.onClickClose} size={20} />
|
||||||
|
</div>
|
||||||
|
<div className='compose-modal__content'>
|
||||||
|
<div className='list-editor'>
|
||||||
|
<EditListForm />
|
||||||
|
<br />
|
||||||
|
|
||||||
|
{
|
||||||
|
accountIds.size > 0 &&
|
||||||
|
<div>
|
||||||
|
<div className='list-editor__accounts'>
|
||||||
|
{accountIds.map(accountId => <Account key={accountId} accountId={accountId} added />)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<ListEditorSearch />
|
||||||
|
<div className='list-editor__accounts'>
|
||||||
|
{searchAccountIds.map(accountId => <Account key={accountId} accountId={accountId} />)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
*/ }
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
// return (
|
|
||||||
// <div className='modal-root__modal compose-modal'>
|
|
||||||
// <div className='compose-modal__header'>
|
|
||||||
// <h3 className='compose-modal__header__title'>
|
|
||||||
// {intl.formatMessage(messages.editList)}
|
|
||||||
// </h3>
|
|
||||||
// <IconButton className='compose-modal__close' title={intl.formatMessage(messages.close)} icon='times' onClick={this.onClickClose} size={20} />
|
|
||||||
// </div>
|
|
||||||
// <div className='compose-modal__content'>
|
|
||||||
// <div className='list-editor'>
|
|
||||||
// <EditListForm />
|
|
||||||
// <br />
|
|
||||||
|
|
||||||
// {
|
|
||||||
// accountIds.size > 0 &&
|
|
||||||
// <div>
|
|
||||||
// <div className='list-editor__accounts'>
|
|
||||||
// {accountIds.map(accountId => <Account key={accountId} accountId={accountId} added />)}
|
|
||||||
// </div>
|
|
||||||
// </div>
|
|
||||||
// }
|
|
||||||
|
|
||||||
// <br />
|
|
||||||
// <ListEditorSearch />
|
|
||||||
// <div className='list-editor__accounts'>
|
|
||||||
// {searchAccountIds.map(accountId => <Account key={accountId} accountId={accountId} />)}
|
|
||||||
// </div>
|
|
||||||
// </div>
|
|
||||||
// </div>
|
|
||||||
// </div>
|
|
||||||
// );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ class ListsDirectory extends ImmutablePureComponent {
|
|||||||
const emptyMessage = intl.formatMessage(messages.empty)
|
const emptyMessage = intl.formatMessage(messages.empty)
|
||||||
|
|
||||||
const listItems = lists.map(list => ({
|
const listItems = lists.map(list => ({
|
||||||
to: `/list/${list.get('id')}`,
|
to: `/lists/${list.get('id')}`,
|
||||||
title: list.get('title'),
|
title: list.get('title'),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
@ -1,89 +0,0 @@
|
|||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
|
||||||
import { FormattedMessage } from 'react-intl';
|
|
||||||
import ColumnHeaderSettingButton from '../../../../components/column_header_setting_button';
|
|
||||||
import SettingSwitch from '../../../../components/setting_switch';
|
|
||||||
|
|
||||||
export default class ColumnSettings extends ImmutablePureComponent {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
settings: ImmutablePropTypes.map.isRequired,
|
|
||||||
pushSettings: ImmutablePropTypes.map.isRequired,
|
|
||||||
onChange: PropTypes.func.isRequired,
|
|
||||||
onClear: PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
onPushChange = (path, checked) => {
|
|
||||||
this.props.onChange(['push', ...path], checked);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { settings, pushSettings, onChange, onClear } = this.props;
|
|
||||||
|
|
||||||
const filterShowStr = <FormattedMessage id='notifications.column_settings.filter_bar.show' defaultMessage='Show' />;
|
|
||||||
const filterAdvancedStr = <FormattedMessage id='notifications.column_settings.filter_bar.advanced' defaultMessage='Display all categories' />;
|
|
||||||
const alertStr = <FormattedMessage id='notifications.column_settings.alert' defaultMessage='Desktop notifications' />;
|
|
||||||
const showStr = <FormattedMessage id='notifications.column_settings.show' defaultMessage='Show in column' />;
|
|
||||||
const soundStr = <FormattedMessage id='notifications.column_settings.sound' defaultMessage='Play sound' />;
|
|
||||||
|
|
||||||
const showPushSettings = pushSettings.get('browserSupport') && pushSettings.get('isSubscribed');
|
|
||||||
const pushStr = showPushSettings && <FormattedMessage id='notifications.column_settings.push' defaultMessage='Push notifications' />;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<ColumnHeaderSettingButton
|
|
||||||
onClick={onClear}
|
|
||||||
title={<FormattedMessage id='notifications.clear' defaultMessage='Clear notifications' />}
|
|
||||||
icon='eraser'
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div role='group' aria-labelledby='notifications-filter-bar'>
|
|
||||||
<FormattedMessage id='notifications.column_settings.filter_bar.category' defaultMessage='Quick filter bar' />
|
|
||||||
<SettingSwitch id='show-filter-bar' prefix='notifications' settings={settings} settingPath={['quickFilter', 'show']} onChange={onChange} label={filterShowStr} />
|
|
||||||
<SettingSwitch id='show-filter-bar' prefix='notifications' settings={settings} settingPath={['quickFilter', 'advanced']} onChange={onChange} label={filterAdvancedStr} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div role='group' aria-labelledby='notifications-follow'>
|
|
||||||
<FormattedMessage id='notifications.column_settings.follow' defaultMessage='New followers:' />
|
|
||||||
<SettingSwitch prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'follow']} onChange={onChange} label={alertStr} />
|
|
||||||
{showPushSettings && <SettingSwitch prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'follow']} onChange={this.onPushChange} label={pushStr} />}
|
|
||||||
<SettingSwitch prefix='notifications' settings={settings} settingPath={['shows', 'follow']} onChange={onChange} label={showStr} />
|
|
||||||
<SettingSwitch prefix='notifications' settings={settings} settingPath={['sounds', 'follow']} onChange={onChange} label={soundStr} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div role='group' aria-labelledby='notifications-favorite'>
|
|
||||||
<FormattedMessage id='notifications.column_settings.favorite' defaultMessage='Favorites:' />
|
|
||||||
<SettingSwitch prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'favorite']} onChange={onChange} label={alertStr} />
|
|
||||||
{showPushSettings && <SettingSwitch prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'favorite']} onChange={this.onPushChange} label={pushStr} />}
|
|
||||||
<SettingSwitch prefix='notifications' settings={settings} settingPath={['shows', 'favorite']} onChange={onChange} label={showStr} />
|
|
||||||
<SettingSwitch prefix='notifications' settings={settings} settingPath={['sounds', 'favorite']} onChange={onChange} label={soundStr} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div role='group' aria-labelledby='notifications-mention'>
|
|
||||||
<FormattedMessage id='notifications.column_settings.mention' defaultMessage='Mentions:' />
|
|
||||||
<SettingSwitch prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'mention']} onChange={onChange} label={alertStr} />
|
|
||||||
{showPushSettings && <SettingSwitch prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'mention']} onChange={this.onPushChange} label={pushStr} />}
|
|
||||||
<SettingSwitch prefix='notifications' settings={settings} settingPath={['shows', 'mention']} onChange={onChange} label={showStr} />
|
|
||||||
<SettingSwitch prefix='notifications' settings={settings} settingPath={['sounds', 'mention']} onChange={onChange} label={soundStr} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div role='group' aria-labelledby='notifications-repost'>
|
|
||||||
<FormattedMessage id='notifications.column_settings.repost' defaultMessage='Reposts:' />
|
|
||||||
<SettingSwitch prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'repost']} onChange={onChange} label={alertStr} />
|
|
||||||
{showPushSettings && <SettingSwitch prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'repost']} onChange={this.onPushChange} label={pushStr} />}
|
|
||||||
<SettingSwitch prefix='notifications' settings={settings} settingPath={['shows', 'repost']} onChange={onChange} label={showStr} />
|
|
||||||
<SettingSwitch prefix='notifications' settings={settings} settingPath={['sounds', 'repost']} onChange={onChange} label={soundStr} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div role='group' aria-labelledby='notifications-poll'>
|
|
||||||
<FormattedMessage id='notifications.column_settings.poll' defaultMessage='Poll results:' />
|
|
||||||
<SettingSwitch prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'poll']} onChange={onChange} label={alertStr} />
|
|
||||||
{showPushSettings && <SettingSwitch prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'poll']} onChange={this.onPushChange} label={pushStr} />}
|
|
||||||
<SettingSwitch prefix='notifications' settings={settings} settingPath={['shows', 'poll']} onChange={onChange} label={showStr} />
|
|
||||||
<SettingSwitch prefix='notifications' settings={settings} settingPath={['sounds', 'poll']} onChange={onChange} label={soundStr} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
export { default } from './column_settings'
|
|
@ -1 +0,0 @@
|
|||||||
export { default } from './notification_filter_bar'
|
|
@ -1,95 +0,0 @@
|
|||||||
import { defineMessages, injectIntl } from 'react-intl';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
mentions: { id: 'notifications.filter.mentions', defaultMessage: 'Mentions' },
|
|
||||||
favorites: { id: 'notifications.filter.favorites', defaultMessage: 'Favorites' },
|
|
||||||
boosts: { id: 'notifications.filter.boosts', defaultMessage: 'Reposts' },
|
|
||||||
polls: { id: 'notifications.filter.polls', defaultMessage: 'Poll results' },
|
|
||||||
follows: { id: 'notifications.filter.follows', defaultMessage: 'Follows' },
|
|
||||||
filterAll: { id: 'notifications.filter.all', defaultMessage: 'All' },
|
|
||||||
filterMentions: { id: 'notifications.filter.mentions', defaultMessage: 'Mentions' },
|
|
||||||
});
|
|
||||||
|
|
||||||
export default
|
|
||||||
@injectIntl
|
|
||||||
class NotificationFilterBar extends PureComponent {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
selectFilter: PropTypes.func.isRequired,
|
|
||||||
selectedFilter: PropTypes.string.isRequired,
|
|
||||||
advancedMode: PropTypes.bool.isRequired,
|
|
||||||
intl: PropTypes.object.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
onClick (notificationType) {
|
|
||||||
return () => this.props.selectFilter(notificationType);
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { selectedFilter, advancedMode, intl } = this.props;
|
|
||||||
|
|
||||||
if (!advancedMode) {
|
|
||||||
return (
|
|
||||||
{ /* <SectionHeadlineBar
|
|
||||||
items={[
|
|
||||||
{
|
|
||||||
className: selectedFilter === 'all' ? 'active' : '',
|
|
||||||
onClick: this.onClick('all'),
|
|
||||||
title: intl.formatMessage(messages.filterAll),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
className: selectedFilter === 'mention' ? 'active' : '',
|
|
||||||
onClick: this.onClick('mention'),
|
|
||||||
title: intl.formatMessage(messages.filterMentions),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
/> */ }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div />
|
|
||||||
)
|
|
||||||
|
|
||||||
/* <SectionHeadlineBar
|
|
||||||
items={[
|
|
||||||
{
|
|
||||||
className: selectedFilter === 'all' ? 'active' : '',
|
|
||||||
onClick: this.onClick('all'),
|
|
||||||
title: intl.formatMessage(messages.filterAll),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
className: selectedFilter === 'mention' ? 'active' : '',
|
|
||||||
onClick: this.onClick('mention'),
|
|
||||||
title: intl.formatMessage(messages.mentions),
|
|
||||||
icon: 'at',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
className: selectedFilter === 'favorite' ? 'active' : '',
|
|
||||||
onClick: this.onClick('favorite'),
|
|
||||||
title: intl.formatMessage(messages.favorites),
|
|
||||||
icon: 'star',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
className: selectedFilter === 'reblog' ? 'active' : '',
|
|
||||||
onClick: this.onClick('reblog'),
|
|
||||||
title: intl.formatMessage(messages.boosts),
|
|
||||||
icon: 'retweet',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
className: selectedFilter === 'poll' ? 'active' : '',
|
|
||||||
onClick: this.onClick('poll'),
|
|
||||||
title: intl.formatMessage(messages.polls),
|
|
||||||
icon: 'tasks',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
className: selectedFilter === 'follow' ? 'active' : '',
|
|
||||||
onClick: this.onClick('follow'),
|
|
||||||
title: intl.formatMessage(messages.follows),
|
|
||||||
icon: 'user-plus',
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/> */
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -16,6 +16,7 @@ const mapStateToProps = state => ({
|
|||||||
pushSettings: state.get('push_notifications'),
|
pushSettings: state.get('push_notifications'),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// : todo : put all notification settings actually IN settings
|
||||||
const mapDispatchToProps = (dispatch, { intl }) => ({
|
const mapDispatchToProps = (dispatch, { intl }) => ({
|
||||||
|
|
||||||
onChange (path, checked) {
|
onChange (path, checked) {
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
import { setFilter } from '../../../actions/notifications';
|
|
||||||
import FilterBar from '../components/notification_filter_bar';
|
|
||||||
|
|
||||||
const makeMapStateToProps = state => ({
|
|
||||||
selectedFilter: state.getIn(['settings', 'notifications', 'quickFilter', 'active']),
|
|
||||||
advancedMode: state.getIn(['settings', 'notifications', 'quickFilter', 'advanced']),
|
|
||||||
});
|
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
|
||||||
selectFilter (newActiveFilter) {
|
|
||||||
dispatch(setFilter(newActiveFilter));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(makeMapStateToProps, mapDispatchToProps)(FilterBar);
|
|
@ -165,7 +165,7 @@ class SwitchingArea extends PureComponent {
|
|||||||
<WrappedRoute path='/lists' exact page={ListsPage} component={ListsDirectory} content={children} />
|
<WrappedRoute path='/lists' exact page={ListsPage} component={ListsDirectory} content={children} />
|
||||||
<WrappedRoute path='/lists/create' exact page={ModalPage} component={ListCreate} content={children} componentParams={{ title: 'Create List' }} />
|
<WrappedRoute path='/lists/create' exact page={ModalPage} component={ListCreate} content={children} componentParams={{ title: 'Create List' }} />
|
||||||
<WrappedRoute path='/lists/:id/edit' exact page={ModalPage} component={ListEdit} content={children} componentParams={{ title: 'Edit List' }} />
|
<WrappedRoute path='/lists/:id/edit' exact page={ModalPage} component={ListEdit} content={children} componentParams={{ title: 'Edit List' }} />
|
||||||
<WrappedRoute path='/list/:id' page={ListPage} component={ListTimeline} content={children} />
|
<WrappedRoute path='/lists/:id' page={ListPage} component={ListTimeline} content={children} />
|
||||||
|
|
||||||
<WrappedRoute path='/notifications' exact page={NotificationsPage} component={Notifications} content={children} />
|
<WrappedRoute path='/notifications' exact page={NotificationsPage} component={Notifications} content={children} />
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import ColumnHeader from '../components/column_header'
|
|||||||
import Sidebar from '../components/sidebar'
|
import Sidebar from '../components/sidebar'
|
||||||
// import Header from '../components/header'
|
// import Header from '../components/header'
|
||||||
// import Footer from '../components/footer'
|
// import Footer from '../components/footer'
|
||||||
|
import FloatingActionButton from '../components/floating_action_button'
|
||||||
import Responsive from '../features/ui/util/responsive_component'
|
import Responsive from '../features/ui/util/responsive_component'
|
||||||
|
|
||||||
export default class DefaultLayout extends PureComponent {
|
export default class DefaultLayout extends PureComponent {
|
||||||
@ -19,9 +20,6 @@ export default class DefaultLayout extends PureComponent {
|
|||||||
render() {
|
render() {
|
||||||
const { children, title, showBackBtn, layout, actions, tabs } = this.props
|
const { children, title, showBackBtn, layout, actions, tabs } = this.props
|
||||||
|
|
||||||
// const shouldHideFAB = path => path.match(/^\/posts\/|^\/search|^\/getting-started/);
|
|
||||||
// const floatingActionButton = shouldHideFAB(this.context.router.history.location.pathname) ? null : <button key='floating-action-button' onClick={this.handleOpenComposeModal} className='floating-action-button' aria-label={intl.formatMessage(messages.publish)}></button>;
|
|
||||||
|
|
||||||
console.log("Constants.BREAKPOINT_EXTRA_SMALL:", Constants.BREAKPOINT_EXTRA_SMALL)
|
console.log("Constants.BREAKPOINT_EXTRA_SMALL:", Constants.BREAKPOINT_EXTRA_SMALL)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -43,9 +41,11 @@ export default class DefaultLayout extends PureComponent {
|
|||||||
tabs={tabs}
|
tabs={tabs}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={[_s.default, _s.width340PX].join(' ')}>
|
<Responsive min={Constants.BREAKPOINT_EXTRA_SMALL}>
|
||||||
<Search />
|
<div className={[_s.default, _s.width340PX].join(' ')}>
|
||||||
</div>
|
<Search />
|
||||||
|
</div>
|
||||||
|
</Responsive>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -68,6 +68,10 @@ export default class DefaultLayout extends PureComponent {
|
|||||||
</div>
|
</div>
|
||||||
</Responsive>
|
</Responsive>
|
||||||
|
|
||||||
|
<Responsive max={Constants.BREAKPOINT_EXTRA_SMALL}>
|
||||||
|
<FloatingActionButton />
|
||||||
|
</Responsive>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
@ -24,14 +24,6 @@ class GroupsPage extends PureComponent {
|
|||||||
document.title = 'Groups - Gab'
|
document.title = 'Groups - Gab'
|
||||||
}
|
}
|
||||||
|
|
||||||
handleClickNewList () {
|
|
||||||
console.log("handleClickNewList")
|
|
||||||
}
|
|
||||||
|
|
||||||
handleClickEditLists () {
|
|
||||||
console.log("handleClickEditLists")
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { children, onOpenGroupCreateModal } = this.props
|
const { children, onOpenGroupCreateModal } = this.props
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ const mapStateToProps = (state, props) => ({
|
|||||||
|
|
||||||
const mapDispatchToProps = (dispatch, { list }) => ({
|
const mapDispatchToProps = (dispatch, { list }) => ({
|
||||||
onOpenListEditModal() {
|
onOpenListEditModal() {
|
||||||
dispatch(openModal('GROUP_DELETE', {
|
dispatch(openModal('LIST_EDIT', {
|
||||||
list,
|
list,
|
||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
|
@ -1,19 +1,87 @@
|
|||||||
import { Fragment } from 'react'
|
import { Fragment } from 'react'
|
||||||
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
|
import { setFilter } from '../actions/notifications'
|
||||||
import LinkFooter from '../components/link_footer'
|
import LinkFooter from '../components/link_footer'
|
||||||
import WhoToFollowPanel from '../components/panel/who_to_follow_panel'
|
import WhoToFollowPanel from '../components/panel/who_to_follow_panel'
|
||||||
import NotificationFilterPanel from '../components/panel/notification_filter_panel'
|
import NotificationFilterPanel from '../components/panel/notification_filter_panel'
|
||||||
import TrendsPanel from '../components/panel/trends_panel'
|
import TrendsPanel from '../components/panel/trends_panel'
|
||||||
import DefaultLayout from '../layouts/default_layout'
|
import DefaultLayout from '../layouts/default_layout'
|
||||||
|
|
||||||
export default class NotificationsPage extends PureComponent {
|
const messages = defineMessages({
|
||||||
|
mentions: { id: 'notifications.filter.mentions', defaultMessage: 'Mentions' },
|
||||||
|
favorites: { id: 'notifications.filter.favorites', defaultMessage: 'Favorites' },
|
||||||
|
boosts: { id: 'notifications.filter.boosts', defaultMessage: 'Reposts' },
|
||||||
|
polls: { id: 'notifications.filter.polls', defaultMessage: 'Poll results' },
|
||||||
|
follows: { id: 'notifications.filter.follows', defaultMessage: 'Follows' },
|
||||||
|
filterAll: { id: 'notifications.filter.all', defaultMessage: 'All' },
|
||||||
|
filterMentions: { id: 'notifications.filter.mentions', defaultMessage: 'Mentions' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const makeMapStateToProps = state => ({
|
||||||
|
selectedFilter: state.getIn(['settings', 'notifications', 'quickFilter', 'active']),
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
selectFilter (newActiveFilter) {
|
||||||
|
dispatch(setFilter(newActiveFilter));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default
|
||||||
|
@injectIntl
|
||||||
|
@connect(makeMapStateToProps, mapDispatchToProps)
|
||||||
|
class NotificationsPage extends PureComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
selectFilter: PropTypes.func.isRequired,
|
||||||
|
selectedFilter: PropTypes.string.isRequired,
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
document.title = 'Notifications - Gab'
|
document.title = 'Notifications - Gab'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onClick (notificationType) {
|
||||||
|
return () => this.props.selectFilter(notificationType);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { children } = this.props
|
const { children } = this.props
|
||||||
|
|
||||||
|
const tabs = [
|
||||||
|
{
|
||||||
|
title: 'All',
|
||||||
|
onClick: null,
|
||||||
|
active: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: ' @ ',
|
||||||
|
onClick: null,
|
||||||
|
active: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'like',
|
||||||
|
onClick: null,
|
||||||
|
active: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'repost',
|
||||||
|
onClick: null,
|
||||||
|
active: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'poll',
|
||||||
|
onClick: null,
|
||||||
|
active: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'user-plus',
|
||||||
|
onClick: null,
|
||||||
|
active: false,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DefaultLayout
|
<DefaultLayout
|
||||||
title='Notifications'
|
title='Notifications'
|
||||||
@ -25,7 +93,7 @@ export default class NotificationsPage extends PureComponent {
|
|||||||
<LinkFooter />
|
<LinkFooter />
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)}
|
)}
|
||||||
showBackBtn
|
tabs={tabs}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</DefaultLayout>
|
</DefaultLayout>
|
||||||
|
@ -44,34 +44,9 @@ const initialState = ImmutableMap({
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
notifications: ImmutableMap({
|
notifications: ImmutableMap({
|
||||||
alerts: ImmutableMap({
|
// : todo : put all notification settings actually IN settings
|
||||||
follow: true,
|
|
||||||
favorite: true,
|
|
||||||
repost: true,
|
|
||||||
mention: true,
|
|
||||||
poll: true,
|
|
||||||
}),
|
|
||||||
|
|
||||||
quickFilter: ImmutableMap({
|
quickFilter: ImmutableMap({
|
||||||
active: 'all',
|
active: 'all',
|
||||||
show: true,
|
|
||||||
advanced: false,
|
|
||||||
}),
|
|
||||||
|
|
||||||
shows: ImmutableMap({
|
|
||||||
follow: true,
|
|
||||||
favorite: true,
|
|
||||||
repost: true,
|
|
||||||
mention: true,
|
|
||||||
poll: true,
|
|
||||||
}),
|
|
||||||
|
|
||||||
sounds: ImmutableMap({
|
|
||||||
follow: true,
|
|
||||||
favorite: true,
|
|
||||||
repost: true,
|
|
||||||
mention: true,
|
|
||||||
poll: true,
|
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
@ -89,6 +89,10 @@ body {
|
|||||||
white-space: inherit;
|
white-space: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.inheritFill {
|
||||||
|
fill: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
.flexNormal {
|
.flexNormal {
|
||||||
flex-basis: 0%;
|
flex-basis: 0%;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
@ -248,6 +252,10 @@ body {
|
|||||||
cursor: pointer
|
cursor: pointer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cursorNotAllowed {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
.pointerEventsAuto>* {
|
.pointerEventsAuto>* {
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
@ -300,6 +308,14 @@ body {
|
|||||||
background-color: rgba(54, 233, 145, 0.1);
|
background-color: rgba(54, 233, 145, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.backgroundColorBlack {
|
||||||
|
background-color: #3B3B3B;
|
||||||
|
}
|
||||||
|
|
||||||
|
.backgroundColorBlackOpaque_onHover:hover {
|
||||||
|
background-color: rgba(59, 59, 59, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
.backgroundColorOpaque {
|
.backgroundColorOpaque {
|
||||||
background-color: rgba(0, 0, 0, 0.4);
|
background-color: rgba(0, 0, 0, 0.4);
|
||||||
}
|
}
|
||||||
@ -324,6 +340,10 @@ body {
|
|||||||
background-color: #38A16B;
|
background-color: #38A16B;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.backgroundColorDanger {
|
||||||
|
background-color: #DE2960;
|
||||||
|
}
|
||||||
|
|
||||||
.colorPrimary {
|
.colorPrimary {
|
||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
@ -356,6 +376,10 @@ body {
|
|||||||
fill: #fff;
|
fill: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fillColorWhite_onHover:hover {
|
||||||
|
fill: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
.fillColorBrand {
|
.fillColorBrand {
|
||||||
fill: #21cf7a;
|
fill: #21cf7a;
|
||||||
}
|
}
|
||||||
@ -761,6 +785,10 @@ body {
|
|||||||
margin-bottom: -5px;
|
margin-bottom: -5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mt2 {
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
.mr2 {
|
.mr2 {
|
||||||
margin-right: 2px;
|
margin-right: 2px;
|
||||||
}
|
}
|
||||||
@ -909,6 +937,10 @@ body {
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.opacity05 {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
.opacity1 {
|
.opacity1 {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user