Progress
This commit is contained in:
parent
fa66d082f8
commit
1c98dd283e
@ -3,7 +3,6 @@
|
||||
import Rails from 'rails-ujs';
|
||||
|
||||
export function start() {
|
||||
require('font-awesome/css/font-awesome.css');
|
||||
require.context('../images/', true);
|
||||
|
||||
try {
|
||||
|
@ -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(' ')}>
|
||||
|
||||
<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 className='account__relationship'>
|
||||
{buttons}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
import './gab_logo.scss'
|
||||
|
||||
const GabLogo = ({
|
||||
width = '50px',
|
||||
height = '30px',
|
@ -1,9 +0,0 @@
|
||||
.gab-logo {
|
||||
enable-background: new 0 0 50 30;
|
||||
|
||||
&__path {
|
||||
fill-rule: evenodd;
|
||||
clip-rule: evenodd;
|
||||
fill: $gab-brand-default;
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
export { default } from './gab_logo';
|
@ -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();
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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>,
|
||||
<div className='autosuggest-textarea__suggestions-wrapper' key='autosuggest-textarea__suggestions-wrapper'>
|
||||
</div>
|
||||
<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 (
|
||||
|
@ -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')}
|
||||
/>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 = {
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
};
|
@ -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;
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
export { default } from './badge';
|
@ -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.' },
|
||||
|
@ -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' },
|
||||
|
@ -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,
|
||||
})
|
||||
|
@ -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 = {
|
||||
|
@ -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 = {
|
||||
|
@ -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' },
|
||||
|
@ -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' },
|
||||
|
@ -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 = {
|
||||
|
@ -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.' },
|
||||
|
@ -1,7 +1,5 @@
|
||||
import Button from '../button';
|
||||
|
||||
import './column_inline_form.scss';
|
||||
|
||||
export default class ColumnInlineForm extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
|
@ -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 = {
|
||||
|
@ -1,5 +1,3 @@
|
||||
import './column_settings_heading.scss';
|
||||
|
||||
export default class ColumnSettingsHeading extends PureComponent {
|
||||
static propTypes = {
|
||||
heading: PropTypes.object.isRequired,
|
||||
|
@ -1,5 +1,3 @@
|
||||
import './column_subheading.scss';
|
||||
|
||||
export default class ColumnSubheading extends PureComponent {
|
||||
static propTypes = {
|
||||
text: PropTypes.string.isRequired,
|
||||
|
36
app/javascript/gabsocial/components/display_name.js
Normal file
36
app/javascript/gabsocial/components/display_name.js
Normal 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>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
export { default } from './display_name';
|
@ -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}' },
|
||||
});
|
||||
|
@ -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;
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import './error_boundary.scss';
|
||||
|
||||
export default class ErrorBoundary extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
|
@ -1,5 +1,3 @@
|
||||
import './extended_video_player.scss';
|
||||
|
||||
export default class ExtendedVideoPlayer extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
|
@ -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,
|
||||
|
@ -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'/>
|
||||
|
@ -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>
|
||||
)
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
import classNames from 'classnames';
|
||||
|
||||
import './icon.scss';
|
||||
export default class Icon extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
|
@ -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,8 +75,6 @@ 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}
|
||||
@ -87,32 +87,10 @@ export default class IconButton extends PureComponent {
|
||||
tabIndex={tabIndex}
|
||||
disabled={disabled}
|
||||
>
|
||||
<Icon id={icon} fixedWidth aria-hidden='true' />
|
||||
<Icon id={icon} fixedWidth aria-hidden='true' width={width} height={height} />
|
||||
{!!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>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 = {
|
||||
|
@ -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
|
||||
|
@ -1,4 +1,4 @@
|
||||
article {
|
||||
// TEMPORARY - content of columns may be significantly altered
|
||||
background: $gab-background-container;
|
||||
// background: $gab-background-container;
|
||||
}
|
@ -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,
|
||||
|
@ -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' },
|
||||
});
|
||||
|
@ -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' },
|
||||
|
@ -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),
|
||||
});
|
||||
|
@ -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' },
|
||||
|
@ -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' },
|
||||
|
@ -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' },
|
||||
|
@ -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 {
|
||||
|
@ -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' },
|
||||
});
|
||||
|
@ -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 {
|
||||
|
@ -1,7 +1,5 @@
|
||||
import { shortNumberFormat } from '../../utils/numbers';
|
||||
|
||||
import './notification_counter.scss';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
count: state.getIn(['notifications', 'unread']),
|
||||
});
|
||||
|
@ -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>
|
||||
|
154
app/javascript/gabsocial/components/panel/groups_panel.js
Normal file
154
app/javascript/gabsocial/components/panel/groups_panel.js
Normal 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)}
|
||||
|
||||
{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>
|
||||
)
|
||||
}
|
||||
}
|
@ -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>
|
||||
{subtitle && <span className={[styles.default, styles.text, styles.colorSubtle, styles.fontSize13PX, styles.marginTop5PX].join(' ')}>{subtitle}</span>}
|
||||
}
|
||||
</div>
|
||||
{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(' ')}>
|
||||
|
@ -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}/>
|
||||
|
@ -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>
|
||||
)
|
||||
}
|
||||
}
|
@ -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>
|
||||
);
|
||||
|
@ -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}
|
||||
|
@ -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]),
|
||||
});
|
||||
|
@ -1 +0,0 @@
|
||||
export { default } from './progress_bar';
|
@ -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 {
|
||||
|
@ -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>
|
||||
|
||||
{
|
||||
|
@ -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 = {
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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,33 +444,63 @@ 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>
|
||||
|
||||
<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' className='status__info__actions__icon'/>
|
||||
<Icon id='ellipsis-h' width='14px' height='15px' className={[styles.default, styles.marginLeftAuto].join(' ')} />
|
||||
</div>
|
||||
<div className='status__info__attributes'>
|
||||
<NavLink to={statusUrl} className='status__relative-time'>
|
||||
<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='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>
|
||||
<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}
|
||||
@ -483,18 +509,19 @@ class Status extends ImmutablePureComponent {
|
||||
onExpandedToggle={this.handleExpandedToggle}
|
||||
collapsable
|
||||
/>
|
||||
</div>
|
||||
|
||||
{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>
|
||||
|
@ -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);
|
||||
|
||||
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>
|
||||
*/
|
||||
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,
|
||||
}
|
||||
</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>*/}
|
||||
return (
|
||||
<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
|
||||
|
@ -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),
|
||||
|
@ -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 = [
|
||||
return (
|
||||
<Fragment>
|
||||
<div
|
||||
ref={this.setRef}
|
||||
tabIndex='0'
|
||||
key='content'
|
||||
className={classNames}
|
||||
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);
|
||||
{
|
||||
this.state.collapsed &&
|
||||
<button
|
||||
className={[styles.default].join(' ')}
|
||||
onClick={this.props.onClick}
|
||||
>
|
||||
<FormattedMessage id='status.read_more' defaultMessage='Read more' />
|
||||
</button>
|
||||
}
|
||||
|
||||
return output;
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
tabIndex='0'
|
||||
ref={this.setRef}
|
||||
className='status__content'
|
||||
className={[styles.statusContent].join(' ')}
|
||||
style={directionStyle}
|
||||
dangerouslySetInnerHTML={content}
|
||||
lang={status.get('language')}
|
||||
|
@ -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'])]),
|
||||
|
@ -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>
|
||||
|
@ -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 = {
|
||||
|
@ -1,7 +1,5 @@
|
||||
import Toggle from 'react-toggle';
|
||||
|
||||
import './toggle_switch.scss';
|
||||
|
||||
export default class ToggleSwitch extends PureComponent {
|
||||
render() {
|
||||
return <Toggle {...this.props} />
|
||||
|
54
app/javascript/gabsocial/components/trending_item.js
Normal file
54
app/javascript/gabsocial/components/trending_item.js
Normal 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>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -1 +0,0 @@
|
||||
export { default } from './trending_item';
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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 = {
|
||||
|
154
app/javascript/gabsocial/components/user_panel.js
Normal file
154
app/javascript/gabsocial/components/user_panel.js
Normal 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>
|
||||
)
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
export { default } from './user_panel';
|
@ -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>
|
||||
)
|
||||
}
|
||||
};
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
export { default } from './verified_icon';
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
};
|
@ -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);
|
||||
}
|
||||
}
|
@ -1,5 +1,3 @@
|
||||
import './zoomable_image.scss';
|
||||
|
||||
const MIN_SCALE = 1;
|
||||
const MAX_SCALE = 4;
|
||||
|
||||
|
@ -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' },
|
||||
|
@ -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 = {
|
||||
|
@ -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 = {
|
||||
|
@ -1 +1 @@
|
||||
export { default } from './inner_header.scss';
|
||||
export { default } from './inner_header';
|
||||
|
@ -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' },
|
||||
|
@ -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 = {
|
||||
|
@ -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.' },
|
||||
|
@ -1,8 +1,6 @@
|
||||
import { length } from 'stringz';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import './character_counter.scss';
|
||||
|
||||
export default class CharacterCounter extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
|
@ -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>
|
||||
)
|
||||
}
|
||||
}
|
@ -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>
|
||||
}
|
||||
|
||||
|
@ -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...' },
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user