This commit is contained in:
mgabdev 2020-02-08 01:12:01 -05:00
parent fa66d082f8
commit 1c98dd283e
146 changed files with 1462 additions and 1951 deletions

View File

@ -3,7 +3,6 @@
import Rails from 'rails-ujs';
export function start() {
require('font-awesome/css/font-awesome.css');
require.context('../images/', true);
try {

View File

@ -1,14 +1,13 @@
import { Fragment } from 'react';
import { Link } from 'react-router-dom';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { defineMessages, injectIntl } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { me } from '../../initial_state';
import Avatar from '../avatar/avatar';
import DisplayName from '../display_name';
import IconButton from '../icon_button';
import './account.scss';
import { Fragment } from 'react'
import { NavLink } from 'react-router-dom'
import ImmutablePropTypes from 'react-immutable-proptypes'
import { defineMessages, injectIntl } from 'react-intl'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { me } from '../../initial_state'
import Avatar from '../avatar/avatar'
import DisplayName from '../display_name'
import IconButton from '../icon_button'
import Icon from '../icon'
const messages = defineMessages({
follow: { id: 'account.follow', defaultMessage: 'Follow' },
@ -18,7 +17,7 @@ const messages = defineMessages({
unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
mute_notifications: { id: 'account.mute_notifications', defaultMessage: 'Mute notifications from @{name}' },
unmute_notifications: { id: 'account.unmute_notifications', defaultMessage: 'Unmute notifications from @{name}' },
});
})
export default @injectIntl
class Account extends ImmutablePureComponent {
@ -34,37 +33,36 @@ class Account extends ImmutablePureComponent {
actionIcon: PropTypes.string,
actionTitle: PropTypes.string,
onActionClick: PropTypes.func,
displayOnly: PropTypes.bool,
};
}
handleFollow = () => {
this.props.onFollow(this.props.account);
this.props.onFollow(this.props.account)
}
handleBlock = () => {
this.props.onBlock(this.props.account);
this.props.onBlock(this.props.account)
}
handleMute = () => {
this.props.onMute(this.props.account);
this.props.onMute(this.props.account)
}
handleMuteNotifications = () => {
this.props.onMuteNotifications(this.props.account, true);
this.props.onMuteNotifications(this.props.account, true)
}
handleUnmuteNotifications = () => {
this.props.onMuteNotifications(this.props.account, false);
this.props.onMuteNotifications(this.props.account, false)
}
handleAction = () => {
this.props.onActionClick(this.props.account);
this.props.onActionClick(this.props.account)
}
render() {
const { account, intl, hidden, onActionClick, actionIcon, actionTitle, displayOnly } = this.props;
const { account, intl, hidden, onActionClick, actionIcon, actionTitle } = this.props
if (!account) return null;
if (!account) return null
if (hidden) {
return (
@ -72,29 +70,29 @@ class Account extends ImmutablePureComponent {
{account.get('display_name')}
{account.get('username')}
</Fragment>
);
)
}
let buttons;
let buttons
if (onActionClick && actionIcon) {
buttons = <IconButton icon={actionIcon} title={actionTitle} onClick={this.handleAction} />;
buttons = <IconButton icon={actionIcon} title={actionTitle} onClick={this.handleAction} />
} 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']);
const muting = account.getIn(['relationship', 'muting']);
const following = account.getIn(['relationship', 'following'])
const requested = account.getIn(['relationship', 'requested'])
const blocking = account.getIn(['relationship', 'blocking'])
const muting = account.getIn(['relationship', 'muting'])
if (requested) {
buttons = <IconButton disabled icon='hourglass' title={intl.formatMessage(messages.requested)} />;
buttons = <IconButton disabled icon='hourglass' title={intl.formatMessage(messages.requested)} />
} else if (blocking) {
buttons = <IconButton active icon='unlock' title={intl.formatMessage(messages.unblock, { name: account.get('username') })} onClick={this.handleBlock} />;
buttons = <IconButton active icon='unlock' title={intl.formatMessage(messages.unblock, { name: account.get('username') })} onClick={this.handleBlock} />
} else if (muting) {
let hidingNotificationsButton;
let hidingNotificationsButton
if (account.getIn(['relationship', 'muting_notifications'])) {
hidingNotificationsButton = <IconButton active icon='bell' title={intl.formatMessage(messages.unmute_notifications, { name: account.get('username') })} onClick={this.handleUnmuteNotifications} />;
hidingNotificationsButton = <IconButton active icon='bell' title={intl.formatMessage(messages.unmute_notifications, { name: account.get('username') })} onClick={this.handleUnmuteNotifications} />
} else {
hidingNotificationsButton = <IconButton active icon='bell-slash' title={intl.formatMessage(messages.mute_notifications, { name: account.get('username') })} onClick={this.handleMuteNotifications} />;
hidingNotificationsButton = <IconButton active icon='bell-slash' title={intl.formatMessage(messages.mute_notifications, { name: account.get('username') })} onClick={this.handleMuteNotifications} />
}
buttons = (
@ -102,41 +100,44 @@ class Account extends ImmutablePureComponent {
<IconButton active icon='volume-up' title={intl.formatMessage(messages.unmute, { name: account.get('username') })} onClick={this.handleMute} />
{hidingNotificationsButton}
</Fragment>
);
)
} else if (!account.get('moved') || following) {
buttons = <IconButton icon={following ? 'user-times' : 'user-plus'} title={intl.formatMessage(following ? messages.unfollow : messages.follow)} onClick={this.handleFollow} active={following} />;
buttons = <IconButton icon={following ? 'user-times' : 'user-plus'} title={intl.formatMessage(following ? messages.unfollow : messages.follow)} onClick={this.handleFollow} active={following} />
}
}
if (displayOnly) {
return (
<div className='account'>
<div className='account__wrapper'>
<div className='account__display-name'>
<div className='account__avatar-wrapper'>
<Avatar account={account} size={36} />
</div>
<DisplayName account={account} />
</div>
</div>
</div>
);
}
return (
<div className='account'>
<div className='account__wrapper'>
<Link key={account.get('id')} className='account__display-name' title={account.get('acct')} to={`/${account.get('acct')}`}>
<div className='account__avatar-wrapper'><Avatar account={account} size={36} /></div>
<DisplayName account={account} />
</Link>
<div className={[styles.default, styles.marginTop5PX, styles.marginBottom15PX].join(' ')}>
<div className={[styles.default, styles.flexRow].join(' ')}>
<div className='account__relationship'>
{buttons}
<NavLink
className={[styles.default, styles.noUnderline].join(' ')}
title={account.get('acct')}
to={`/${account.get('acct')}`}
>
<Avatar account={account} size={52} />
</NavLink>
<div className={[styles.default, styles.alignItemsStart, styles.paddingHorizontal10PX].join(' ')}>
<NavLink
className={[styles.default, styles.noUnderline].join(' ')}
title={account.get('acct')}
to={`/${account.get('acct')}`}
>
<DisplayName account={account} />
</NavLink>
<button className={[styles.default, styles.marginTop5PX, styles.colorBrand, styles.text, styles.cursorPointer, styles.fontSize14PX, styles.circle, styles.border1PX, styles.borderColorBrand, styles.paddingHorizontal20PX, styles.paddingVertical5PX].join(' ')}>
{intl.formatMessage(messages.follow)}
</button>
</div>
<button className={[styles.default, styles.marginLeftAuto, styles.paddingVertical2PX, styles.cursorPointer].join(' ')}>
<Icon width='14px' height='14px' />
</button>
</div>
</div>
);
)
}
}

View File

@ -1,5 +1,3 @@
import './gab_logo.scss'
const GabLogo = ({
width = '50px',
height = '30px',

View File

@ -1,9 +0,0 @@
.gab-logo {
enable-background: new 0 0 50 30;
&__path {
fill-rule: evenodd;
clip-rule: evenodd;
fill: $gab-brand-default;
}
}

View File

@ -1 +0,0 @@
export { default } from './gab_logo';

View File

@ -4,8 +4,6 @@ import { makeGetAccount } from '../../selectors';
import Avatar from '../avatar';
import DisplayName from '../display_name';
import './autosuggest_account.scss';
const makeMapStateToProps = () => {
const getAccount = makeGetAccount();

View File

@ -1,7 +1,5 @@
import unicodeMapping from '../emoji/emoji_unicode_mapping_light';
import './autosuggest_emoji.scss';
const assetHost = process.env.CDN_HOST || '';
export default class AutosuggestEmoji extends PureComponent {

View File

@ -1,3 +1,4 @@
import { Fragment } from 'react'
import ImmutablePropTypes from 'react-immutable-proptypes';
import classNames from 'classnames';
import ImmutablePureComponent from 'react-immutable-pure-component';
@ -7,8 +8,6 @@ import { textAtCursorMatchesToken } from '../../utils/cursor_token_match';
import AutosuggestAccount from '../autosuggest_account';
import AutosuggestEmoji from '../autosuggest_emoji';
import './autosuggest_textbox.scss';
export default class AutosuggestTextbox extends ImmutablePureComponent {
static propTypes = {
@ -198,15 +197,13 @@ export default class AutosuggestTextbox extends ImmutablePureComponent {
}
if (textarea) {
return [
<div className='autosuggest-textarea__wrapper' key='autosuggest-textarea__wrapper'>
<div className='autosuggest-textarea'>
<label>
<span style={{ display: 'none' }}>{placeholder}</span>
return (
<Fragment>
<div className={[styles.default].join(' ')}>
<div className={[styles.default, styles.marginLeft5PX].join(' ')}>
<Textarea
inputRef={this.setTextbox}
className='autosuggest-textarea__textarea'
className={[styles.default, styles.backgroundWhite, styles.lineHeight125, styles.resizeNone, styles.paddingVertical15PX, styles.outlineNone, styles.fontSize16PX, styles.text, styles.displayBlock].join(' ')}
disabled={disabled}
placeholder={placeholder}
autoFocus={autoFocus}
@ -220,16 +217,16 @@ export default class AutosuggestTextbox extends ImmutablePureComponent {
style={style}
aria-autocomplete='list'
/>
</label>
</div>
{children}
</div>
{children}
</div>,
<div className='autosuggest-textarea__suggestions-wrapper' key='autosuggest-textarea__suggestions-wrapper'>
<div className={`autosuggest-textarea__suggestions ${suggestionsHidden || suggestions.isEmpty() ? '' : 'autosuggest-textarea__suggestions--visible'}`}>
{suggestions.map(this.renderSuggestion)}
<div className='autosuggest-textarea__suggestions-wrapper'>
<div className={`autosuggest-textarea__suggestions ${suggestionsHidden || suggestions.isEmpty() ? '' : 'autosuggest-textarea__suggestions--visible'}`}>
{suggestions.map(this.renderSuggestion)}
</div>
</div>
</div>,
];
</Fragment>
)
}
return (

View File

@ -1,10 +1,8 @@
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { Map as ImmutableMap } from 'immutable';
import classNames from 'classnames';
import { autoPlayGif } from '../../initial_state';
import './avatar.scss';
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { Map as ImmutableMap } from 'immutable'
import classNames from 'classnames'
import { autoPlayGif } from '../../initial_state'
export default class Avatar extends ImmutablePureComponent {
@ -13,57 +11,53 @@ export default class Avatar extends ImmutablePureComponent {
size: PropTypes.number,
inline: PropTypes.bool,
animate: PropTypes.bool,
};
}
static defaultProps = {
account: ImmutableMap(),
animate: autoPlayGif,
inline: false,
};
}
state = {
hovering: false,
sameImg: this.props.account.get('avatar') === this.props.account.get('avatar_static'),
};
}
handleMouseEnter = () => {
if (this.props.animate || this.state.sameImg) return;
if (this.props.animate || this.state.sameImg) return
this.setState({ hovering: true });
this.setState({ hovering: true })
}
handleMouseLeave = () => {
if (this.props.animate || this.state.sameImg) return;
if (this.props.animate || this.state.sameImg) return
this.setState({ hovering: false });
this.setState({ hovering: false })
}
render () {
const { account, size, animate, inline } = this.props;
const { hovering } = this.state;
const { account, size, animate, inline } = this.props
const { hovering } = this.state
// : TODO : remove inline and change all avatars to be sized using css
const style = !size ? {} : {
width: `${size}px`,
height: `${size}px`,
};
}
const theSrc = account.get((hovering || animate) ? 'avatar' : 'avatar_static');
const className = classNames('account__avatar', {
'account__avatar--inline': inline,
});
const theSrc = account.get((hovering || animate) ? 'avatar' : 'avatar_static')
return (
<img
className={className}
className={[styles.default, styles.circle].join(' ')}
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
style={style}
src={theSrc}
alt={account.get('display_name')}
/>
);
)
}
}

View File

@ -2,8 +2,6 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { autoPlayGif } from '../../initial_state';
import './avatar_overlay.scss';
export default class AvatarOverlay extends ImmutablePureComponent {
static propTypes = {

View File

@ -1,25 +0,0 @@
import './badge.scss';
export default class Badge extends PureComponent {
static propTypes = {
type: PropTypes.oneOf([
'pro',
'donor',
'investor',
]).isRequired,
};
render() {
const { type } = this.props;
if (!type) return null;
return (
<span className={`badge badge--${type}`}>
{type.toUpperCase()}
</span>
);
}
};

View File

@ -1,23 +0,0 @@
.badge {
text-transform: uppercase;
padding: 2px 6px;
border-radius: 2px;
margin: 0 5px 5px 0;
@include text-sizing(12px, 600, 1, center);
&--pro {
background-color: blueviolet;
color: #fff;
}
&--investor {
background-color: gold;
color: #000;
}
&--donor {
background-color: lightgreen;
color: #000;
}
}

View File

@ -1 +0,0 @@
export { default } from './badge';

View File

@ -3,8 +3,6 @@ import Column from '../column';
import { ColumnHeader } from '../column_header';
import IconButton from '../icon_button';
import './bundle_column_error.scss';
const messages = defineMessages({
title: { id: 'bundle_column_error.title', defaultMessage: 'Network error' },
body: { id: 'bundle_column_error.body', defaultMessage: 'Something went wrong while loading this component.' },

View File

@ -1,8 +1,6 @@
import { defineMessages, injectIntl } from 'react-intl';
import IconButton from '../icon_button';
import './bundle_modal_error.scss';
const messages = defineMessages({
error: { id: 'bundle_modal_error.message', defaultMessage: 'Something went wrong while loading this component.' },
retry: { id: 'bundle_modal_error.retry', defaultMessage: 'Try again' },

View File

@ -56,10 +56,10 @@ export default class Button extends PureComponent {
colorWhite: 1,
circle: 1,
cursorPointer: 1,
width100PC: 1,
textAlignCenter: 1,
paddingVertical10PX: 1,
paddingHorizontal15PX: 1,
width100PC: block,
backgroundColorBrand: !hovering,
backgroundColorBrandDark: hovering,
})

View File

@ -2,8 +2,6 @@ import { isMobile } from '../../utils/is_mobile';
import { ColumnHeader } from '../column_header';
import ColumnBackButton from '../column_back_button';
import './column.scss';
export default class Column extends PureComponent {
static propTypes = {

View File

@ -2,8 +2,6 @@ import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import Icon from '../icon';
import './column_back_button.scss';
export default class ColumnBackButton extends PureComponent {
static contextTypes = {

View File

@ -3,8 +3,6 @@ import classNames from 'classnames';
import { injectIntl, defineMessages } from 'react-intl';
import Icon from '../icon';
import './column_header.scss';
const messages = defineMessages({
show: { id: 'column_header.show_settings', defaultMessage: 'Show settings' },
hide: { id: 'column_header.hide_settings', defaultMessage: 'Hide settings' },

View File

@ -7,8 +7,6 @@ import { createSelector } from 'reselect';
import { fetchLists } from '../../actions/lists';
import Icon from '../icon';
import './column_header.scss';
const messages = defineMessages({
show: { id: 'column_header.show_settings', defaultMessage: 'Show settings' },
hide: { id: 'column_header.hide_settings', defaultMessage: 'Hide settings' },

View File

@ -2,8 +2,6 @@ import { Link } from 'react-router-dom';
import classNames from 'classnames';
import Icon from '../icon';
import './column_header_setting_button.scss';
export default class ColumnHeaderSettingButton extends PureComponent {
static propTypes = {

View File

@ -1,8 +1,6 @@
import { defineMessages, injectIntl } from 'react-intl';
import Column from '../column';
import './column_indicator.scss';
const messages = defineMessages({
loading: { id: 'loading_indicator.label', defaultMessage: 'Loading...' },
missing: { id: 'missing_indicator.sublabel', defaultMessage: 'This resource could not be found.' },

View File

@ -1,7 +1,5 @@
import Button from '../button';
import './column_inline_form.scss';
export default class ColumnInlineForm extends PureComponent {
static propTypes = {

View File

@ -1,8 +1,6 @@
import { Link } from 'react-router-dom';
import Icon from '../../components/icon';
import './column_link.scss';
export default class ColumnLink extends PureComponent {
static propTypes = {

View File

@ -1,5 +1,3 @@
import './column_settings_heading.scss';
export default class ColumnSettingsHeading extends PureComponent {
static propTypes = {
heading: PropTypes.object.isRequired,

View File

@ -1,5 +1,3 @@
import './column_subheading.scss';
export default class ColumnSubheading extends PureComponent {
static propTypes = {
text: PropTypes.string.isRequired,

View File

@ -0,0 +1,36 @@
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import Icon from './icon';
export default class DisplayName extends ImmutablePureComponent {
static propTypes = {
account: ImmutablePropTypes.map.isRequired,
};
render () {
const { account } = this.props;
return (
<span className={[styles.default, styles.flexRow, styles.maxWidth100PC, styles.alignItemsCenter].join(' ')}>
<bdi className={[styles.text, styles.whiteSpaceNoWrap, styles.textOverflowEllipsis].join(' ')}>
<strong
className={[styles.text, styles.overflowWrapBreakWord, styles.whiteSpaceNoWrap, styles.fontSize15PX, styles.fontWeightBold, styles.colorBlack, styles.lineHeight125, styles.marginRight2PX].join(' ')}
dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }}
/>
</bdi>
{
account.get('is_verified') &&
<Icon id='verified' width='15px' height='15px' className={[styles.default]} title='Verified Account' />
/*<Icon id='verified' width='15px' height='15px' className={[styles.default]} title='PRO' />
<Icon id='verified' width='15px' height='15px' className={[styles.default]} title='Donor' />
<Icon id='verified' width='15px' height='15px' className={[styles.default]} title='Investor' />*/
}
<span className={[styles.text, styles.displayFlex, styles.flexNormal, styles.flexShrink1, styles.fontSize15PX, styles.overflowWrapBreakWord, styles.textOverflowEllipsis, styles.marginLeft5PX, styles.colorSubtle, styles.fontWeightNormal, styles.lineHeight125].join(' ')}>
@{account.get('acct')}
</span>
</span>
);
}
}

View File

@ -1,27 +0,0 @@
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import VerifiedIcon from '../verified_icon';
import './display_name.scss';
export default class DisplayName extends ImmutablePureComponent {
static propTypes = {
account: ImmutablePropTypes.map.isRequired,
};
render () {
const { account } = this.props;
return (
<span className='display-name'>
<bdi>
<strong className='display-name__html' dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }} />
</bdi>
{account.get('is_verified') && <VerifiedIcon />}
<span className='display-name__account'>@{account.get('acct')}</span>
</span>
);
}
}

View File

@ -1,16 +0,0 @@
.display-name {
display: block;
max-width: 100%;
@include text-overflow(break-word);
&__html {
font-weight: 600;
font-size: 15px;
}
&__account {
margin-left: 5px;
font-size: 15px;
}
}

View File

@ -1 +0,0 @@
export { default } from './display_name';

View File

@ -1,8 +1,6 @@
import { defineMessages, injectIntl } from 'react-intl';
import IconButton from '../icon_button';
import './domain.scss';
const messages = defineMessages({
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unhide {domain}' },
});

View File

@ -6,8 +6,6 @@ import spring from 'react-motion/lib/spring';
import Motion from '../../features/ui/util/optional_motion';
import IconButton from '../icon_button';
import './dropdown_menu.scss';
const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false;
let id = 0;

View File

@ -1,7 +1,5 @@
import { FormattedMessage } from 'react-intl';
import './error_boundary.scss';
export default class ErrorBoundary extends PureComponent {
static propTypes = {

View File

@ -1,5 +1,3 @@
import './extended_video_player.scss';
export default class ExtendedVideoPlayer extends PureComponent {
static propTypes = {

View File

@ -1,7 +1,5 @@
import ComposeIcon from './assets/compose_icon';
import './floating_action_button.scss';
export default class FloatingActionButton extends Component {
static propTypes = {
onClick: PropTypes.func.isRequired,

View File

@ -3,8 +3,6 @@ import { FormattedMessage, injectIntl } from 'react-intl';
import NotificationCounter from '../notification_counter';
import { me } from '../../initial_state';
import './footer_bar.scss';
const links = [
<NavLink key='pr1' className='footer-bar__link' to='/home' data-preview-title-id='column.home'>
<i className='tabs-bar__link__icon home'/>

View File

@ -1,14 +1,11 @@
import { Link, NavLink } from 'react-router-dom'
import { NavLink } from 'react-router-dom'
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { injectIntl, defineMessages } from 'react-intl'
import classNames from 'classnames/bind'
import Avatar from './avatar'
import Button from './button'
import Icon from './icon'
import DisplayName from './display_name'
import { closeSidebar } from '../actions/sidebar'
import { shortNumberFormat } from '../utils/numbers'
import { me } from '../initial_state'
import { makeGetAccount } from '../selectors'
// import ProgressPanel from './progress_panel'
@ -87,18 +84,6 @@ class Header extends ImmutablePureComponent {
})
}
onMouseEnterLinkFooterItem = (i) => {
this.setState({
hoveringItemIndex: i,
})
}
onMouseLeaveLinkFooterItem = () => {
this.setState({
hoveringItemIndex: null,
})
}
handleSidebarClose = () => {
this.props.onClose()
this.setState({
@ -119,44 +104,73 @@ class Header extends ImmutablePureComponent {
// 'sidebar-menu__root--visible': sidebarOpen,
// })
const cx = classNames.bind(styles)
const moreIcon = moreOpen ? 'minus' : 'plus'
const moreContainerStyle = { display: moreOpen ? 'block' : 'none' }
const sidebarItems = [
const menuItems = [
{
name: 'Home',
title: 'Home',
icon: <NotificationsIcon />,
to: '/',
},
{
name: 'Notifications',
title: 'Notifications',
icon: <NotificationsIcon />,
to: '/notifications',
},
{
name: 'Groups',
title: 'Groups',
icon: <NotificationsIcon />,
to: '/groups',
},
{
name: 'Lists',
title: 'Lists',
icon: <NotificationsIcon />,
to: '/lists',
},
{
name: 'Chat',
title: 'Chat',
icon: <NotificationsIcon />,
to: '/',
},
{
name: 'Trends',
title: 'Profile',
icon: <NotificationsIcon />,
to: '/',
},
]
const shortcutItems = [
{
title: 'Meme Group',
icon: <NotificationsIcon />,
to: '/',
},
{
name: 'Profile',
title: 'Andrew',
icon: <NotificationsIcon />,
to: '/',
},
]
const exploreItems = [
{
title: 'Trends',
icon: <NotificationsIcon />,
to: '/',
},
{
title: 'Dissenter',
icon: <NotificationsIcon />,
to: '/',
},
{
title: 'Apps',
icon: <NotificationsIcon />,
to: '/',
},
{
title: 'Shop',
icon: <NotificationsIcon />,
to: '/',
},
@ -166,186 +180,116 @@ class Header extends ImmutablePureComponent {
<header role='banner' className={[styles.default, styles.flexGrow1, styles.z3, styles.alignItemsEnd].join(' ')}>
<div className={[styles.default, styles.width250PX].join(' ')}>
<div className={[styles.default, styles.positionFixed, styles.top0, styles.height100PC].join(' ')}>
<div className={[styles.default, styles.height100PC, styles.width250PX, styles.paddingHorizontal20PX].join(' ')}>
<div className={[styles.default, styles.height100PC, styles.width250PX, styles.paddingHorizontal20PX, styles.marginVertical10PX].join(' ')}>
<h1 className={[styles.default].join(' ')}>
<NavLink to='/' aria-label='Gab' className={[styles.default, styles.noSelect, styles.noUnderline, styles.height50PX, styles.justifyContentCenter, styles.cursorPointer, styles.paddingHoizontal10PX].join(' ')}>
<NavLink to='/' aria-label='Gab' className={[styles.default, styles.noSelect, styles.noUnderline, styles.height50PX, styles.justifyContentCenter, styles.cursorPointer, styles.paddingHorizontal10PX].join(' ')}>
<GabLogo />
</NavLink>
</h1>
<nav aria-label='Primary' role='navigation' className={[styles.default, styles.width100PC].join(' ')}>
<div>
{ /* profile card */}
</div>
<nav aria-label='Primary' role='navigation' className={[styles.default, styles.width100PC, styles.marginBottom15PX].join(' ')}>
<span className={[styles.default, styles.text, styles.colorSubtle, styles.displayBlock, styles.fontSize13PX, styles.paddingVertical5PX, styles.marginTop10PX, styles.paddingHorizontal10PX, styles.fontWeight500].join(' ')}>Menu</span>
{
sidebarItems.map((sidebarItem, i) => {
const containerClasses = cx({
default: 1,
maxWidth100PC: 1,
flexRow: 1,
paddingVertical10PX: 1,
paddingHoizontal10PX: 1,
circle: 1,
alignItemsCenter: 1,
backgroundColorBrandLightOpaque: hoveringItemIndex === i,
})
const textClasses = cx({
default: 1,
fontWeightBold: 1,
fontSize19PX: 1,
text: 1,
colorBrand: hoveringItemIndex === i,
colorBlack: hoveringItemIndex !== i,
})
const iconClasses = cx({
fillColorBrand: hoveringItemIndex === i,
fillColorBlack: hoveringItemIndex !== i,
})
return (
<NavLink
to={sidebarItem.to}
key={`header-nav-item-${i}`}
onMouseEnter={() => this.onMouseEnterLinkFooterItem(i)}
onMouseLeave={() => this.onMouseLeaveLinkFooterItem(i)}
className={[styles.default, styles.noUnderline, styles.cursorPointer, styles.width100PC, styles.alignItemsStart, styles.flexGrow1].join(' ')}
>
<div className={containerClasses}>
<div className={[styles.default]}>
<NotificationsIcon className={iconClasses}/>
</div>
<div className={[styles.default, styles.paddingHorizontal20PX, styles.textOverflowEllipsis, styles.overflowWrapBreakWord, styles.displayInline].join(' ')}>
<span className={textClasses}>
{sidebarItem.name}
</span>
</div>
</div>
</NavLink>
)
})
menuItems.map((menuItem, i) => (
<HeaderMenuItem {...menuItem} key={`header-item-menu-${i}`} />
))
}
<span className={[styles.default, styles.text, styles.colorSubtle, styles.displayBlock, styles.fontSize13PX, styles.paddingVertical5PX, styles.marginTop10PX, styles.paddingHorizontal10PX, styles.fontWeight500].join(' ')}>Shortcuts</span>
{
shortcutItems.map((shortcutItem, i) => (
<HeaderMenuItem {...shortcutItem} key={`header-item-shortcut-${i}`} />
))
}
<span className={[styles.default, styles.text, styles.colorSubtle, styles.displayBlock, styles.fontSize13PX, styles.paddingVertical5PX, styles.marginTop10PX, styles.paddingHorizontal10PX, styles.fontWeight500].join(' ')}>Explore</span>
{
exploreItems.map((exploreItem, i) => (
<HeaderMenuItem {...exploreItem} key={`header-item-explore-${i}`} />
))
}
</nav>
<Button className={[styles.paddingVertical15PX, styles.marginVertical5PX, styles.fontSize15PX, styles.fontWeightBold].join(' ')}>Gab</Button>
<Button className={[styles.paddingVertical15PX, styles.fontSize15PX, styles.fontWeightBold].join(' ')}>Gab</Button>
</div>
</div>
</div>
</header>
)
// return (
// <div className={classes}>
// <div className='sidebar-menu__wrapper' role='button' onClick={this.handleSidebarClose} />
// <div className='sidebar-menu'>
// <div className='sidebar-menu-header'>
// <span className='sidebar-menu-header__title'>Account Info</span>
// <IconButton title='close' onClick={this.handleSidebarClose} icon='close' className='sidebar-menu-header__btn' />
// </div>
// <div className='sidebar-menu__content'>
// <div className='sidebar-menu-profile'>
// <div className='sidebar-menu-profile__avatar'>
// <Link to={`/${acct}`} title={acct} onClick={this.handleSidebarClose}>
// <Avatar account={account} />
// </Link>
// </div>
// <div className='sidebar-menu-profile__name'>
// <DisplayName account={account} />
// </div>
// <div className='sidebar-menu-profile__stats'>
// <NavLink className='sidebar-menu-profile-stat' to={`/${acct}/followers`} onClick={this.handleSidebarClose} title={intl.formatNumber(account.get('followers_count'))}>
// <strong className='sidebar-menu-profile-stat__value'>{shortNumberFormat(account.get('followers_count'))}</strong>
// <span className='sidebar-menu-profile-stat__label'>{intl.formatMessage(messages.followers)}</span>
// </NavLink>
// <NavLink className='sidebar-menu-profile-stat' to={`/${acct}/following`} onClick={this.handleSidebarClose} title={intl.formatNumber(account.get('following_count'))}>
// <strong className='sidebar-menu-profile-stat__value'>{shortNumberFormat(account.get('following_count'))}</strong>
// <span className='sidebar-menu-profile-stat__label'>{intl.formatMessage(messages.follows)}</span>
// </NavLink>
// </div>
// </div>
// <div className='sidebar-menu__section'>
// { /* <ProgressPanel /> */ }
// </div>
// <div className='sidebar-menu__section sidebar-menu__section--borderless'>
// <NavLink className='sidebar-menu-item' to={`/${acct}`} onClick={this.handleSidebarClose}>
// <Icon id='user' fixedWidth />
// <span className='sidebar-menu-item__title'>{intl.formatMessage(messages.profile)}</span>
// </NavLink>
// {
// !isPro &&
// <a className='sidebar-menu-item' href='https://pro.gab.com'>
// <Icon id='arrow-up' fixedWidth />
// <span className='sidebar-menu-item__title'>{intl.formatMessage(messages.pro)}</span>
// </a>
// }
// <a className='sidebar-menu-item' href='https://shop.dissenter.com/category/donations'>
// <Icon id='heart' fixedWidth />
// <span className='sidebar-menu-item__title'>{intl.formatMessage(messages.donate)}</span>
// </a>
// <a className='sidebar-menu-item' href='https://shop.dissenter.com'>
// <Icon id='shopping-cart' fixedWidth />
// <span className='sidebar-menu-item__title'>{intl.formatMessage(messages.shop)}</span>
// </a>
// <a className='sidebar-menu-item' href='https://trends.gab.com'>
// <Icon id='signal' fixedWidth />
// <span className='sidebar-menu-item__title'>{intl.formatMessage(messages.trends)}</span>
// </a>
// <NavLink className='sidebar-menu-item' to='/search' onClick={this.handleSidebarClose}>
// <Icon id='search' fixedWidth />
// <span className='sidebar-menu-item__title'>{intl.formatMessage(messages.search)}</span>
// </NavLink>
// <a className='sidebar-menu-item' href='/settings/preferences'>
// <Icon id='cog' fixedWidth />
// <span className='sidebar-menu-item__title'>{intl.formatMessage(messages.preferences)}</span>
// </a>
// </div>
// <div className='sidebar-menu__section'>
// <div className='sidebar-menu-item' onClick={this.toggleMore} role='button'>
// <Icon id={moreIcon} fixedWidth />
// <span className='sidebar-menu-item__title'>{intl.formatMessage(messages.more)}</span>
// </div>
// <div style={moreContainerStyle}>
// <NavLink className='sidebar-menu-item' to='/lists' onClick={this.handleSidebarClose}>
// <Icon id='list' fixedWidth />
// <span className='sidebar-menu-item__title'>{intl.formatMessage(messages.lists)}</span>
// </NavLink>
// <NavLink className='sidebar-menu-item' to='/follow_requests' onClick={this.handleSidebarClose}>
// <Icon id='user-plus' fixedWidth />
// <span className='sidebar-menu-item__title'>{intl.formatMessage(messages.follow_requests)}</span>
// </NavLink>
// <NavLink className='sidebar-menu-item' to='/blocks' onClick={this.handleSidebarClose}>
// <Icon id='ban' fixedWidth />
// <span className='sidebar-menu-item__title'>{intl.formatMessage(messages.blocks)}</span>
// </NavLink>
// <NavLink className='sidebar-menu-item' to='/domain_blocks' onClick={this.handleSidebarClose}>
// <Icon id='sitemap' fixedWidth />
// <span className='sidebar-menu-item__title'>{intl.formatMessage(messages.domain_blocks)}</span>
// </NavLink>
// <NavLink className='sidebar-menu-item' to='/mutes' onClick={this.handleSidebarClose}>
// <Icon id='times-circle' fixedWidth />
// <span className='sidebar-menu-item__title'>{intl.formatMessage(messages.mutes)}</span>
// </NavLink>
// <a className='sidebar-menu-item' href='/filters'>
// <Icon id='filter' fixedWidth />
// <span className='sidebar-menu-item__title'>{intl.formatMessage(messages.filters)}</span>
// </a>
// </div>
// </div>
// <div className='sidebar-menu__section'>
// <a className='sidebar-menu-item' href='/auth/sign_out' data-method='delete'>
// <span className='sidebar-menu-item__title'>{intl.formatMessage(messages.logout)}</span>
// </a>
// </div>
// </div>
// </div>
// </div>
// )
}
}
class HeaderMenuItem extends PureComponent {
static propTypes = {
to: PropTypes.string,
active: PropTypes.bool,
icon: PropTypes.node,
title: PropTypes.string,
}
state = {
hovering: false,
}
handleOnMouseEnter = () => {
this.setState({ hovering: true })
}
handleOnMouseLeave = () => {
this.setState({ hovering: false })
}
render() {
const { to, active, icon, title } = this.props
const { hovering } = this.state
const cx = classNames.bind(styles)
const shouldShowActive = hovering || active
const containerClasses = cx({
default: 1,
maxWidth100PC: 1,
width100PC: 1,
flexRow: 1,
paddingVertical5PX: 1,
paddingHorizontal10PX: 1,
alignItemsCenter: 1,
// border1PX: shouldShowActive,
// borderColorSubtle: shouldShowActive,
backgroundWhite: shouldShowActive,
})
const textClasses = cx({
default: 1,
fontWeightNormal: 1,
fontSize15PX: 1,
text: 1,
colorBrand: shouldShowActive,
colorBlack: !hovering && !active,
})
const iconClasses = cx({
fillColorBrand: shouldShowActive,
fillColorBlack: !hovering && !active,
})
return (
<NavLink
to={to}
onMouseEnter={() => this.handleOnMouseEnter()}
onMouseLeave={() => this.handleOnMouseLeave()}
className={[styles.default, styles.noUnderline, styles.cursorPointer, styles.width100PC, styles.alignItemsStart, styles.flexGrow1].join(' ')}
>
<div className={containerClasses}>
<div className={[styles.default]}>
<NotificationsIcon className={iconClasses} width='16px' height='16px' />
</div>
<div className={[styles.default, styles.paddingHorizontal10PX, styles.textOverflowEllipsis, styles.overflowWrapBreakWord, styles.displayInline].join(' ')}>
<span className={textClasses}>{title}</span>
</div>
</div>
</NavLink>
)
}
}

View File

@ -1,6 +1,5 @@
import classNames from 'classnames';
import './icon.scss';
export default class Icon extends PureComponent {
static propTypes = {

View File

@ -3,8 +3,6 @@ import spring from 'react-motion/lib/spring';
import Motion from '../../features/ui/util/optional_motion';
import Icon from '../icon';
import './icon_button.scss';
export default class IconButton extends PureComponent {
static propTypes = {
@ -23,6 +21,8 @@ export default class IconButton extends PureComponent {
overlay: PropTypes.bool,
tabIndex: PropTypes.string,
text: PropTypes.string,
width: PropTypes.string,
height: PropTypes.string,
};
static defaultProps = {
@ -64,6 +64,8 @@ export default class IconButton extends PureComponent {
tabIndex,
title,
text,
width,
height,
} = this.props;
const classes = classNames(className, 'icon-button', {
@ -73,45 +75,21 @@ export default class IconButton extends PureComponent {
overlayed: overlay,
});
// Perf optimization: avoid unnecessary <Motion> components unless we actually need to animate.
if (!animate) {
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' />
{!!text && text}
</button>
);
}
return (
<Motion defaultStyle={{ rotate: active ? -360 : 0 }} style={{ rotate: animate ? spring(active ? -360 : 0, { stiffness: 120, damping: 7 }) : 0 }}>
{({ rotate }) => (
<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} style={{ transform: `rotate(${rotate}deg)` }} fixedWidth aria-hidden='true' />
{!!text && text}
</button>
)}
</Motion>
<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>
);
}

View File

@ -2,8 +2,6 @@ import classNames from 'classnames';
import { LoadingBar } from 'react-redux-loading-bar';
import ZoomableImage from '../zoomable_image';
import './image_loader.scss';
export default class ImageLoader extends PureComponent {
static propTypes = {

View File

@ -2,8 +2,6 @@ import { is } from 'immutable';
import scheduleIdleTask from '../../utils/schedule_idle_task';
import getRectFromEntry from '../../utils/get_rect_from_entry';
import './intersection_observer_article.scss';
// Diff these props in the "rendered" state
const updateOnPropsForRendered = ['id', 'index', 'listLength'];
// Diff these props in the "unrendered" state

View File

@ -1,4 +1,4 @@
article {
// TEMPORARY - content of columns may be significantly altered
background: $gab-background-container;
// background: $gab-background-container;
}

View File

@ -109,7 +109,7 @@ class LinkFooter extends PureComponent {
]
return (
<div className={styles.default}>
<div className={[styles.default, styles.paddingHorizontal10PX].join(' ')}>
<nav aria-label='Footer' role='navigation' className={[styles.default, styles.flexWrap, styles.flexRow].join(' ')}>
{
linkFooterItems.map((linkFooterItem, i) => {
@ -121,6 +121,7 @@ class LinkFooter extends PureComponent {
marginVertical5PX: 1,
paddingRight15PX: 1,
cursorPointer: 1,
backgroundTransparent: 1,
colorSubtle: i !== hoveringItemIndex,
noUnderline: i !== hoveringItemIndex,
colorBlack: i === hoveringItemIndex,

View File

@ -2,8 +2,6 @@ import { injectIntl, defineMessages } from 'react-intl';
import classNames from 'classnames';
import Icon from '../icon';
import './load_more.scss';
const messages = defineMessages({
load_more: { id: 'status.load_more', defaultMessage: 'Load more' },
});

View File

@ -9,8 +9,6 @@ import { isIOS } from '../../utils/is_mobile';
import { isPanoramic, isPortrait, isNonConformingRatio, minimumAspectRatio, maximumAspectRatio } from '../../utils/media_aspect_ratio';
import IconButton from '../icon_button';
import './media_gallery.scss';
const messages = defineMessages({
toggle_visible: { id: 'media_gallery.toggle_visible', defaultMessage: 'Toggle visibility' },
warning: { id: 'status.sensitive_warning', defaultMessage: 'Sensitive content' },

View File

@ -5,8 +5,6 @@ import { changeUploadCompose } from '../../../actions/compose';
import { getPointerPosition } from '../../../utils/element_position';
import ImageLoader from '../../image_loader';
import './focal_point_modal.scss';
const mapStateToProps = (state, { id }) => ({
media: state.getIn(['compose', 'media_attachments']).find(item => item.get('id') === id),
});

View File

@ -2,8 +2,6 @@ import { defineMessages, injectIntl } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
import IconButton from '../../icon_button';
import './hotkeys_modal.scss';
const messages = defineMessages({
heading: { id: 'keyboard_shortcuts.heading', defaultMessage: 'Keyboard Shortcuts' },
close: { id: 'lightbox.close', defaultMessage: 'Close' },

View File

@ -9,8 +9,6 @@ import IconButton from '../../icon_button';
import ImageLoader from '../../image_loader';
import Icon from '../../icon';
import './media_modal.scss';
const messages = defineMessages({
close: { id: 'lightbox.close', defaultMessage: 'Close' },
previous: { id: 'lightbox.previous', defaultMessage: 'Previous' },

View File

@ -2,8 +2,6 @@ import { defineMessages, injectIntl } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
import IconButton from '../../icon_button';
import './unauthorized_modal.scss';
const messages = defineMessages({
close: { id: 'lightbox.close', defaultMessage: 'Close' },
signup: {id: 'unauthorized_modal.title', defaultMessage: 'Sign up for Gab' },

View File

@ -3,8 +3,6 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
import { FormattedMessage } from 'react-intl';
import Video from '../../../features/video';
import './video_modal.scss';
export const previewState = 'previewVideoModal';
export default class VideoModal extends ImmutablePureComponent {

View File

@ -3,8 +3,6 @@ import classNames from 'classnames';
import { openModal } from '../../actions/modal';
import { cancelReplyCompose } from '../../actions/compose';
import './modal_base.scss';
const messages = defineMessages({
confirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
});

View File

@ -1,7 +1,5 @@
import ColumnIndicator from '../column_indicator';
import './modal_loading.scss';
// Keep the markup in sync with <BundleModalError />
// (make sure they have the same dimensions)
export default class ModalLoading extends PureComponent {

View File

@ -1,7 +1,5 @@
import { shortNumberFormat } from '../../utils/numbers';
import './notification_counter.scss';
const mapStateToProps = state => ({
count: state.getIn(['notifications', 'unread']),
});

View File

@ -11,11 +11,11 @@ export default class PageLayout extends PureComponent {
const right = layout.RIGHT || null
return (
<div className={[styles.default, styles.flexRow, styles.width100PC].join(' ')}>
<div className={[styles.default, styles.flexRow, styles.width100PC, styles.backgroundColorSubtle3].join(' ')}>
<Header />
<main role='main' className={[styles.default, styles.flexShrink1, styles.flexGrow1, styles.flexRow].join(' ')}>
<div className={[styles.default, styles.width1015PX, styles.flexRow, styles.justifyContentSpaceBetween].join(' ')}>
<div className={[styles.default, styles.width660PX, styles.z1, styles.borderColorSubtle, styles.borderLeft1PX, styles.borderRight1PX].join(' ')}>
<div className={[styles.default, styles.width670PX, styles.z1].join(' ')}>
<div className={styles.default}>
{children}
</div>

View File

@ -0,0 +1,154 @@
import { Fragment } from 'react'
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { defineMessages, injectIntl } from 'react-intl'
import { NavLink } from 'react-router-dom'
import classNames from 'classnames/bind'
import { shortNumberFormat } from '../../utils/numbers'
import PanelLayout from './panel_layout'
import Icon from '../icon'
const messages = defineMessages({
title: { id: 'groups.sidebar-panel.title', defaultMessage: 'Groups you\'re in' },
show_all: { id: 'groups.sidebar-panel.show_all', defaultMessage: 'Show all' },
all: { id: 'groups.sidebar-panel.all', defaultMessage: 'All' },
new_statuses: { id: 'groups.sidebar-panel.item.view', defaultMessage: 'new gabs' },
no_recent_activity: { id: 'groups.sidebar-panel.item.no_recent_activity', defaultMessage: 'No recent activity' },
})
const mapStateToProps = (state) => ({
groupIds: state.getIn(['group_lists', 'member']),
})
export default @connect(mapStateToProps)
@injectIntl
class GroupSidebarPanel extends ImmutablePureComponent {
static propTypes = {
groupIds: ImmutablePropTypes.list,
}
render() {
const { intl, groupIds } = this.props
const count = groupIds.count()
if (count === 0) return null
return (
<PanelLayout
title={intl.formatMessage(messages.title)}
button={(
<NavLink
to='/groups/browse/member'
className={[styles.default, styles.fontWeightBold, styles.text, styles.colorBrand, styles.fontSize13PX, styles.noUnderline].join(' ')}
>
{intl.formatMessage(messages.all)}
</NavLink>
)}
>
<div className={styles.default}>
{
groupIds.slice(0, 6).map(groupId => (
<GroupPanelItem key={`group-panel-item-${groupId}`} id={groupId} />
))
}
{
count > 6 &&
<NavLink
to='/groups/browse/member'
className={[styles.default, styles.noUnderline, styles.paddingVertical5PX, styles.marginRightAuto, styles.marginTop5PX, styles.marginLeftAuto, styles.cursorPointer, styles.circle, styles.text, styles.displayFlex, styles.colorBrand].join(' ')}
>
{intl.formatMessage(messages.show_all)}
</NavLink>
}
</div>
</PanelLayout>
)
}
}
const mapStateToProps2 = (state, { id }) => ({
group: state.getIn(['groups', id]),
relationships: state.getIn(['group_relationships', id]),
})
@connect(mapStateToProps2)
@injectIntl
class GroupPanelItem extends ImmutablePureComponent {
static propTypes = {
group: ImmutablePropTypes.map,
relationships: ImmutablePropTypes.map,
}
state = {
hovering: false,
}
handleOnMouseEnter = () => {
this.setState({ hovering: true })
}
handleOnMouseLeave = () => {
this.setState({ hovering: false })
}
render() {
const { intl, group, relationships } = this.props
const { hovering } = this.state
if (!relationships) return null
const unreadCount = relationships.get('unread_count')
const cx = classNames.bind(styles)
const titleClasses = cx({
default: 1,
text: 1,
displayFlex: 1,
colorBrand: 1,
fontSize14PX: 1,
fontWeightBold: 1,
lineHeight15: 1,
underline: hovering,
})
return (
<NavLink
to={`/groups/${group.get('id')}`}
className={[styles.default, styles.noUnderline, styles.marginTop5PX, styles.overflowHidden, styles.radiusSmall, styles.marginBottom10PX, styles.border1PX, styles.borderColorSubtle].join(' ')}
onMouseEnter={() => this.handleOnMouseEnter()}
onMouseLeave={() => this.handleOnMouseLeave()}
>
<div className={[styles.default, styles.height122PX].join(' ')}>
<img
src='https://gab.com/media/user/bz-5cf53d08403d4.jpeg'
alt={group.get('title')}
className={[styles.default, styles.objectFitCover, styles.height122PX, styles.width100PC].join(' ')}
/>
</div>
<div className={[styles.default, styles.paddingHorizontal10PX, styles.marginVertical5PX].join(' ')}>
<span className={titleClasses}>
{group.get('title')}
</span>
<span className={[styles.default, styles.text, styles.alignItemsCenter, styles.displayFlex, styles.flexRow, styles.colorSubtle, styles.fontSize13PX, styles.fontWeightNormal, styles.lineHeight15].join(' ')}>
{
unreadCount > 0 &&
<Fragment>
{shortNumberFormat(unreadCount)}
&nbsp;
{intl.formatMessage(messages.new_statuses)}
</Fragment>
}
{
unreadCount === 0 &&
<Fragment>
{intl.formatMessage(messages.no_recent_activity)}
</Fragment>
}
<Icon className={styles.marginLeftAuto} width='10px' height='10px' />
</span>
</div>
</NavLink>
)
}
}

View File

@ -1,28 +1,32 @@
import classNames from 'classnames/bind'
import Icon from '../icon'
export default class PanelLayout extends PureComponent {
static propTypes = {
title: PropTypes.string,
subtitle: PropTypes.string,
icon: PropTypes.string,
children: PropTypes.node,
hasBackground: PropTypes.boolean,
button: PropTypes.node,
}
render() {
const { title, subtitle, icon, hasBackground, children } = this.props
const { title, subtitle, button, hasBackground, children } = this.props
return (
<aside className={[styles.default, styles.backgroundSubtle, styles.overflowHidden, styles.radiusSmall, styles.marginBottom15PX].join(' ')}>
<aside className={[styles.default, styles.backgroundWhite, styles.overflowHidden, styles.radiusSmall, styles.marginBottom15PX, styles.borderColorSubtle, styles.border1PX].join(' ')}>
{
(title || subtitle) &&
<div className={[styles.default, styles.paddingHorizontal15PX, styles.paddingVertical10PX, styles.borderColorSubtle, styles.borderBottom1PX].join(' ')}>
<div className={[styles.default, styles.flexRow, styles.alignItemsCenter].join(' ')}>
{icon && <Icon id={icon} height='16px' width='16px' className={[styles.default, styles.marginRight10PX].join(' ')} />}
<span className={[styles.default, styles.text, styles.fontWeightExtraBold, styles.colorBlack, styles.fontSize19PX].join(' ')}>{title}</span>
<h1 className={[styles.default, styles.text, styles.fontWeightBold, styles.colorBlack, styles.fontSize16PX].join(' ')}>{title}</h1>
{
!!button &&
<div className={[styles.default, styles.marginLeftAuto].join(' ')}>
{button}
</div>
}
</div>
{subtitle && <span className={[styles.default, styles.text, styles.colorSubtle, styles.fontSize13PX, styles.marginTop5PX].join(' ')}>{subtitle}</span>}
{subtitle && <h2 className={[styles.default, styles.text, styles.colorSubtle, styles.fontSize13PX, styles.fontWeightBold, styles.marginTop5PX].join(' ')}>{subtitle}</h2>}
</div>
}
<div className={[styles.default, styles.paddingHorizontal15PX, styles.paddingVertical10PX].join(' ')}>

View File

@ -10,7 +10,6 @@ export default class ProgressPanel extends PureComponent {
<PanelLayout
title="Gab's Operational Expenses"
subtitle="We are 100% funded by you"
icon="comments"
hasBackground
>
<ProgressBar progress={monthlyExpensesComplete}/>

View File

@ -1,85 +0,0 @@
import { injectIntl, defineMessages } from 'react-intl'
import classNames from 'classnames/bind'
import { me } from '../../initial_state';
import Icon from '../icon'
import PanelLayout from './panel_layout'
const messages = defineMessages({
pro: { id: 'promo.gab_pro', defaultMessage: 'Upgrade to GabPRO' },
donation: { id: 'promo.donation', defaultMessage: 'Make a Donation' },
store: { id: 'promo.store', defaultMessage: 'Store - Buy Merch' },
apps: { id: 'promo.gab_apps', defaultMessage: 'Gab Apps' },
})
const mapStateToProps = state => {
return {
isPro: state.getIn(['accounts', me, 'is_pro']),
};
};
export default
@injectIntl
@connect(mapStateToProps)
class PromoPanel extends PureComponent {
static propTypes = {
isPro: PropTypes.bool,
intl: PropTypes.object.isRequired,
}
render() {
const { isPro, intl } = this.props
const cx = classNames.bind(styles)
const items = [
{
text: intl.formatMessage(messages.pro),
href: 'https://pro.gab.com',
icon: 'arrow-up',
conditions: isPro,
highlighted: true,
},
{
text: intl.formatMessage(messages.donation),
href: 'https://shop.dissenter.com/category/donations',
icon: 'heart',
},
{
text: intl.formatMessage(messages.store),
href: 'https://shop.dissenter.com',
icon: 'shopping-cart',
},
{
text: intl.formatMessage(messages.apps),
href: 'https://apps.gab.com',
icon: 'th',
}
]
return (
<PanelLayout>
{
items.map((item, i) => {
if (item.conditions === false) return null
const classes = cx({
default: true,
borderColorSubtle: i !== items.length - 1,
borderBottom1PX: i !== items.length - 1,
})
return (
<div key={`promo-panel-item-${i}`} className={classes}>
<a className={[styles.default, styles.text, styles.colorBlack, styles.noUnderline, styles.paddingVertical10PX, styles.alignItemsCenter].join(' ')} href={item.href}>
<Icon id={item.icon} height='13px' width='13px' className={[styles.flex, styles.marginRight10PX].join(' ')} />
{item.text}
</a>
</div>
)
})
}
</PanelLayout>
)
}
}

View File

@ -6,7 +6,7 @@ import TrendingItem from '../../components/trending_item';
import PanelLayout from './panel_layout';
const messages = defineMessages({
title: { id:'trends.title', defaultMessage: 'Trends' },
title: { id:'trends.title', defaultMessage: 'Trends for you' },
});
const mapStateToProps = state => ({
@ -36,16 +36,22 @@ class TrendsPanel extends ImmutablePureComponent {
render() {
const { intl, trends } = this.props;
if (trends.isEmpty()) {
return null;
}
// !!! TESTING !!!
// if (trends.isEmpty()) {
// return null;
// }
return (
<PanelLayout id='hashtag' title={intl.formatMessage(messages.title)}>
<div className='panel__list'>
{trends && trends.map(hashtag => (
<PanelLayout title={intl.formatMessage(messages.title)}>
<div className={styles.default}>
{ /* trends && trends.map(hashtag => (
<TrendingItem key={hashtag.get('name')} hashtag={hashtag} />
))}
)) */ }
<TrendingItem />
<TrendingItem />
<TrendingItem />
<TrendingItem />
<TrendingItem />
</div>
</PanelLayout>
);

View File

@ -7,7 +7,7 @@ import PanelLayout from './panel_layout';
const messages = defineMessages({
dismissSuggestion: { id: 'suggestions.dismiss', defaultMessage: 'Dismiss suggestion' },
title: { id: 'who_to_follow.title', defaultMessage: 'Who To Follow' },
title: { id: 'who_to_follow.title', defaultMessage: 'Who to Follow' },
});
const mapStateToProps = state => ({
@ -37,15 +37,20 @@ class WhoToFollowPanel extends ImmutablePureComponent {
}
render() {
const { intl, suggestions, dismissSuggestion } = this.props;
if (suggestions.isEmpty()) {
return null;
}
const { intl, /* suggestions, */ dismissSuggestion } = this.props;
// : testing!!! :
const suggestions = [
"1",
"1",
"1",
]
// if (suggestions.isEmpty()) {
// return null;
// }
return (
<PanelLayout icon='users' title={intl.formatMessage(messages.title)}>
<div className='panel__list'>
<PanelLayout title={intl.formatMessage(messages.title)}>
<div className={styles.default}>
{suggestions && suggestions.map(accountId => (
<AccountContainer
key={accountId}

View File

@ -10,8 +10,6 @@ import emojify from '../emoji/emoji';
import RelativeTimestamp from '../relative_timestamp';
import Button from '../button';
import './poll.scss';
const mapStateToProps = (state, { pollId }) => ({
poll: state.getIn(['polls', pollId]),
});

View File

@ -1 +0,0 @@
export { default } from './progress_bar';

View File

@ -5,8 +5,6 @@ import IntersectionObserverWrapper from '../../features/ui/util/intersection_obs
import ColumnIndicator from '../column_indicator';
import LoadMore from '../load_more';
import './scrollable_list.scss';
const MOUSE_IDLE_DELAY = 300;
export default class ScrollableList extends PureComponent {

View File

@ -3,8 +3,6 @@ import Overlay from 'react-overlays/lib/Overlay';
import Icon from '../icon';
import SearchPopout from '../search_popout';
import './search.scss';
export default class Search extends PureComponent {
static contextTypes = {
@ -17,7 +15,6 @@ export default class Search extends PureComponent {
onShow: PropTypes.func.isRequired,
openInRoute: PropTypes.bool,
placeholder: PropTypes.string.isRequired,
className: PropTypes.string,
searchTitle: PropTypes.string,
onChange: PropTypes.func.isRequired,
onKeyUp: PropTypes.func.isRequired,
@ -40,32 +37,29 @@ export default class Search extends PureComponent {
}
render() {
const { value, submitted, placeholder, className, searchTitle, onKeyUp, handleClear, handleSubmit, withOverlay, onChange } = this.props;
const { value, submitted, placeholder, searchTitle, onKeyUp, handleClear, handleSubmit, withOverlay, onChange } = this.props;
const { expanded } = this.state;
const hasValue = value.length > 0 || submitted;
const classes = classNames('search', className);
const hasValue = value ? value.length > 0 || submitted : 0;
const iconClass = hasValue ? 'active' : '';
return (
<div className={classes}>
<label>
<span className='invisible'>{placeholder}</span>
<div className={[styles.default, styles.backgroundColorSubtle3, styles.positionSticky, styles.justifyContentCenter, styles.top0, styles.z2, styles.height53PX, styles.marginBottom10PX].join(' ')}>
<div className={[styles.default, styles.backgroundSubtle2, styles.flexRow, styles.circle, styles.alignItemsCenter].join(' ')}>
<Icon id='search' width='18px' height='18px' className={[styles.default, styles.marginLeft15PX, styles.marginRight10PX].join(' ')} />
<input
className='search__input'
className={[styles.default, styles.text, styles.lineHeight125, styles.displayBlock, styles.paddingVertical10PX, styles.paddingHorizontal10PX, styles.backgroundTransparent, styles.fontSize15PX, styles.flexGrow1].join(' ')}
type='text'
placeholder={placeholder}
placeholder='Search on Gab...'
value={value}
onChange={onChange}
onKeyUp={onKeyUp}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
/>
</label>
<div role='button' tabIndex='0' className='search__icon' onClick={handleClear}>
<Icon id='search' className={iconClass} />
<Icon id='times-circle' className={iconClass} aria-label={placeholder} />
<div role='button' tabIndex='0' className={[styles.default, styles.paddingHorizontal10PX].join(' ')} onClick={handleClear}>
<Icon id='times-circle' width='18px' height='18px' className={iconClass} aria-label={placeholder} />
</div>
</div>
{

View File

@ -3,8 +3,6 @@ import spring from 'react-motion/lib/spring';
import Motion from '../../features/ui/util/optional_motion';
import { searchEnabled } from '../../initial_state';
import './search_popout.scss';
export default class SearchPopout extends PureComponent {
static propTypes = {

View File

@ -2,7 +2,6 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import ToggleSwitch from '../toggle_switch';
import './setting_toggle.scss';
export default class SettingToggle extends ImmutablePureComponent {

View File

@ -1,4 +1,4 @@
import { Fragment } from 'react'
import { NavLink } from 'react-router-dom';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { injectIntl, FormattedMessage } from 'react-intl';
@ -18,8 +18,6 @@ import StatusActionBar from '../status_action_bar';
import Icon from '../icon';
import Poll from '../poll';
import './status.scss';
// We use the component (and not the container) since we do not want
// to use the progress bar to show download progress
import Bundle from '../../features/ui/util/bundle';
@ -93,6 +91,7 @@ class Status extends ImmutablePureComponent {
group: ImmutablePropTypes.map,
promoted: PropTypes.bool,
onOpenProUpgradeModal: PropTypes.func,
intl: PropTypes.object.isRequired,
};
// Avoid checking props that are functions (and whose equality will always
@ -403,7 +402,7 @@ class Status extends ImmutablePureComponent {
}
if (account === undefined || account === null) {
statusAvatar = <Avatar account={status.get('account')} size={42} />;
statusAvatar = <Avatar account={status.get('account')} size={50} />;
} else {
statusAvatar = <AvatarOverlay account={status.get('account')} friend={account} />;
}
@ -428,16 +427,13 @@ class Status extends ImmutablePureComponent {
return (
<HotKeys handlers={handlers}>
<div
className={classNames('status__wrapper', `status__wrapper-${status.get('visibility')}`, {
'status__wrapper-reply': !!status.get('in_reply_to_id'),
read: unread === false,
focusable: !this.props.muted,
})}
className={[styles.default, styles.backgroundWhite, styles.radiusSmall, styles.marginBottom15PX, styles.border1PX, styles.borderColorSubtle].join(' ')}
tabIndex={this.props.muted ? null : 0}
data-featured={featured ? 'true' : null}
aria-label={textForScreenReader(intl, status, rebloggedByText)}
ref={this.handleRef}
>
{prepend}
<div
@ -448,53 +444,84 @@ class Status extends ImmutablePureComponent {
})}
data-id={status.get('id')}
>
<div className='status__info'>
<div className='status__info__actions'>
<NavLink className='status__display-name' to={`/${status.getIn(['account', 'acct'])}`} title={status.getIn(['account', 'acct'])}>
<div className='status__avatar'>{statusAvatar}</div>
<DisplayName account={status.get('account')} />
</NavLink>
<Icon id='ellipsis-h' className='status__info__actions__icon'/>
</div>
<div className='status__info__attributes'>
<NavLink to={statusUrl} className='status__relative-time'>
<RelativeTimestamp timestamp={status.get('created_at')} />
</NavLink>
<span className='status__info__dot-seperator'></span>
{ /* <Icon id='globe' className='status__info__attributes__item'/> */ }
<span className='status__info__attributes__item status__info__attributes__item--link'>Memes Group</span>
<span className='status__info__dot-seperator'></span>
<span className='status__info__attributes__item'>Edited</span>
<div className={[styles.default, styles.paddingHorizontal15PX, styles.paddingVertical10PX].join(' ')}>
<div className={[styles.default, styles.flexRow, styles.marginTop5PX].join(' ')}>
<div className={[styles.default, styles.marginRight10PX].join(' ')}>{statusAvatar}</div>
<div className={[styles.default, styles.alignItemsStart, styles.flexGrow1, styles.marginTop5PX].join(' ')}>
<div className={[styles.default, styles.flexRow, styles.width100PC, styles.alignItemsStart].join(' ')}>
<NavLink
className={[styles.default, styles.flexRow, styles.alignItemsStart, styles.noUnderline].join(' ')}
to={`/${status.getIn(['account', 'acct'])}`}
title={status.getIn(['account', 'acct'])}
>
<DisplayName account={status.get('account')} />
</NavLink>
<Icon id='ellipsis-h' width='14px' height='15px' className={[styles.default, styles.marginLeftAuto].join(' ')} />
</div>
<div className={[styles.default, styles.flexRow, styles.alignItemsCenter, styles.lineHeight15].join(' ')}>
<NavLink
to={statusUrl}
className={[styles.default, styles.text, styles.fontSize13PX, styles.noUnderline, styles.colorSubtle].join(' ')}
>
<RelativeTimestamp timestamp={status.get('created_at')} />
</NavLink>
<span className={[styles.default, styles.text, styles.fontSize13PX, styles.marginLeft5PX, styles.colorSubtle].join(' ')}></span>
<Icon id='globe' width='12px' height='12px' className={[styles.default, styles.displayInline, styles.marginLeft5PX, styles.fillColorSubtle].join(' ')}/>
{
status.get('group') &&
<Fragment>
<span className={[styles.default, styles.text, styles.fontSize13PX, styles.marginLeft5PX, styles.colorSubtle].join(' ')}></span>
<NavLink
to={`/groups/${status.getIn(['group', 'id'])}`}
className={[styles.default, styles.text, styles.fontSize13PX, styles.marginLeft5PX, styles.colorBlack].join(' ')}
>
{status.getIn(['group', 'title'])}
</NavLink>
</Fragment>
}
{
status.get('revised_at') !== null &&
<Fragment>
<span className={[styles.default, styles.text, styles.fontSize13PX, styles.marginLeft5PX, styles.colorSubtle].join(' ')}></span>
<button
onClick={() => other.onShowRevisions(status)}
className={[styles.default, styles.text, styles.fontSize13PX, styles.marginLeft5PX, styles.colorSubtle].join(' ')}
>
Edited
</button>
</Fragment>
}
</div>
</div>
</div>
</div>
{((!group && status.get('group')) || status.get('revised_at') !== null) && (
<div className='status__meta'>
{!group && status.get('group') && <React.Fragment>Posted in <NavLink to={`/groups/${status.getIn(['group', 'id'])}`}>{status.getIn(['group', 'title'])}</NavLink></React.Fragment>}
{status.get('revised_at') !== null && <a onClick={() => other.onShowRevisions(status)}> Edited</a>}
</div>
)}
<div className={[styles.default, styles.paddingHorizontal15PX].join(' ')}>
<StatusContent
status={status}
reblogContent={reblogContent}
onClick={this.handleClick}
expanded={!status.get('hidden')}
onExpandedToggle={this.handleExpandedToggle}
collapsable
/>
</div>
<StatusContent
status={status}
reblogContent={reblogContent}
onClick={this.handleClick}
expanded={!status.get('hidden')}
onExpandedToggle={this.handleExpandedToggle}
collapsable
/>
{media}
{ /* media */ }
{ /* status.get('quote') && <StatusQuote
id={status.get('quote')}
/> */ }
{showThread && status.get('in_reply_to_id') && status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) && (
{ /* showThread && status.get('in_reply_to_id') && status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) && (
<button className='status__content__read-more-button' onClick={this.handleClick}>
<FormattedMessage id='status.show_thread' defaultMessage='Show thread' />
</button>
)}
) */ }
<StatusActionBar status={status} account={account} {...other} />
</div>

View File

@ -1,15 +1,13 @@
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 { Link } from 'react-router-dom';
import { openModal } from '../../actions/modal';
import { me, isStaff } from '../../initial_state';
import DropdownMenuContainer from '../../containers/dropdown_menu_container';
import ComposeFormContainer from '../../features/compose/containers/compose_form_container';
import IconButton from '../icon_button';
import './status_action_bar.scss';
import Icon from '../icon';
const messages = defineMessages({
delete: { id: 'status.delete', defaultMessage: 'Delete' },
@ -48,6 +46,77 @@ const mapDispatchToProps = (dispatch) => ({
},
});
class StatusActionBarItem extends PureComponent {
static propTypes = {
title: PropTypes.string.isRequired,
onClick: PropTypes.func.isRequired,
icon: PropTypes.string.isRequired,
active: PropTypes.bool,
disabled: PropTypes.bool,
}
state = {
hovering: false,
}
handleOnMouseEnter = () => {
this.setState({ hovering: true })
}
handleOnMouseLeave = () => {
this.setState({ hovering: false })
}
render() {
const { title, onClick, icon, active, disabled } = this.props
const { hovering } = this.state
const cx = classNames.bind(styles)
const btnClasses = cx({
default: 1,
text: 1,
fontSize15PX: 1,
cursorPointer: 1,
displayFlex: 1,
justifyContentCenter: 1,
flexRow: 1,
alignItemsCenter: 1,
paddingVertical10PX: 1,
paddingHorizontal10PX: 1,
width100PC: 1,
radiusSmall: 1,
backgroundTransparent: !hovering,
backgroundSubtle2: hovering,
colorBlack: !hovering,
colorBrand: hovering,
})
const iconClasses = cx({
default: 1,
marginRight10PX: 1,
fillColorSubtle: !hovering,
fillColorBrand: hovering,
})
return (
<div className={[styles.default, styles.flexGrow1, styles.paddingHorizontal10PX].join(' ')}>
<button
className={btnClasses}
onClick={onClick}
active={active}
disabled={disabled}
onMouseEnter={() => this.handleOnMouseEnter()}
onMouseLeave={() => this.handleOnMouseLeave()}
>
<Icon width='16px' height='16px' icon={icon} className={iconClasses} />
{title}
</button>
</div>
)
}
}
export default @connect(null, mapDispatchToProps)
@injectIntl
class StatusActionBar extends ImmutablePureComponent {
@ -254,7 +323,6 @@ class StatusActionBar extends ImmutablePureComponent {
const reblogCount = status.get('reblogs_count');
const reblogTitle = !publicStatus ? formatMessage(messages.cannot_reblog) : formatMessage(messages.reblog);
const reblogIcon = (status.get('visibility') === 'private') ? 'lock' : 'retweet';
const favoriteCount = status.get('favourites_count');
@ -264,55 +332,36 @@ class StatusActionBar extends ImmutablePureComponent {
const menu = this._makeMenu(publicStatus);
const items = [
{
title: formatMessage(messages.favourite),
icon: 'star',
active: status.get('favourited'),
onClick: this.handleFavouriteClick,
},
{
title: replyTitle,
icon: 'reply',
active: 0,
onClick: this.handleReplyClick,
},
{
title: reblogTitle,
icon: (status.get('visibility') === 'private') ? 'lock' : 'retweet',
disabled: !publicStatus,
active: status.get('reblogged'),
onClick: this.handleReblogClick,
}
]
return (
<div className='status-action-bar'>
<div className='status-action-bar__items'>
<div className='status-action-bar-item'>
<IconButton
className='status-action-bar-item__btn star-icon'
animate
active={status.get('favourited')}
pressed={status.get('favourited')}
title={formatMessage(messages.favourite)}
icon='star'
onClick={this.handleFavouriteClick}
text='Like'
/>
{ /* favoriteCount !== 0 && <span className='detailed-status__link'>{favoriteCount}</span> */ }
</div>
<div className='status-action-bar-item'>
<IconButton
className='status-action-bar-item__btn'
title={replyTitle}
icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon}
onClick={this.handleReplyClick}
text='Comment'
/>
{ /*
replyCount !== 0 &&
<Link to={`/${status.getIn(['account', 'acct'])}/posts/${status.get('id')}`} className='status-action-bar-item__link'>{replyCount}</Link>
*/
}
</div>
<div className='status-action-bar-item'>
<IconButton
className='status-action-bar-item__btn'
disabled={!publicStatus}
active={status.get('reblogged')}
pressed={status.get('reblogged')}
title={reblogTitle}
icon={reblogIcon}
onClick={this.handleReblogClick}
text='Share'
/>
{ /* reblogCount !== 0 && <Link to={`/${status.getIn(['account', 'acct'])}/posts/${status.get('id')}/reblogs`} className='status-action-bar-item__link'>{reblogCount}</Link> */ }
</div>
{/*<div className='status__action-bar__counter'>
<IconButton className='status__action-bar-button' disabled={!publicStatus} title={!publicStatus ? formatMessage(messages.cannot_quote) : formatMessage(messages.quote)} icon='quote-left' onClick={this.handleQuoteClick} />
</div>*/}
<div className={[styles.default, styles.marginTop10PX, styles.paddingHorizontal10PX].join(' ')}>
<div className={[styles.default, styles.paddingVertical2PX, styles.flexRow, styles.borderTop1PX, styles.borderColorSubtle].join(' ')}>
{
items.map((item, i) => (
<StatusActionBarItem {...item} />
))
}
{/*<div className='status-action-bar__dropdown'>
<DropdownMenuContainer

View File

@ -8,8 +8,6 @@ import Bundle from '../../features/ui/util/bundle';
import StatusContent from '../status_content';
import ToggleSwitch from '../toggle_switch';
import './status_check_box.scss';
const mapStateToProps = (state, { id }) => ({
status: state.getIn(['statuses', id]),
checked: state.getIn(['reports', 'new', 'status_ids'], ImmutableSet()).includes(id),

View File

@ -1,16 +1,22 @@
import { Fragment } from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { FormattedMessage } from 'react-intl';
import { FormattedMessage, injectIntl, defineMessages } from 'react-intl';
import classnames from 'classnames';
import { isRtl } from '../../utils/rtl';
import Permalink from '../permalink/permalink';
import Icon from '../icon';
import './status_content.scss';
const MAX_HEIGHT = 200;
export default class StatusContent extends ImmutablePureComponent {
const messages = defineMessages({
showMore: { id: 'status.show_more', defaultMessage: 'Show more' },
showLess: { id: 'status.show_less', defaultMessage: 'Show less' },
})
export default
@injectIntl
class StatusContent extends ImmutablePureComponent {
static contextTypes = {
router: PropTypes.object,
@ -23,6 +29,7 @@ export default class StatusContent extends ImmutablePureComponent {
onExpandedToggle: PropTypes.func,
onClick: PropTypes.func,
collapsable: PropTypes.bool,
intl: PropTypes.object.isRequired,
};
state = {
@ -145,7 +152,7 @@ export default class StatusContent extends ImmutablePureComponent {
}
render () {
const { status } = this.props;
const { status, intl } = this.props;
if (status.get('content').length === 0) return null;
@ -164,12 +171,6 @@ export default class StatusContent extends ImmutablePureComponent {
directionStyle.direction = 'rtl';
}
const readMoreButton = (
<button className='status__content__read-more-button' onClick={this.props.onClick} key='read-more'>
<FormattedMessage id='status.read_more' defaultMessage='Read more' />
</button>
);
if (status.get('spoiler_text').length > 0) {
let mentionsPlaceholder = '';
@ -179,7 +180,7 @@ export default class StatusContent extends ImmutablePureComponent {
</Permalink>
)).reduce((aggregate, item) => [...aggregate, item, ' '], []);
const toggleText = hidden ? <FormattedMessage id='status.show_more' defaultMessage='Show more' /> : <FormattedMessage id='status.show_less' defaultMessage='Show less' />;
const toggleText = intl.formatMessaeg(hidden ? messages.showMore : messages.showLess);
if (hidden) {
mentionsPlaceholder = <div>{mentionLinks}</div>;
@ -199,32 +200,37 @@ export default class StatusContent extends ImmutablePureComponent {
</div>
);
} else if (this.props.onClick) {
const output = [
<div
ref={this.setRef}
tabIndex='0'
key='content'
className={classNames}
style={directionStyle}
dangerouslySetInnerHTML={content}
lang={status.get('language')}
onMouseDown={this.handleMouseDown}
onMouseUp={this.handleMouseUp}
/>,
];
return (
<Fragment>
<div
ref={this.setRef}
tabIndex='0'
className={[styles.statusContent].join(' ')}
style={directionStyle}
dangerouslySetInnerHTML={content}
lang={status.get('language')}
onMouseDown={this.handleMouseDown}
onMouseUp={this.handleMouseUp}
/>
if (this.state.collapsed) {
output.push(readMoreButton);
}
return output;
{
this.state.collapsed &&
<button
className={[styles.default].join(' ')}
onClick={this.props.onClick}
>
<FormattedMessage id='status.read_more' defaultMessage='Read more' />
</button>
}
</Fragment>
)
}
return (
<div
tabIndex='0'
ref={this.setRef}
className='status__content'
className={[styles.statusContent].join(' ')}
style={directionStyle}
dangerouslySetInnerHTML={content}
lang={status.get('language')}

View File

@ -3,8 +3,6 @@ import StatusContent from '../status_content';
import DisplayName from '../display_name';
import { NavLink } from 'react-router-dom';
import './status_quote.scss';
const mapStateToProps = (state, { id }) => ({
status: state.getIn(['statuses', id]),
account: state.getIn(['accounts', state.getIn(['statuses', id, 'account'])]),

View File

@ -4,8 +4,6 @@ import { me } from '../../initial_state';
import ComposeFormContainer from '../../features/compose/containers/compose_form_container';
import Avatar from '../avatar';
import './timeline_compose_block.scss';
const mapStateToProps = state => {
return {
account: state.getIn(['accounts', me]),
@ -28,9 +26,9 @@ class TimelineComposeBlock extends ImmutablePureComponent {
const { account, size, ...rest } = this.props;
return (
<div className='timeline-compose-block'>
<div className='timeline-compose-block__avatar'>
<Avatar account={account} size={size} />
<div className={[styles.default, styles.flexRow, styles.radiusSmall, styles.border1PX, styles.borderColorSubtle, styles.backgroundWhite, styles.marginBottom15PX, styles.paddingVertical10PX, styles.paddingHorizontal15PX].join(' ')}>
<div className={[styles.default, styles.marginRight10PX].join(' ')}>
<Avatar account={account} size={46} />
</div>
<ComposeFormContainer {...rest} />
</div>

View File

@ -2,8 +2,6 @@ import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import { shortNumberFormat } from '../../utils/numbers';
import './timeline_queue_button_header.scss';
export default class TimelineQueueButtonHeader extends PureComponent {
static propTypes = {

View File

@ -1,7 +1,5 @@
import Toggle from 'react-toggle';
import './toggle_switch.scss';
export default class ToggleSwitch extends PureComponent {
render() {
return <Toggle {...this.props} />

View File

@ -0,0 +1,54 @@
import { FormattedMessage } from 'react-intl'
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { NavLink } from 'react-router-dom'
import classNames from 'classnames/bind'
import { shortNumberFormat } from '../utils/numbers'
export default class TrendingItem extends ImmutablePureComponent {
static propTypes = {
hashtag: ImmutablePropTypes.map.isRequired,
};
state = {
hovering: false,
}
handleOnMouseEnter = () => {
this.setState({ hovering: true })
}
handleOnMouseLeave = () => {
this.setState({ hovering: false })
}
render() {
const { hashtag } = this.props
const { hovering } = this.state
const cx = classNames.bind(styles)
const subtitleClasses = cx({
default: 1,
text: 1,
displayFlex: 1,
fontSize13PX: 1,
fontWeightNormal: 1,
colorSubtle: 1,
underline: hovering,
})
return (
<NavLink
to='/test'
className={[styles.default, styles.noUnderline, styles.marginBottom10PX].join(' ')}
onMouseEnter={() => this.handleOnMouseEnter()}
onMouseLeave={() => this.handleOnMouseLeave()}
>
<span className={[styles.default, styles.text, styles.displayFlex, styles.colorBrand, styles.fontSize15PX, styles.fontWeightBold, styles.lineHeight15].join(' ')}>#randomhashtag</span>
<span className={subtitleClasses}>10,240 Gabs</span>
</NavLink>
)
}
}

View File

@ -1 +0,0 @@
export { default } from './trending_item';

View File

@ -1,49 +0,0 @@
import { Sparklines, SparklinesCurve } from 'react-sparklines';
import { FormattedMessage } from 'react-intl';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { shortNumberFormat } from '../../utils/numbers';
import Permalink from '../permalink';
import './trending_item.scss';
export default class TrendingItem extends ImmutablePureComponent {
static propTypes = {
hashtag: ImmutablePropTypes.map.isRequired,
};
render() {
const { hashtag } = this.props;
return (
<div className='trending-item'>
<div className='trending-item__text'>
<Permalink href={hashtag.get('url')} to={`/tags/${hashtag.get('name')}`}>
#<span>{hashtag.get('name')}</span>
</Permalink>
<FormattedMessage
id='trends.count_by_accounts'
defaultMessage='{count} {rawCount, plural, one {person} other {people}} talking'
values={{
rawCount: hashtag.getIn(['history', 0, 'accounts']),
count: <strong>{shortNumberFormat(hashtag.getIn(['history', 0, 'accounts']))}</strong>,
}}
/>
</div>
<div className='trending-item__uses'>
{shortNumberFormat(hashtag.getIn(['history', 0, 'uses']))}
</div>
<div className='trending-item__sparkline'>
<Sparklines width={50} height={28} data={hashtag.get('history').reverse().map(day => day.get('uses')).toArray()}>
<SparklinesCurve style={{ fill: 'none' }} />
</Sparklines>
</div>
</div>
);
}
}

View File

@ -1,55 +0,0 @@
.trending-item {
display: flex;
align-items: center;
padding: 15px;
border-bottom: 1px solid lighten($ui-base-color, 8%);
&:last-child {
border-bottom: 0;
}
&__name {
flex: 1 1 auto;
color: $dark-text-color;
@include text-overflow(nowrap);
strong {
font-weight: 500;
}
a {
display: block;
color: $darker-text-color;
text-decoration: none;
@include text-sizing(14px, 500);
@include text-overflow(nowrap);
&:hover,
&:focus,
&:active {
span {
text-decoration: underline;
}
}
}
}
&__uses {
flex: 0 0 auto;
width: 100px;
color: $secondary-text-color;
@include text-sizing(24px, 500, 36px, center);
}
&__sparkline {
flex: 0 0 auto;
width: 50px;
path {
stroke: lighten($highlight-text-color, 6%) !important;
}
}
}

View File

@ -2,8 +2,6 @@ import { FormattedMessage } from 'react-intl';
import spring from 'react-motion/lib/spring';
import Motion from '../../features/ui/util/optional_motion';
import './upload_area.scss';
export default class UploadArea extends PureComponent {
static propTypes = {

View File

@ -0,0 +1,154 @@
import { NavLink } from 'react-router-dom'
import { injectIntl, defineMessages } from 'react-intl'
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import classNames from 'classnames/bind'
import { autoPlayGif, me } from '../initial_state'
import { makeGetAccount } from '../selectors'
import { shortNumberFormat } from '../utils/numbers'
import Avatar from './avatar'
const messages = defineMessages({
gabs: { id: 'account.posts', defaultMessage: 'Gabs' },
followers: { id: 'account.followers', defaultMessage: 'Followers' },
follows: { id: 'account.follows', defaultMessage: 'Follows' }
})
const mapStateToProps = state => {
const getAccount = makeGetAccount()
return {
account: getAccount(state, me),
}
}
export default @connect(mapStateToProps)
@injectIntl
class UserPanel extends ImmutablePureComponent {
static propTypes = {
account: ImmutablePropTypes.map,
intl: PropTypes.object.isRequired,
}
render() {
const { account, intl } = this.props
const displayNameHtml = { __html: account.get('display_name_html') }
const statItems = [
{
to: `/${account.get('acct')}`,
title: intl.formatMessage(messages.gabs),
value: shortNumberFormat(account.get('statuses_count')),
},
{
to: `/${account.get('acct')}/followers`,
title: intl.formatMessage(messages.followers),
value: shortNumberFormat(account.get('followers_count')),
},
{
to: `/${account.get('acct')}/following`,
title: intl.formatMessage(messages.follows),
value: shortNumberFormat(account.get('following_count')),
},
]
return (
<aside className={[styles.default, styles.backgroundWhite, styles.overflowHidden, styles.radiusSmall, styles.marginBottom15PX, styles.borderColorSubtle, styles.border1PX].join(' ')}>
<div className={[styles.default].join(' ')}>
<div className={[styles.default, styles.height122PX].join(' ')}>
<img
className={[styles.default, styles.image, styles.height122PX, styles.width100PC, styles.objectFitCover].join(' ')}
src={autoPlayGif ? account.get('header') : account.get('header_static')}
alt=''
/>
</div>
<div className={[styles.default, styles.positionRelative].join(' ')}>
<NavLink
className={[styles.default, styles.flexRow, styles.paddingVertical10PX, styles.paddingHorizontal15PX, styles.noUnderline].join(' ')}
to={`/${account.get('acct')}`}
>
<div className={[styles.default, styles.marginTopNeg30PX, styles.circle, styles.borderColorWhite, styles.border2PX].join(' ')}>
<Avatar account={account} size={62} />
</div>
<h1 className={[styles.default, styles.marginLeft15PX].join(' ')}>
<span
className={[styles.default, styles.text, styles.displayBlock, styles.textOverflowEllipsis, styles.fontSize14PX, styles.colorBlack, styles.fontWeightBold].join(' ')}
dangerouslySetInnerHTML={displayNameHtml}
/>
<small className={[styles.default, styles.text, styles.displayBlock, styles.textOverflowEllipsis, styles.fontWeightNormal, styles.fontSize14PX, styles.colorSubtle].join(' ')}>@{account.get('acct')}</small>
</h1>
</NavLink>
</div>
<div className={[styles.default, styles.marginBottom15PX, styles.marginTop5PX, styles.flexRow, styles.paddingHorizontal15PX].join(' ')}>
{
statItems.map((statItem, i) => (
<UserPanelStatItem {...statItem} key={`user-panel-stat-item-${i}`} />
))
}
</div>
</div>
</aside>
)
}
}
class UserPanelStatItem extends PureComponent {
static propTypes = {
to: PropTypes.string,
title: PropTypes.string,
value: PropTypes.string,
}
state = {
hovering: false,
}
handleOnMouseEnter = () => {
this.setState({ hovering: true })
}
handleOnMouseLeave = () => {
this.setState({ hovering: false })
}
render() {
const { to, title, value } = this.props
const { hovering } = this.state
const cx = classNames.bind(styles)
const titleClasses = cx({
default: 1,
fontWeightNormal: 1,
text: 1,
displayFlex: 1,
fontSize13PX: 1,
colorSubtle: !hovering,
colorBlack: hovering,
underline: hovering,
})
return (
<NavLink
to={to}
title={`${value} ${title}`}
className={[styles.default, styles.flexGrow1, styles.cursorPointer, styles.noUnderline, styles.paddingRight15PX].join(' ')}
onMouseEnter={() => this.handleOnMouseEnter()}
onMouseLeave={() => this.handleOnMouseLeave()}
>
<span
className={[styles.default, styles.fontWeightBold, styles.text, styles.displayFlex, styles.fontSize19PX, styles.colorBrand].join(' ')}
>
{value}
</span>
<strong
className={titleClasses}
>
{title}
</strong>
</NavLink>
)
}
}

View File

@ -1 +0,0 @@
export { default } from './user_panel';

View File

@ -1,95 +0,0 @@
import { Link } from 'react-router-dom';
import { injectIntl, defineMessages } from 'react-intl';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { autoPlayGif, me } from '../../initial_state';
import { makeGetAccount } from '../../selectors';
import { shortNumberFormat } from '../../utils/numbers';
import Avatar from '../avatar';
import './user_panel.scss';
const messages = defineMessages({
gabs: { id:'account.posts', defaultMessage: 'Gabs' },
followers: { id: 'account.followers', defaultMessage: 'Followers' },
follows: { id: 'account.follows', defaultMessage: 'Follows' }
});
const mapStateToProps = state => {
const getAccount = makeGetAccount();
return {
account: getAccount(state, me),
};
};
export default @connect(mapStateToProps)
@injectIntl
class UserPanel extends ImmutablePureComponent {
static propTypes = {
account: ImmutablePropTypes.map,
intl: PropTypes.object.isRequired,
}
render() {
const { account, intl } = this.props;
const displayNameHtml = { __html: account.get('display_name_html') };
return (
<div className='user-panel'>
<div className='user-panel__container'>
<div className='user-panel__header'>
<img src={autoPlayGif ? account.get('header') : account.get('header_static')} alt='' />
</div>
<div className='user-panel__profile'>
<Link to={`/${account.get('acct')}`} title={account.get('acct')}>
<Avatar account={account} />
</Link>
</div>
<div className='user-panel__meta'>
<div className='user-panel__account'>
<h1>
<Link to={`/${account.get('acct')}`}>
<span className='user-panel__account__name' dangerouslySetInnerHTML={displayNameHtml} />
<small className='user-panel__account__username'>@{account.get('acct')}</small>
</Link>
</h1>
</div>
<div className='user-panel__stats-block'>
<div className='user-panel-stats-item'>
<Link to={`/${account.get('acct')}`} title={intl.formatNumber(account.get('statuses_count'))}>
<strong className='user-panel-stats-item__value'>{shortNumberFormat(account.get('statuses_count'))}</strong>
<span className='user-panel-stats-item__label'>{intl.formatMessage(messages.gabs)}</span>
</Link>
</div>
<div className='user-panel-stats-item'>
<Link to={`/${account.get('acct')}/followers`} title={intl.formatNumber(account.get('followers_count'))}>
<strong className='user-panel-stats-item__value'>{shortNumberFormat(account.get('followers_count'))}</strong>
<span className='user-panel-stats-item__label'>{intl.formatMessage(messages.followers)}</span>
</Link>
</div>
<div className='user-panel-stats-item'>
<Link to={`/${account.get('acct')}/following`} title={intl.formatNumber(account.get('following_count'))}>
<strong className='user-panel-stats-item__value'>{shortNumberFormat(account.get('following_count'))}</strong>
<span className='user-panel-stats-item__label'>{intl.formatMessage(messages.follows)}</span>
</Link>
</div>
</div>
</div>
</div>
</div>
)
}
};

View File

@ -1,125 +0,0 @@
.user-panel {
display: flex;
width: 265px;
flex-direction: column;
overflow-y: hidden;
// @include gab-container-standards();
&__header {
display: block;
background: lighten($gab-background-container, 4%);
@include size(100%, 112px);
body.theme-gabsocial-light & {
background: darken($gab-background-container-light, 4%);
}
img {
display: block;
margin: 0;
object-fit: cover;
@include size(100%);
}
}
&__profile {
display: flex;
align-items: flex-start;
padding: 0 10px;
margin-top: -53px;
.account__avatar {
display: block;
border: 6px solid $gab-background-base;
background-size: cover;
@include size(82px);
body.theme-gabsocial-light & {
border: 6px solid $gab-background-base-light;
}
}
}
&__meta {
display: block;
padding: 6px 20px 17px 20px;
}
&__account {
a {
text-decoration: none;
color: $primary-text-color;
}
&__name {
display: block;
color: #fff;
@include text-sizing(20px, 700, 24px);
body.theme-gabsocial-light & {
color: $gab-default-text-light;
}
}
&:hover & {
&__name {
text-decoration: underline;
}
}
&__username {
display: block;
color: $gab-secondary-text;
text-decoration: none !important;
@include text-sizing(14px, 16px);
}
}
&__stats-block {
padding-top: 12px;
@include flex(space-between);
}
.user-panel-stats-item {
flex-wrap: wrap;
@include flex(left, start, column);
a {
text-decoration: none;
color: $primary-text-color;
&:hover {
opacity: 0.8;
}
}
&__value {
display: block;
width: 100%;
color: #fff;
@include text-sizing(20px, 800, 24px);
body.theme-gabsocial-light & {
color: $gab-default-text-light;
}
}
&__label {
display: block;
width: 100%;
color: $gab-secondary-text;
@include text-sizing(12px, 400, 14px);
}
}
}

View File

@ -1 +0,0 @@
export { default } from './verified_icon';

View File

@ -1,13 +0,0 @@
import './verified_icon.scss';
export default class VerifiedIcon extends PureComponent {
render() {
return (
<span className='verified-icon'>
<span className='invisible'>Verified Account</span>
</span>
);
}
};

View File

@ -1,28 +0,0 @@
.verified-icon {
display: inline-block;
margin-top: 1px;
margin-left: 4px;
vertical-align: top;
position: relative;
@include size(15px);
&:before {
background-color: #00A3ED;
border-radius: 50%;
@include pseudo;
@include abs-position(0, 0, 0, 0, false);
}
&:after {
font: normal normal normal 14px/1 FontAwesome;
text-rendering: auto;
-webkit-font-smoothing: antialiased;
color: #fff;
@include pseudo('\f00c');
@include size(15px);
@include text-sizing(0.6em, 400, 15px, center);
}
}

View File

@ -1,5 +1,3 @@
import './zoomable_image.scss';
const MIN_SCALE = 1;
const MAX_SCALE = 4;

View File

@ -15,8 +15,6 @@ import MediaItem from './components/media_item';
import LoadMore from '../../components/load_more';
import SectionHeadlineBar from '../../components/section_headline_bar';
import './account_gallery.scss';
const messages = defineMessages({
posts: { id: 'account.posts', defaultMessage: 'Gabs' },
postsWithReplies: { id: 'account.posts_with_replies', defaultMessage: 'Gabs and replies' },

View File

@ -6,8 +6,6 @@ import Icon from '../../../../components/icon';
import { autoPlayGif, displayMedia } from '../../../../initial_state';
import { isIOS } from '../../../../utils/is_mobile';
import './media_item.scss';
export default class MediaItem extends ImmutablePureComponent {
static propTypes = {

View File

@ -3,8 +3,6 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
import InnerHeader from '../inner_header';
import MovedNote from '../moved_note';
import './header.scss';
export default class Header extends ImmutablePureComponent {
static propTypes = {

View File

@ -1 +1 @@
export { default } from './inner_header.scss';
export { default } from './inner_header';

View File

@ -11,8 +11,6 @@ import { shortNumberFormat } from '../../../utils/numbers';
import DropdownMenuContainer from '../../../containers/dropdown_menu_container';
import ProfileInfoPanel from './profile_info_panel/profile_info_panel';
import './inner_header.scss';
const messages = defineMessages({
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
follow: { id: 'account.follow', defaultMessage: 'Follow' },

View File

@ -6,8 +6,6 @@ import AvatarOverlay from '../../../../components/avatar_overlay';
import DisplayName from '../../../../components/display_name';
import Icon from '../../../../components/icon';
import './moved_note.scss';
export default class MovedNote extends ImmutablePureComponent {
static contextTypes = {

View File

@ -6,8 +6,6 @@ import Icon from '../../../../components/icon';
import VerifiedIcon from '../../../../components/verified_icon';
import Badge from '../../../../components/badge';
import './profile_info_panel.scss';
const messages = defineMessages({
linkVerifiedOn: { id: 'account.link_verified_on', defaultMessage: 'Ownership of this link was checked on {date}' },
account_locked: { id: 'account.locked_info', defaultMessage: 'This account privacy status is set to locked. The owner manually reviews who can follow them.' },

View File

@ -1,8 +1,6 @@
import { length } from 'stringz';
import classNames from 'classnames';
import './character_counter.scss';
export default class CharacterCounter extends PureComponent {
static propTypes = {

View File

@ -0,0 +1,71 @@
import classNames from 'classnames/bind'
import Icon from '../../../components/icon'
export default class ComposeExtraButton extends PureComponent {
static propTypes = {
title: PropTypes.string,
disabled: PropTypes.bool,
onClick: PropTypes.func,
icon: PropTypes.string,
}
state = {
hovering: false,
}
handleOnMouseEnter = () => {
this.setState({ hovering: true })
}
handleOnMouseLeave = () => {
this.setState({ hovering: false })
}
render() {
const { title, disabled, onClick, icon, children } = this.props
const { hovering } = this.state
const cx = classNames.bind(styles)
const btnClasses = cx({
default: 1,
circle: 1,
flexRow: 1,
paddingVertical10PX: 1,
paddingHorizontal10PX: 1,
cursorPointer: 1,
backgroundSubtle: !hovering,
backgroundSubtle2: hovering,
})
const titleClasses = cx({
default: 1,
marginLeft5PX: 1,
text: 1,
lineHeight15: 1,
fontSize12PX: 1,
fontWeight500: 1,
colorSubtle: 1,
displayNone: !hovering,
})
return (
<div className={[styles.default, styles.marginRight10PX].join(' ')}>
<button
className={btnClasses}
title={title}
disabled={disabled}
onClick={onClick}
onMouseEnter={() => this.handleOnMouseEnter()}
onMouseLeave={() => this.handleOnMouseLeave()}
>
<Icon icon={icon} width='18px' height='18px' />
<span className={titleClasses}>
{title}
</span>
</button>
{children}
</div>
)
}
}

View File

@ -7,22 +7,20 @@ import CharacterCounter from '../character_counter';
import UploadForm from '../upload_form';
import ReplyIndicatorContainer from '../../containers/reply_indicator_container';
import AutosuggestTextbox from '../../../../components/autosuggest_textbox';
import PollButtonContainer from '../../containers/poll_button_container';
import UploadButtonContainer from '../../containers/upload_button_container';
import SpoilerButtonContainer from '../../containers/spoiler_button_container';
import PrivacyDropdownContainer from '../../containers/privacy_dropdown_container';
import PollButton from '../../components/poll_button';
import UploadButton from '../../components/upload_button';
import SpoilerButton from '../../components/spoiler_button';
import PrivacyDropdown from '../../components/privacy_dropdown';
import EmojiPickerDropdown from '../../containers/emoji_picker_dropdown_container';
import PollFormContainer from '../../containers/poll_form_container';
import WarningContainer from '../../containers/warning_container';
import SchedulePostDropdownContainer from '../../containers/schedule_post_dropdown_container';
import SchedulePostDropdown from '../../components/schedule_post_dropdown';
import QuotedStatusPreviewContainer from '../../containers/quoted_status_preview_container';
import Icon from '../../../../components/icon';
import Button from '../../../../components/button';
import { isMobile } from '../../../../utils/is_mobile';
import { countableText } from '../../util/counter';
import './compose_form.scss';
const allowedAroundShortCode = '><\u0085\u0020\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029\u0009\u000a\u000b\u000c\u000d';
const maxPostCharacterCount = 3000;
@ -30,7 +28,7 @@ const messages = defineMessages({
placeholder: { id: 'compose_form.placeholder', defaultMessage: 'What is on your mind?' },
spoiler_placeholder: { id: 'compose_form.spoiler_placeholder', defaultMessage: 'Write your warning here' },
publish: { id: 'compose_form.publish', defaultMessage: 'Gab' },
publishLoud: { id: 'compose_form.publish_loud', defaultMessage: '{publish}!' },
publishLoud: { id: 'compose_form.publish_loud', defaultMessage: '{publish}' },
schedulePost: { id: 'compose_form.schedule_post', defaultMessage: 'Schedule Post' }
});
@ -220,29 +218,22 @@ class ComposeForm extends ImmutablePureComponent {
const disabledButton = disabled || this.props.isUploading || this.props.isChangingUpload || length(text) > maxPostCharacterCount || (text.length !== 0 && text.trim().length === 0 && !anyMedia);
const shouldAutoFocus = autoFocus && !showSearch && !isMobile(window.innerWidth)
let publishText = '';
if (this.props.privacy === 'private' || this.props.privacy === 'direct') {
publishText = <span className='compose-form__publish-private'><Icon id='lock' /> {intl.formatMessage(messages.publish)}</span>;
} else {
publishText = this.props.privacy !== 'unlisted' ? intl.formatMessage(messages.publishLoud, { publish: intl.formatMessage(messages.publish) }) : intl.formatMessage(messages.publish);
}
if (scheduledAt) {
publishText = intl.formatMessage(messages.schedulePost);
}
const composeClassNames = classNames({
'compose-form': true,
'condensed': condensed,
});
return (
<div className={composeClassNames} ref={this.setForm} onClick={this.handleClick}>
<WarningContainer />
<div
className={[styles.default, styles.flexGrow1].join(' ')}
ref={this.setForm}
onClick={this.handleClick}
>
{ /* <WarningContainer /> */ }
{ !shouldCondense && <ReplyIndicatorContainer /> }
{ /* !shouldCondense && <ReplyIndicatorContainer /> */ }
{ /*
<div className={`spoiler-input ${this.props.spoiler ? 'spoiler-input--visible' : ''}`}>
<AutosuggestTextbox
placeholder={intl.formatMessage(messages.spoiler_placeholder)}
@ -260,13 +251,14 @@ class ComposeForm extends ImmutablePureComponent {
className='spoiler-input__input'
/>
</div>
*/ }
{ /*
<div className='emoji-picker-wrapper'>
<EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} />
</div>
</div> */ }
<AutosuggestTextbox
textarea={true}
ref={(isModalOpen && shouldCondense) ? null : this.setAutosuggestTextarea}
placeholder={intl.formatMessage(messages.placeholder)}
disabled={disabled}
@ -280,38 +272,37 @@ class ComposeForm extends ImmutablePureComponent {
onSuggestionSelected={this.onSuggestionSelected}
onPaste={onPaste}
autoFocus={shouldAutoFocus}
textarea
>
{
{ /*
!condensed &&
<div className='compose-form__modifiers'>
<UploadForm />
{!edit && <PollFormContainer />}
</div>
}
*/ }
</AutosuggestTextbox>
{quoteOfId && <QuotedStatusPreviewContainer id={quoteOfId} />}
{ /* quoteOfId && <QuotedStatusPreviewContainer id={quoteOfId} /> */ }
{
!condensed &&
<div className='compose-form__buttons-wrapper'>
<div className='compose-form__buttons'>
<UploadButtonContainer />
{!edit && <PollButtonContainer />}
<PrivacyDropdownContainer />
<SpoilerButtonContainer />
<SchedulePostDropdownContainer
position={isModalOpen ? 'top' : undefined}
/>
/* !condensed && */
<div className={[styles.default, styles.flexRow, styles.marginTop10PX].join(' ')}>
<div className={[styles.default, styles.flexRow, styles.marginRightAuto].join(' ')}>
<UploadButton />
{
!edit && <PollButton />
}
<PrivacyDropdown />
<SpoilerButton />
<SchedulePostDropdown position={isModalOpen ? 'top' : undefined} />
</div>
<CharacterCounter max={maxPostCharacterCount} text={text} />
</div>
}
{
!condensed &&
<div className='compose-form__publish'>
<div className='compose-form__publish-button-wrapper'><Button text={publishText} onClick={this.handleSubmit} disabled={disabledButton} block /></div>
<Button
text={intl.formatMessage(scheduledAt ? messages.schedulePost : messages.publish)}
onClick={this.handleSubmit}
disabled={disabledButton}
/>
</div>
}

View File

@ -7,8 +7,6 @@ import Overlay from 'react-overlays/lib/Overlay';
import { EmojiPicker as EmojiPickerAsync } from '../../../ui/util/async-components';
import { buildCustomEmojis } from '../../../../components/emoji/emoji';
import './emoji_picker_dropdown.scss';
const messages = defineMessages({
emoji: { id: 'emoji_button.label', defaultMessage: 'Insert emoji' },
emoji_search: { id: 'emoji_button.search', defaultMessage: 'Search...' },

View File

@ -7,8 +7,6 @@ import Permalink from '../../../../components/permalink';
import IconButton from '../../../../components/icon_button';
import { me } from '../../../../initial_state';
import './navigation_bar.scss';
const mapStateToProps = state => {
return {
account: state.getIn(['accounts', me]),

Some files were not shown because too many files have changed in this diff Show More