Progress
This commit is contained in:
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 IconButton from './icon_button'
|
||||
import Button from './button'
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'bundle_column_error.title', defaultMessage: 'Network error' },
|
||||
@@ -25,7 +25,7 @@ class BundleColumnError extends PureComponent {
|
||||
|
||||
return (
|
||||
<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)}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import IconButton from './icon_button';
|
||||
import Button from './button';
|
||||
|
||||
const messages = defineMessages({
|
||||
error: { id: 'bundle_modal_error.message', defaultMessage: 'Something went wrong while loading this component.' },
|
||||
@@ -29,7 +29,7 @@ class BundleModalError extends PureComponent {
|
||||
return (
|
||||
<div className='modal-root__modal error-modal'>
|
||||
<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)}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -10,8 +10,9 @@ const COLORS = {
|
||||
secondary: 'secondary',
|
||||
tertiary: 'tertiary',
|
||||
white: 'white',
|
||||
black: 'black',
|
||||
brand: 'brand',
|
||||
error: 'error',
|
||||
danger: 'danger',
|
||||
none: 'none',
|
||||
}
|
||||
|
||||
@@ -87,12 +88,11 @@ export default class Button extends PureComponent {
|
||||
<Icon
|
||||
id={icon}
|
||||
width={iconWidth}
|
||||
height={iconWidth}
|
||||
height={iconHeight}
|
||||
className={iconClassName}
|
||||
/>
|
||||
) : undefined
|
||||
|
||||
// : todo :
|
||||
const classes = noClasses ? className : cx(className, {
|
||||
default: 1,
|
||||
noUnderline: 1,
|
||||
@@ -101,18 +101,22 @@ export default class Button extends PureComponent {
|
||||
textAlignCenter: 1,
|
||||
outlineNone: 1,
|
||||
flexRow: !!children && !!icon,
|
||||
cursorNotAllowed: disabled,
|
||||
opacity05: disabled,
|
||||
|
||||
backgroundColorPrimary: backgroundColor === COLORS.white,
|
||||
backgroundColorBlack: backgroundColor === COLORS.black,
|
||||
backgroundColorBrand: backgroundColor === COLORS.brand,
|
||||
backgroundTransparent: backgroundColor === COLORS.none,
|
||||
backgroundSubtle2: backgroundColor === COLORS.tertiary,
|
||||
backgroundSubtle: backgroundColor === COLORS.secondary,
|
||||
|
||||
colorPrimary: color === COLORS.primary,
|
||||
colorSecondary: color === COLORS.secondary,
|
||||
colorTertiary: color === COLORS.tertiary,
|
||||
colorWhite: color === COLORS.white,
|
||||
colorBrand: color === COLORS.brand,
|
||||
backgroundColorDanger: backgroundColor === COLORS.danger,
|
||||
|
||||
colorPrimary: !!children && color === COLORS.primary,
|
||||
colorSecondary: !!children && color === COLORS.secondary,
|
||||
colorTertiary: !!children && color === COLORS.tertiary,
|
||||
colorWhite: !!children && color === COLORS.white,
|
||||
colorBrand: !!children && color === COLORS.brand,
|
||||
|
||||
borderColorBrand: color === COLORS.brand && outline,
|
||||
border1PX: outline,
|
||||
@@ -128,12 +132,16 @@ export default class Button extends PureComponent {
|
||||
|
||||
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,
|
||||
|
||||
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'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import IconButton from './icon_button';
|
||||
import Button from './button';
|
||||
|
||||
const messages = defineMessages({
|
||||
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unhide {domain}' },
|
||||
@@ -30,7 +30,7 @@ class Domain extends PureComponent {
|
||||
</span>
|
||||
|
||||
<div className='domain__buttons'>
|
||||
<IconButton
|
||||
<Button
|
||||
active
|
||||
icon='unlock'
|
||||
title={intl.formatMessage(messages.unblockDomain, {
|
||||
|
||||
@@ -14,9 +14,19 @@ export default class FloatingActionButton extends Component {
|
||||
render() {
|
||||
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 (
|
||||
<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' />
|
||||
[...]
|
||||
</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,
|
||||
readOnly: PropTypes.string,
|
||||
inputRef: PropTypes.func,
|
||||
id: PropTypes.string,
|
||||
hideLabel: PropTypes.bool,
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -36,20 +38,25 @@ export default class Input extends PureComponent {
|
||||
title,
|
||||
small,
|
||||
readOnly,
|
||||
inputRef
|
||||
inputRef,
|
||||
id,
|
||||
hideLabel
|
||||
} = this.props
|
||||
|
||||
const inputClasses = cx({
|
||||
default: 1,
|
||||
text: 1,
|
||||
outlineNone: 1,
|
||||
lineHeight125: 1,
|
||||
lineHeight125: !small,
|
||||
lineHeight1: small,
|
||||
displayBlock: 1,
|
||||
py10: 1,
|
||||
py10: !small,
|
||||
py5: small,
|
||||
backgroundTransparent: !readOnly,
|
||||
backgroundSubtle2: readOnly,
|
||||
colorSecondary: readOnly,
|
||||
fontSize15PX: 1,
|
||||
fontSize15PX: !small,
|
||||
fontSize13PX: small,
|
||||
flexGrow1: 1,
|
||||
circle: 1,
|
||||
px5: !!prependIcon,
|
||||
@@ -57,12 +64,19 @@ export default class Input extends PureComponent {
|
||||
pr15: !hasClear,
|
||||
})
|
||||
|
||||
const titleClasses = cx({
|
||||
default: 1,
|
||||
mb10: 1,
|
||||
pl15: 1,
|
||||
displayNone: hideLabel,
|
||||
})
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{
|
||||
!!title &&
|
||||
<div className={[_s.default, _s.mb10, _s.pl15].join(' ')}>
|
||||
<Text size='small' weight='medium' color='secondary'>
|
||||
<div className={titleClasses}>
|
||||
<Text htmlFor={id} size='small' weight='medium' color='secondary' tagName='label'>
|
||||
{title}
|
||||
</Text>
|
||||
</div>
|
||||
@@ -74,6 +88,7 @@ export default class Input extends PureComponent {
|
||||
}
|
||||
|
||||
<input
|
||||
id={id}
|
||||
className={inputClasses}
|
||||
type='text'
|
||||
placeholder={placeholder}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { decode } from 'blurhash';
|
||||
import { autoPlayGif, displayMedia } from '../../initial_state';
|
||||
import { isIOS } from '../../utils/is_mobile';
|
||||
import { isPanoramic, isPortrait, isNonConformingRatio, minimumAspectRatio, maximumAspectRatio } from '../../utils/media_aspect_ratio';
|
||||
import IconButton from '../icon_button';
|
||||
import Button from '../button';
|
||||
|
||||
const messages = defineMessages({
|
||||
toggle_visible: { id: 'media_gallery.toggle_visible', defaultMessage: 'Toggle visibility' },
|
||||
@@ -503,7 +503,7 @@ class MediaGallery extends PureComponent {
|
||||
));
|
||||
|
||||
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 {
|
||||
spoilerButton = (
|
||||
<button type='button' onClick={this.handleOpen} className='spoiler-button__overlay'>
|
||||
|
||||
@@ -5,7 +5,7 @@ import StatusContent from '../status_content';
|
||||
import Avatar from '../avatar';
|
||||
import RelativeTimestamp from '../relative_timestamp';
|
||||
import DisplayName from '../display_name';
|
||||
import IconButton from '../icon_button';
|
||||
import Button from '../button';
|
||||
|
||||
export default class ActionsModal extends ImmutablePureComponent {
|
||||
|
||||
@@ -32,7 +32,7 @@ export default class ActionsModal extends ImmutablePureComponent {
|
||||
className={classNames({ active })}
|
||||
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 className={classNames({ 'actions-modal__item-label': !!meta })}>{text}</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 Video from '../../features/video';
|
||||
import ExtendedVideoPlayer from '../extended_video_player';
|
||||
import IconButton from '../icon_button';
|
||||
import Button from '../button';
|
||||
import ImageLoader from '../image_loader';
|
||||
import Icon from '../icon';
|
||||
|
||||
@@ -221,7 +221,7 @@ class MediaModal extends ImmutablePureComponent {
|
||||
</div>
|
||||
|
||||
<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}
|
||||
{rightNav}
|
||||
|
||||
@@ -23,6 +23,7 @@ import ConfirmationModal from './confirmation_modal'
|
||||
import GroupCreateModal from './group_create_modal'
|
||||
import GroupDeleteModal from './group_delete_modal'
|
||||
import GroupEditorModal from './group_editor_modal'
|
||||
import HashtagTimelineSettingsModal from './hashtag_timeline_settings_modal'
|
||||
import HomeTimelineSettingsModal from './home_timeline_settings_modal'
|
||||
import HotkeysModal from './hotkeys_modal'
|
||||
import ListCreateModal from './list_create_modal'
|
||||
@@ -48,6 +49,7 @@ const MODAL_COMPONENTS = {
|
||||
GROUP_CREATE: () => Promise.resolve({ default: GroupCreateModal }),
|
||||
GROUP_DELETE: () => Promise.resolve({ default: GroupDeleteModal }),
|
||||
GROUP_EDITOR: () => Promise.resolve({ default: GroupEditorModal }),
|
||||
HASHTAG_TIMELINE_SETTINGS: () => Promise.resolve({ default: HashtagTimelineSettingsModal }),
|
||||
HOME_TIMELINE_SETTINGS: () => Promise.resolve({ default: HomeTimelineSettingsModal }),
|
||||
HOTKEYS: () => Promise.resolve({ default: HotkeysModal }),
|
||||
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 ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import StatusRevisionListContainer from '../../containers/status_revision_list_container'
|
||||
import IconButton from '../icon_button'
|
||||
import Button from '../button'
|
||||
|
||||
const messages = defineMessages({
|
||||
close: { id: 'lightbox.close', defaultMessage: 'Close' },
|
||||
@@ -28,7 +28,7 @@ class StatusRevisionModal extends ImmutablePureComponent {
|
||||
<h3 className='status-revisions__header__title'>
|
||||
<FormattedMessage id='status_revisions.heading' defaultMessage='Revision History' />
|
||||
</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 className='status-revisions__content'>
|
||||
<StatusRevisionListContainer id={status.get('id')} />
|
||||
|
||||
@@ -12,7 +12,11 @@ export default class ProgressPanel extends PureComponent {
|
||||
subtitle="We are 100% funded by you"
|
||||
hasBackground
|
||||
>
|
||||
<ProgressBar progress={monthlyExpensesComplete}/>
|
||||
<ProgressBar
|
||||
progress={monthlyExpensesComplete}
|
||||
title={`${Math.min(parseFloat(monthlyExpensesComplete), 100)}% covered this month`}
|
||||
href='https://shop.dissenter.com/category/donations'
|
||||
/>
|
||||
</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 List from '../list'
|
||||
|
||||
export default class StatusOptionsPopover extends PureComponent {
|
||||
_makeMenu = (publicStatus) => {
|
||||
// const { status, intl: { formatMessage }, withDismiss, withGroupAdmin } = this.props
|
||||
// const mutingConversation = status.get('muted')
|
||||
const messages = defineMessages({
|
||||
delete: { id: 'status.delete', defaultMessage: 'Delete' },
|
||||
edit: { id: 'status.edit', defaultMessage: 'Edit' },
|
||||
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 = [];
|
||||
|
||||
// menu.push({ text: formatMessage(messages.open), action: this.handleOpen });
|
||||
menu.push({
|
||||
icon: 'circle',
|
||||
hideArrow: true,
|
||||
title: formatMessage(messages.open),
|
||||
onClick: this.handleOpen
|
||||
});
|
||||
|
||||
// if (publicStatus) {
|
||||
// menu.push({ text: formatMessage(messages.copy), action: this.handleCopy });
|
||||
// menu.push({ text: formatMessage(messages.embed), action: this.handleEmbed });
|
||||
// }
|
||||
if (publicStatus) {
|
||||
menu.push({
|
||||
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) {
|
||||
// menu.push({ text: formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation), action: this.handleConversationMuteClick });
|
||||
// menu.push(null);
|
||||
// }
|
||||
if (status.getIn(['account', 'id']) === me) {
|
||||
if (publicStatus) {
|
||||
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 (publicStatus) {
|
||||
// menu.push({ text: formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick });
|
||||
// } 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) {
|
||||
menu.push({ text: formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }), href: `/admin/accounts/${status.getIn(['account', 'id'])}` });
|
||||
menu.push({ text: formatMessage(messages.admin_status), href: `/admin/accounts/${status.getIn(['account', 'id'])}/statuses/${status.get('id')}` });
|
||||
}
|
||||
|
||||
// if (isStaff) {
|
||||
// menu.push(null);
|
||||
// menu.push({ text: formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }), href: `/admin/accounts/${status.getIn(['account', 'id'])}` });
|
||||
// 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 });
|
||||
// }
|
||||
// }
|
||||
if (withGroupAdmin) {
|
||||
menu.push({ text: formatMessage(messages.group_remove_account), action: this.handleGroupRemoveAccount });
|
||||
menu.push({ text: formatMessage(messages.group_remove_post), action: this.handleGroupRemovePost });
|
||||
}
|
||||
}
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
render() {
|
||||
const items = this.getItems()
|
||||
|
||||
return (
|
||||
<PopoverLayout className={_s.width240PX}>
|
||||
<List
|
||||
scrollKey='profile_options'
|
||||
items={[
|
||||
{
|
||||
title: 'Help',
|
||||
href: 'https://help.gab.com',
|
||||
},
|
||||
{
|
||||
title: 'Settings',
|
||||
href: '/settings',
|
||||
},
|
||||
{
|
||||
title: 'Log Out',
|
||||
href: '/auth/log_out',
|
||||
},
|
||||
]}
|
||||
items={items}
|
||||
small
|
||||
/>
|
||||
</PopoverLayout>
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
import { injectIntl, defineMessages } from 'react-intl'
|
||||
import spring from 'react-motion/lib/spring'
|
||||
import detectPassiveEvents from 'detect-passive-events'
|
||||
import classNames from 'classnames'
|
||||
import Overlay from 'react-overlays/lib/Overlay'
|
||||
import { changeComposeVisibility } from '../../actions/compose'
|
||||
import { openModal, closeModal } from '../../actions/modal'
|
||||
import { isUserTouching } from '../../utils/is_mobile'
|
||||
import Motion from '../../features/ui/util/optional_motion'
|
||||
import Icon from '../icon'
|
||||
import ComposeExtraButton from '../../features/compose/components/compose_extra_button'
|
||||
|
||||
const messages = defineMessages({
|
||||
public_short: { id: 'privacy.public.short', defaultMessage: 'Public' },
|
||||
public_long: { id: 'privacy.public.long', defaultMessage: 'Post to public timelines' },
|
||||
unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' },
|
||||
unlisted_long: { id: 'privacy.unlisted.long', defaultMessage: 'Do not show in public timelines' },
|
||||
private_short: { id: 'privacy.private.short', defaultMessage: 'Followers-only' },
|
||||
private_long: { id: 'privacy.private.long', defaultMessage: 'Post to followers only' },
|
||||
change_privacy: { id: 'privacy.change', defaultMessage: 'Adjust status privacy' },
|
||||
visibility: { id: 'privacy.visibility', defaultMessage: 'Visibility' },
|
||||
})
|
||||
|
||||
const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false
|
||||
|
||||
class PrivacyDropdownMenu extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
style: PropTypes.object,
|
||||
items: PropTypes.array.isRequired,
|
||||
value: PropTypes.string.isRequired,
|
||||
placement: PropTypes.string.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
state = {
|
||||
mounted: false,
|
||||
}
|
||||
|
||||
handleDocumentClick = e => {
|
||||
if (this.node && !this.node.contains(e.target)) {
|
||||
this.props.onClose()
|
||||
}
|
||||
}
|
||||
|
||||
handleKeyDown = e => {
|
||||
const { items } = this.props
|
||||
const value = e.currentTarget.getAttribute('data-index')
|
||||
const index = items.findIndex(item => {
|
||||
return (item.value === value)
|
||||
})
|
||||
let element
|
||||
|
||||
switch(e.key) {
|
||||
case 'Escape':
|
||||
this.props.onClose()
|
||||
break
|
||||
case 'Enter':
|
||||
this.handleClick(e)
|
||||
break
|
||||
case 'ArrowDown':
|
||||
element = this.node.childNodes[index + 1]
|
||||
if (element) {
|
||||
element.focus()
|
||||
this.props.onChange(element.getAttribute('data-index'))
|
||||
}
|
||||
break
|
||||
case 'ArrowUp':
|
||||
element = this.node.childNodes[index - 1]
|
||||
if (element) {
|
||||
element.focus()
|
||||
this.props.onChange(element.getAttribute('data-index'))
|
||||
}
|
||||
break
|
||||
case 'Home':
|
||||
element = this.node.firstChild
|
||||
if (element) {
|
||||
element.focus()
|
||||
this.props.onChange(element.getAttribute('data-index'))
|
||||
}
|
||||
break
|
||||
case 'End':
|
||||
element = this.node.lastChild
|
||||
if (element) {
|
||||
element.focus()
|
||||
this.props.onChange(element.getAttribute('data-index'))
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
handleClick = e => {
|
||||
const value = e.currentTarget.getAttribute('data-index')
|
||||
|
||||
e.preventDefault()
|
||||
|
||||
this.props.onClose()
|
||||
this.props.onChange(value)
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
document.addEventListener('click', this.handleDocumentClick, false)
|
||||
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions)
|
||||
if (this.focusedItem) this.focusedItem.focus()
|
||||
this.setState({ mounted: true })
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
document.removeEventListener('click', this.handleDocumentClick, false)
|
||||
document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions)
|
||||
}
|
||||
|
||||
setRef = c => {
|
||||
this.node = c
|
||||
}
|
||||
|
||||
setFocusRef = c => {
|
||||
this.focusedItem = c
|
||||
}
|
||||
|
||||
render () {
|
||||
const { mounted } = this.state
|
||||
const { style, items, placement, value } = this.props
|
||||
|
||||
return (
|
||||
<Motion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
|
||||
{({ opacity, scaleX, scaleY }) => (
|
||||
// It should not be transformed when mounting because the resulting
|
||||
// size will be used to determine the coordinate of the menu by
|
||||
// react-overlays
|
||||
<div className={`privacy-dropdown__dropdown ${placement}`} style={{ ...style, opacity: opacity, transform: mounted ? `scale(${scaleX}, ${scaleY})` : null }} role='listbox' ref={this.setRef}>
|
||||
{items.map(item => (
|
||||
<div role='option' tabIndex='0' key={item.value} data-index={item.value} onKeyDown={this.handleKeyDown} onClick={this.handleClick} className={classNames('privacy-dropdown__option', { active: item.value === value })} aria-selected={item.value === value} ref={item.value === value ? this.setFocusRef : null}>
|
||||
<div className='privacy-dropdown__option__icon'>
|
||||
<Icon id={item.icon} fixedWidth />
|
||||
</div>
|
||||
|
||||
<div className='privacy-dropdown__option__content'>
|
||||
<strong>{item.text}</strong>
|
||||
{item.meta}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</Motion>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
isModalOpen: state.get('modal').modalType === 'ACTIONS',
|
||||
value: state.getIn(['compose', 'privacy']),
|
||||
})
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
|
||||
onChange (value) {
|
||||
dispatch(changeComposeVisibility(value))
|
||||
},
|
||||
|
||||
isUserTouching,
|
||||
onModalOpen: props => dispatch(openModal('ACTIONS', props)),
|
||||
onModalClose: () => dispatch(closeModal()),
|
||||
|
||||
})
|
||||
|
||||
export default
|
||||
@connect(mapStateToProps, mapDispatchToProps)
|
||||
@injectIntl
|
||||
class PrivacyDropdown extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
isUserTouching: PropTypes.func,
|
||||
isModalOpen: PropTypes.bool.isRequired,
|
||||
onModalOpen: PropTypes.func,
|
||||
onModalClose: PropTypes.func,
|
||||
value: PropTypes.string.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
state = {
|
||||
open: false,
|
||||
placement: 'bottom',
|
||||
}
|
||||
|
||||
handleToggle = ({ target }) => {
|
||||
if (this.props.isUserTouching()) {
|
||||
if (this.state.open) {
|
||||
this.props.onModalClose()
|
||||
} else {
|
||||
this.props.onModalOpen({
|
||||
actions: this.options.map(option => ({ ...option, active: option.value === this.props.value })),
|
||||
onClick: this.handleModalActionClick,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
const { top } = target.getBoundingClientRect()
|
||||
this.setState({ placement: top * 2 < innerHeight ? 'bottom' : 'top' })
|
||||
this.setState({ open: !this.state.open })
|
||||
}
|
||||
}
|
||||
|
||||
handleModalActionClick = (e) => {
|
||||
e.preventDefault()
|
||||
|
||||
const { value } = this.options[e.currentTarget.getAttribute('data-index')]
|
||||
|
||||
this.props.onModalClose()
|
||||
this.props.onChange(value)
|
||||
}
|
||||
|
||||
handleKeyDown = e => {
|
||||
switch(e.key) {
|
||||
case 'Escape':
|
||||
this.handleClose()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
handleClose = () => {
|
||||
this.setState({ open: false })
|
||||
}
|
||||
|
||||
handleChange = value => {
|
||||
this.props.onChange(value)
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
const { intl: { formatMessage } } = this.props
|
||||
|
||||
this.options = [
|
||||
{ icon: 'globe', value: 'public', text: formatMessage(messages.public_short), meta: formatMessage(messages.public_long) },
|
||||
{ icon: 'unlock', value: 'unlisted', text: formatMessage(messages.unlisted_short), meta: formatMessage(messages.unlisted_long) },
|
||||
{ icon: 'lock', value: 'private', text: formatMessage(messages.private_short), meta: formatMessage(messages.private_long) },
|
||||
]
|
||||
}
|
||||
|
||||
render () {
|
||||
const { value, intl } = this.props
|
||||
const { open, placement } = this.state
|
||||
|
||||
const valueOption = this.options.find(item => item.value === value)
|
||||
|
||||
return (
|
||||
<PrivacyDropdownMenu
|
||||
items={this.options}
|
||||
value={value}
|
||||
onClose={this.handleClose}
|
||||
onChange={this.handleChange}
|
||||
placement={placement}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -105,14 +105,11 @@ class ProfileHeader extends ImmutablePureComponent {
|
||||
this.props.onFollow(this.props.account)
|
||||
}
|
||||
|
||||
handleUnrequest = () => {
|
||||
//
|
||||
}
|
||||
|
||||
handleBlock = () => {
|
||||
// this.props.onBlock(this.props.account)
|
||||
this.props.onBlock(this.props.account)
|
||||
}
|
||||
|
||||
// : todo :
|
||||
makeInfo() {
|
||||
const { account, intl } = this.props;
|
||||
|
||||
@@ -135,28 +132,6 @@ class ProfileHeader extends ImmutablePureComponent {
|
||||
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) => {
|
||||
this.openMoreNode = n
|
||||
}
|
||||
@@ -195,37 +170,78 @@ class ProfileHeader extends ImmutablePureComponent {
|
||||
|
||||
const avatarSize = headerMissing ? '75' : '150'
|
||||
|
||||
let buttonText;
|
||||
let buttonOptions;
|
||||
let buttonText = ''
|
||||
let buttonOptions = {}
|
||||
|
||||
if (!account) {
|
||||
console.log("no account")
|
||||
}
|
||||
else {
|
||||
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.handleUnrequest : 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)
|
||||
if (!account.get('relationship')) {
|
||||
console.log("no relationship")
|
||||
// Wait until the relationship is loaded
|
||||
} else if (account.getIn(['relationship', 'requested'])) {
|
||||
buttonText = intl.formatMessage(messages.requested)
|
||||
buttonOptions = {
|
||||
narrow: true,
|
||||
onClick: this.handleFollow,
|
||||
color: 'primary',
|
||||
backgroundColor: 'tertiary',
|
||||
}
|
||||
} 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 (
|
||||
<div className={[_s.default, _s.z1, _s.width100PC].join(' ')}>
|
||||
|
||||
@@ -287,7 +303,7 @@ class ProfileHeader extends ImmutablePureComponent {
|
||||
icon='ellipsis'
|
||||
iconWidth='18px'
|
||||
iconHeight='18px'
|
||||
iconClassName={_s.fillColorBrand}
|
||||
iconClassName={_s.inheritFill}
|
||||
color='brand'
|
||||
backgroundColor='none'
|
||||
className={[_s.justifyContentCenter, _s.alignItemsCenter, _s.mr10, _s.px10].join(' ')}
|
||||
@@ -302,7 +318,7 @@ class ProfileHeader extends ImmutablePureComponent {
|
||||
icon='chat'
|
||||
iconWidth='18px'
|
||||
iconHeight='18px'
|
||||
iconClassName={_s.fillColorBrand}
|
||||
iconClassName={_s.inheritFill}
|
||||
color='brand'
|
||||
backgroundColor='none'
|
||||
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'
|
||||
|
||||
const cx = classNames.bind(_s)
|
||||
|
||||
export default class ProgressBar extends PureComponent {
|
||||
static propTypes = {
|
||||
progress: PropTypes.number,
|
||||
small: PropTypes.bool,
|
||||
title: PropTypes.string,
|
||||
}
|
||||
|
||||
render() {
|
||||
const { progress } = this.props
|
||||
const {
|
||||
progress,
|
||||
small,
|
||||
title,
|
||||
href
|
||||
} = this.props
|
||||
|
||||
const completed = Math.min(parseFloat(progress), 100)
|
||||
const style = {
|
||||
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 (
|
||||
<a href='https://shop.dissenter.com/category/donations' className={[_s.default, _s.backgroundPanel, _s.noUnderline, _s.circle, _s.overflowHidden, _s.height22PX, _s.cursorPointer].join(' ')}>
|
||||
<div className={[_s.default, _s.backgroundColorBrandDark, _s.circle, _s.height22PX].join(' ')} style={style} />
|
||||
<div className={[_s.default, _s.positionAbsolute, _s.width100PC, _s.height22PX, _s.alignItemsCenter, _s.justifyContentCenter].join(' ')}>
|
||||
<Text size='small' weight='bold' color='white'>
|
||||
{title}
|
||||
</Text>
|
||||
<Button
|
||||
noClasses
|
||||
{...containerOptions}
|
||||
>
|
||||
<div className={[_s.default, _s.backgroundColorBrandDark, _s.circle, _s.height100PC].join(' ')} style={style} />
|
||||
<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>
|
||||
</a>
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -149,7 +149,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
const favoriteCount = status.get('favourites_count') // : todo :
|
||||
|
||||
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
|
||||
@@ -189,7 +189,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
{
|
||||
favoriteCount > 0 &&
|
||||
<button className={interactionBtnClasses}>
|
||||
<Text color='secondary'>
|
||||
<Text color='secondary' size='small'>
|
||||
{favoriteCount}
|
||||
Likes
|
||||
</Text>
|
||||
@@ -198,7 +198,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
{
|
||||
replyCount > 0 &&
|
||||
<button className={interactionBtnClasses}>
|
||||
<Text color='secondary'>
|
||||
<Text color='secondary' size='small'>
|
||||
{replyCount}
|
||||
Comments
|
||||
</Text>
|
||||
@@ -207,7 +207,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
{
|
||||
repostCount > 0 &&
|
||||
<button className={interactionBtnClasses}>
|
||||
<Text color='secondary'>
|
||||
<Text color='secondary' size='small'>
|
||||
{repostCount}
|
||||
Reposts
|
||||
</Text>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { Fragment } from 'react';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { FormattedMessage, injectIntl, defineMessages } from 'react-intl';
|
||||
import { Fragment } from 'react'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import { FormattedMessage, injectIntl, defineMessages } from 'react-intl'
|
||||
import classNames from 'classnames/bind'
|
||||
import { isRtl } from '../../utils/rtl';
|
||||
import { isRtl } from '../../utils/rtl'
|
||||
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({
|
||||
showMore: { id: 'status.show_more', defaultMessage: 'Show more' },
|
||||
@@ -23,7 +24,7 @@ class StatusContent extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
status: ImmutablePropTypes.map.isRequired,
|
||||
@@ -34,36 +35,36 @@ class StatusContent extends ImmutablePureComponent {
|
||||
collapsable: PropTypes.bool,
|
||||
intl: PropTypes.object.isRequired,
|
||||
isComment: PropTypes.bool,
|
||||
};
|
||||
}
|
||||
|
||||
state = {
|
||||
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 () {
|
||||
const node = this.node;
|
||||
_updateStatusLinks() {
|
||||
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) {
|
||||
let link = links[i];
|
||||
let link = links[i]
|
||||
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) {
|
||||
link.addEventListener('click', this.onMentionClick.bind(this, mention), false);
|
||||
link.setAttribute('title', mention.get('acct'));
|
||||
link.addEventListener('click', this.onMentionClick.bind(this, mention), false)
|
||||
link.setAttribute('title', mention.get('acct'))
|
||||
} 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 {
|
||||
link.setAttribute('title', link.href);
|
||||
link.setAttribute('title', link.href)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,59 +75,59 @@ class StatusContent extends ImmutablePureComponent {
|
||||
&& node.clientHeight > MAX_HEIGHT
|
||||
&& this.props.status.get('spoiler_text').length === 0
|
||||
) {
|
||||
this.setState({ collapsed: true });
|
||||
this.setState({ collapsed: true })
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this._updateStatusLinks();
|
||||
componentDidMount() {
|
||||
this._updateStatusLinks()
|
||||
}
|
||||
|
||||
componentDidUpdate () {
|
||||
this._updateStatusLinks();
|
||||
componentDidUpdate() {
|
||||
this._updateStatusLinks()
|
||||
}
|
||||
|
||||
onMentionClick = (mention, e) => {
|
||||
if (this.context.router && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
||||
e.preventDefault();
|
||||
this.context.router.history.push(`/${mention.get('acct')}`);
|
||||
e.preventDefault()
|
||||
this.context.router.history.push(`/${mention.get('acct')}`)
|
||||
}
|
||||
}
|
||||
|
||||
onHashtagClick = (hashtag, e) => {
|
||||
hashtag = hashtag.replace(/^#/, '').toLowerCase();
|
||||
hashtag = hashtag.replace(/^#/, '').toLowerCase()
|
||||
|
||||
if (this.context.router && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
||||
e.preventDefault();
|
||||
this.context.router.history.push(`/tags/${hashtag}`);
|
||||
e.preventDefault()
|
||||
this.context.router.history.push(`/tags/${hashtag}`)
|
||||
}
|
||||
}
|
||||
|
||||
handleMouseDown = (e) => {
|
||||
this.startXY = [e.clientX, e.clientY];
|
||||
this.startXY = [e.clientX, e.clientY]
|
||||
}
|
||||
|
||||
handleMouseUp = (e) => {
|
||||
if (!this.startXY) return;
|
||||
if (!this.startXY) return
|
||||
|
||||
const [ startX, startY ] = this.startXY;
|
||||
const [ deltaX, deltaY ] = [Math.abs(e.clientX - startX), Math.abs(e.clientY - startY)];
|
||||
const [startX, startY] = this.startXY
|
||||
const [deltaX, deltaY] = [Math.abs(e.clientX - startX), Math.abs(e.clientY - startY)]
|
||||
|
||||
if (e.target.localName === 'button' ||
|
||||
e.target.localName === 'a' ||
|
||||
(e.target.parentNode && (e.target.parentNode.localName === 'button' || e.target.parentNode.localName === 'a'))) {
|
||||
return;
|
||||
e.target.localName === 'a' ||
|
||||
(e.target.parentNode && (e.target.parentNode.localName === 'button' || e.target.parentNode.localName === 'a'))) {
|
||||
return
|
||||
}
|
||||
|
||||
if (deltaX + deltaY < 5 && e.button === 0 && this.props.onClick) {
|
||||
this.props.onClick();
|
||||
this.props.onClick()
|
||||
}
|
||||
|
||||
this.startXY = null;
|
||||
this.startXY = null
|
||||
}
|
||||
|
||||
handleSpoilerClick = (e) => {
|
||||
e.preventDefault();
|
||||
e.preventDefault()
|
||||
|
||||
if (this.props.onExpandedToggle) {
|
||||
// The parent manages the state
|
||||
@@ -137,77 +138,125 @@ class StatusContent extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
handleReadMore = (e) => {
|
||||
e.preventDefault();
|
||||
this.setState({ collapsed: false });
|
||||
e.preventDefault()
|
||||
this.setState({ collapsed: false })
|
||||
}
|
||||
|
||||
setRef = (c) => {
|
||||
this.node = c;
|
||||
this.node = c
|
||||
}
|
||||
|
||||
getHtmlContent = () => {
|
||||
const { status, reblogContent } = this.props;
|
||||
const { status, reblogContent } = this.props
|
||||
|
||||
const properContent = status.get('contentHtml');
|
||||
const properContent = status.get('contentHtml')
|
||||
|
||||
return reblogContent
|
||||
? `${reblogContent} <div class='status__quote'>${properContent}</div>`
|
||||
: properContent;
|
||||
: properContent
|
||||
}
|
||||
|
||||
render () {
|
||||
render() {
|
||||
const { status, intl, isComment } = this.props
|
||||
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 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';
|
||||
const content = { __html: this.getHtmlContent() }
|
||||
const spoilerContent = { __html: status.get('spoilerHtml') }
|
||||
const directionStyle = {
|
||||
direction: isRtl(status.get('search_index')) ? 'rtl' : 'ltr',
|
||||
}
|
||||
|
||||
if (status.get('spoiler_text').length > 0) {
|
||||
let mentionsPlaceholder = '';
|
||||
let mentionsPlaceholder = null
|
||||
|
||||
const mentionLinks = status.get('mentions').map(item => (
|
||||
<Button to={`/${item.get('acct')}`} href={`/${item.get('acct')}`} key={item.get('id')} className='mention'>
|
||||
@<span>{item.get('username')}</span>
|
||||
<Button
|
||||
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>
|
||||
)).reduce((aggregate, item) => [...aggregate, item, ' '], []);
|
||||
|
||||
const toggleText = intl.formatMessage(hidden ? messages.showMore : messages.showLess);
|
||||
)).reduce((aggregate, item) => [...aggregate, item, ' '], [])
|
||||
|
||||
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 (
|
||||
<div className={[].join(' ')} ref={this.setRef} tabIndex='0' style={directionStyle} onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp}>
|
||||
<p style={{ marginBottom: hidden && status.get('mentions').isEmpty() ? '0px' : null }}>
|
||||
<span dangerouslySetInnerHTML={spoilerContent} lang={status.get('language')} />
|
||||
{' '}
|
||||
<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>
|
||||
</p>
|
||||
<div
|
||||
className={[].join(' ')}
|
||||
ref={this.setRef}
|
||||
tabIndex='0'
|
||||
className={[_s.px15, _s.statusContent].join(' ')}
|
||||
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}
|
||||
|
||||
<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>
|
||||
);
|
||||
)
|
||||
} else if (this.props.onClick) {
|
||||
const hasMarginBottom = !!status.get('card') || !!status.get('poll') || status.get('media_attachments').size > 0
|
||||
|
||||
@@ -256,7 +305,7 @@ class StatusContent extends ImmutablePureComponent {
|
||||
dangerouslySetInnerHTML={content}
|
||||
lang={status.get('language')}
|
||||
/>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,24 +12,30 @@ import Icon from './icon'
|
||||
import Button from './button'
|
||||
import Avatar from './avatar'
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
onOpenStatusOptionsPopover(targetRef, status) {
|
||||
dispatch(openPopover('STATUS_OPTIONS', {
|
||||
targetRef,
|
||||
status,
|
||||
position: 'top',
|
||||
}))
|
||||
},
|
||||
})
|
||||
|
||||
export default
|
||||
@connect(null, null)
|
||||
@connect(null, mapDispatchToProps)
|
||||
class StatusHeader extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
status: ImmutablePropTypes.map,
|
||||
onOpenStatusOptionsPopover: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
handleStatusOptionsClick() {
|
||||
console.log("handleStatusOptionsClick:", this.props)
|
||||
this.props.dispatch(openPopover('STATUS_OPTIONS', {
|
||||
targetRef: this.statusOptionsButton,
|
||||
position: 'top',
|
||||
status: this.props.status,
|
||||
}))
|
||||
handleOpenStatusOptionsPopover = () => {
|
||||
this.props.onOpenStatusOptionsPopover(this.statusOptionsButton, this.props.status)
|
||||
}
|
||||
|
||||
handleOpenStatusEdits() {
|
||||
handleOpenStatusEdits = () => {
|
||||
// : todo :
|
||||
this.props.dispatch(openPopover('REPOST', {
|
||||
targetRef: this.statusOptionsButton,
|
||||
@@ -150,7 +156,7 @@ class StatusHeader extends ImmutablePureComponent {
|
||||
iconHeight='20px'
|
||||
iconClassName={_s.fillColorSecondary}
|
||||
className={_s.marginLeftAuto}
|
||||
onClick={this.handleStatusOptionsClick}
|
||||
onClick={this.handleOpenStatusOptionsPopover}
|
||||
buttonRef={this.setStatusOptionsButton}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -54,7 +54,7 @@ export default class Switch extends PureComponent {
|
||||
|
||||
return (
|
||||
<div className={[_s.default, _s.flexRow, _s.py5, _s.alignItemsCenter].join(' ')}>
|
||||
<Text {...labelProps}>
|
||||
<Text {...labelProps} className={_s.mr10}>
|
||||
{label}
|
||||
</Text>
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { NavLink, withRouter } from 'react-router-dom'
|
||||
import { withRouter } from 'react-router-dom'
|
||||
import classNames from 'classnames/bind'
|
||||
import Button from './button'
|
||||
import Icon from './icon'
|
||||
import Text from './text'
|
||||
|
||||
const cx = classNames.bind(_s)
|
||||
@@ -10,8 +12,11 @@ class TabBarItem extends PureComponent {
|
||||
static propTypes = {
|
||||
location: PropTypes.object.isRequired,
|
||||
title: PropTypes.string,
|
||||
onClick: PropTypes.func,
|
||||
icon: PropTypes.string,
|
||||
to: PropTypes.string,
|
||||
large: PropTypes.bool,
|
||||
active: PropTypes.bool,
|
||||
}
|
||||
|
||||
state = {
|
||||
@@ -31,7 +36,15 @@ class TabBarItem extends PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { title, to, location, large } = this.props
|
||||
const {
|
||||
title,
|
||||
to,
|
||||
onClick,
|
||||
location,
|
||||
large,
|
||||
icon,
|
||||
// active
|
||||
} = this.props
|
||||
const { active } = this.state
|
||||
|
||||
const isCurrent = active === -1 ? to === location.pathname : active
|
||||
@@ -46,6 +59,7 @@ class TabBarItem extends PureComponent {
|
||||
justifyContentCenter: 1,
|
||||
borderBottom2PX: 1,
|
||||
py5: 1,
|
||||
backgroundTransparent: 1,
|
||||
borderColorTransparent: !isCurrent,
|
||||
borderColorBrand: isCurrent,
|
||||
mr5: large,
|
||||
@@ -68,14 +82,31 @@ class TabBarItem extends PureComponent {
|
||||
weight: isCurrent ? 'bold' : large ? 'medium' : 'normal',
|
||||
}
|
||||
|
||||
const iconOptions = {
|
||||
id: icon,
|
||||
width: !!large ? 20 : 14,
|
||||
height: !!large ? 20 : 14,
|
||||
}
|
||||
|
||||
return (
|
||||
<NavLink to={to} className={containerClasses}>
|
||||
<Button
|
||||
onClick={onClick}
|
||||
to={to}
|
||||
className={containerClasses}
|
||||
noClasses
|
||||
>
|
||||
<span className={textParentClasses}>
|
||||
{ !!title &&
|
||||
<Text {...textOptions}>
|
||||
{title}
|
||||
</Text>
|
||||
}
|
||||
|
||||
{ !!icon &&
|
||||
<Icon {...iconOptions} />
|
||||
}
|
||||
</span>
|
||||
</NavLink>
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,7 @@ export default class Text extends PureComponent {
|
||||
weight: PropTypes.oneOf(Object.keys(WEIGHTS)),
|
||||
align: PropTypes.oneOf(Object.keys(ALIGNMENTS)),
|
||||
underline: PropTypes.bool,
|
||||
htmlFor: PropTypes.string,
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
@@ -60,7 +61,8 @@ export default class Text extends PureComponent {
|
||||
size,
|
||||
weight,
|
||||
underline,
|
||||
align
|
||||
align,
|
||||
htmlFor
|
||||
} = this.props
|
||||
|
||||
const classes = cx(className, {
|
||||
@@ -93,6 +95,7 @@ export default class Text extends PureComponent {
|
||||
return React.createElement(
|
||||
tagName,
|
||||
{
|
||||
htmlFor,
|
||||
className: classes,
|
||||
},
|
||||
children,
|
||||
|
||||
Reference in New Issue
Block a user