This commit is contained in:
mgabdev 2020-02-05 17:45:48 -05:00
parent df346596cf
commit fa66d082f8
22 changed files with 553 additions and 480 deletions

View File

@ -1,47 +1,92 @@
import classNames from 'classnames';
import './button.scss';
import classNames from 'classnames/bind'
export default class Button extends PureComponent {
static propTypes = {
text: PropTypes.node,
href: PropTypes.string,
onClick: PropTypes.func,
disabled: PropTypes.bool,
block: PropTypes.bool,
secondary: PropTypes.bool,
className: PropTypes.string,
children: PropTypes.node,
};
className: PropTypes.string,
}
state = {
hovering: false,
}
handleOnMouseEnter = () => {
this.setState({
hovering: true,
})
}
handleOnMouseLeave = () => {
this.setState({
hovering: false,
})
}
handleClick = (e) => {
if (!this.props.disabled && this.props.onClick) {
this.props.onClick(e);
this.props.onClick(e)
}
}
setRef = (c) => {
this.node = c;
this.node = c
}
focus() {
this.node.focus();
this.node.focus()
}
render () {
const className = classNames('button', this.props.className, {
'button--secondary': this.props.secondary,
'button--block': this.props.block,
});
const { secondary, block, className, disabled, text, children, href } = this.props
const { hovering } = this.state
const cx = classNames.bind(styles)
const classes = cx(className, {
default: 1,
noUnderline: 1,
font: 1,
colorWhite: 1,
circle: 1,
cursorPointer: 1,
width100PC: 1,
textAlignCenter: 1,
paddingVertical10PX: 1,
paddingHorizontal15PX: 1,
backgroundColorBrand: !hovering,
backgroundColorBrandDark: hovering,
})
if (href) {
return (
<a
className={classes}
href={href}
onMouseEnter={() => this.handleOnMouseEnter()}
onMouseLeave={() => this.handleOnMouseLeave()}
>
{text || children}
</a>
)
}
return (
<button
className={className}
disabled={this.props.disabled}
onClick={this.handleClick}
ref={this.setRef}
disabled={disabled}
onClick={this.handleClick}
className={classes}
onMouseEnter={() => this.handleOnMouseEnter()}
onMouseLeave={() => this.handleOnMouseLeave()}
>
{this.props.text || this.props.children}
{text || children}
</button>
);
}

View File

@ -1,23 +1,23 @@
import { Link, 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';
import Avatar from './avatar';
import IconButton from './icon_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';
import GabLogo from './assets/gab_logo';
import { Link, 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'
import GabLogo from './assets/gab_logo'
import {
GroupIcon,
HomeIcon,
NotificationsIcon,
} from './assets/tabs_bar_icon';
} from './assets/tabs_bar_icon'
const messages = defineMessages({
followers: { id: 'account.followers', defaultMessage: 'Followers' },
@ -41,19 +41,19 @@ const messages = defineMessages({
})
const mapStateToProps = state => {
const getAccount = makeGetAccount();
const getAccount = makeGetAccount()
return {
account: getAccount(state, me),
sidebarOpen: state.get('sidebar').sidebarOpen,
};
};
}
}
const mapDispatchToProps = (dispatch) => ({
onClose() {
dispatch(closeSidebar());
dispatch(closeSidebar())
},
});
})
export default @connect(mapStateToProps, mapDispatchToProps)
@injectIntl
@ -64,50 +64,65 @@ class Header extends ImmutablePureComponent {
account: ImmutablePropTypes.map,
sidebarOpen: PropTypes.bool,
onClose: PropTypes.func.isRequired,
};
}
state = {
moreOpen: false,
hoveringItemIndex: null,
}
componentDidUpdate() {
if (!me) return;
if (!me) return
if (this.props.sidebarOpen) {
document.body.classList.add('with-modals--active');
document.body.classList.add('with-modals--active')
} else {
document.body.classList.remove('with-modals--active');
document.body.classList.remove('with-modals--active')
}
}
toggleMore = () => {
this.setState({
moreOpen: !this.state.moreOpen
});
})
}
onMouseEnterLinkFooterItem = (i) => {
this.setState({
hoveringItemIndex: i,
})
}
onMouseLeaveLinkFooterItem = () => {
this.setState({
hoveringItemIndex: null,
})
}
handleSidebarClose = () => {
this.props.onClose();
this.props.onClose()
this.setState({
moreOpen: false,
});
})
}
render() {
const { sidebarOpen, intl, account } = this.props;
const { moreOpen } = this.state;
const { sidebarOpen, intl, account } = this.props
const { moreOpen, hoveringItemIndex } = this.state
if (!me || !account) return null;
if (!me || !account) return null
const acct = account.get('acct');
const isPro = account.get('is_pro');
const acct = account.get('acct')
const isPro = account.get('is_pro')
const classes = classNames('sidebar-menu__root', {
'sidebar-menu__root--visible': sidebarOpen,
});
// const classes = classNames('sidebar-menu__root', {
// 'sidebar-menu__root--visible': sidebarOpen,
// })
const moreIcon = moreOpen ? 'minus' : 'plus';
const moreContainerStyle = { display: moreOpen ? 'block' : 'none' };
const cx = classNames.bind(styles)
const moreIcon = moreOpen ? 'minus' : 'plus'
const moreContainerStyle = { display: moreOpen ? 'block' : 'none' }
const sidebarItems = [
{
@ -145,13 +160,13 @@ class Header extends ImmutablePureComponent {
icon: <NotificationsIcon />,
to: '/',
},
];
]
return (
<header role='banner' className={[styles.default, styles.flexGrow1, styles.z3, styles.alignItemsEnd].join(' ')}>
<div className={[styles.default, styles.width275PX].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.width275PX, styles.paddingHorizontal20PX].join(' ')}>
<div className={[styles.default, styles.height100PC, styles.width250PX, styles.paddingHorizontal20PX].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(' ')}>
<GabLogo />
@ -160,14 +175,45 @@ class Header extends ImmutablePureComponent {
<nav aria-label='Primary' role='navigation' className={[styles.default, styles.width100PC].join(' ')}>
{
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}`} className={[styles.default, styles.noUnderline, styles.cursorPointer, styles.width100PC, styles.alignItemsStart, styles.flexGrow1].join(' ')}>
<div className={[styles.default, styles.maxWidth100PC, styles.flexRow, styles.paddingVertical10PX, styles.paddingHoizontal10PX, styles.circle, styles.alignItemsCenter].join(' ')}>
<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]}>
{sidebarItem.icon}
<NotificationsIcon className={iconClasses}/>
</div>
<div className={[styles.default, styles.paddingHorizontal20PX, styles.textOverflowEllipsis, styles.overflowWrapBreakWord, styles.displayInline].join(' ')}>
<span className={[styles.default, styles.fontWeightBold, styles.fontSize19PX, styles.text].join(' ')}>
<span className={textClasses}>
{sidebarItem.name}
</span>
</div>
@ -177,128 +223,129 @@ class Header extends ImmutablePureComponent {
})
}
</nav>
<Button className={[styles.paddingVertical15PX, styles.marginVertical5PX, 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'>
// 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-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__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'>
// <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 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>
<div className='sidebar-menu__section'>
<ProgressPanel />
</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 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'>
// <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 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>
);
// </div>
// </div>
// </div>
// )
}
}

View File

@ -6,18 +6,36 @@ export default class Icon extends PureComponent {
static propTypes = {
id: PropTypes.string.isRequired,
className: PropTypes.string,
fixedWidth: PropTypes.bool,
width: PropTypes.string,
height: PropTypes.string,
};
static defaultProps = {
width: '26px',
height: '26px',
};
render () {
const { id, className, fixedWidth, ...other } = this.props;
const classes = classNames('fa', `fa-${id}`, className, {
'fa-fw': fixedWidth,
});
const { className, width, height } = this.props;
return (
<i role='img' alt={id} className={classes} {...other} />
<svg
className={className}
version='1.1'
xmlns='http://www.w3.org/2000/svg'
x='0px'
y='0px'
width={width}
height={height}
viewBox='0 0 48 48'
xmlSpace='preserve'
>
<g>
<path d='M 17 40 C 18 46 21 48 24 48 C 26 48 29 46 30 40 Z M 17 40' />
<path d='M 24 4 C 25 4 27 4 29 5 L 29 5 C 29 2 27 0 24 0 L 23 0 C 20 0 18 2 18 5 L 18 5 C 20 4 22 4 24 4 Z M 24 4' />
<path d='M 41 40 L 6 40 C 5 40 5 40 5 39 C 4 39 5 38 5 38 C 5 38 6 37 8 35 C 9 30 10 25 10 21 C 10 13 16 7 24 7 C 31 7 37 13 37 20 C 37 20 37 20 37 21 C 37 25 38 30 39 35 C 41 37 42 38 42 38 C 42 38 43 39 42 39 C 42 40 42 40 41 40 Z M 42 38 Z M 42 38' />
</g>
</svg>
);
}

View File

@ -1,12 +1,12 @@
import moment from 'moment'
import classNames from 'classnames/bind'
import {
FormattedMessage,
defineMessages,
injectIntl,
} from 'react-intl'
import { openModal } from '../actions/modal'
import {
version,
repository,
source_url,
me,
@ -23,7 +23,6 @@ const messages = defineMessages({
terms: { id: 'getting_started.terms_of_sale', defaultMessage: 'Terms of Sale' },
privacy: { id: 'getting_started.privacy', defaultMessage: 'Privacy Policy' },
logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
notice: { id: 'getting_started.open_source_notice', defaultMessage: 'Gab Social is open source software. You can contribute or report issues on our self-hosted GitLab at {gitlab}.' },
})
const mapDispatchToProps = (dispatch) => ({
@ -61,7 +60,7 @@ class LinkFooter extends PureComponent {
const { onOpenHotkeys, intl } = this.props
const { hoveringItemIndex } = this.state
const cx = classNames.bind(styles);
const cx = classNames.bind(styles)
const currentYear = moment().format('YYYY')
@ -121,17 +120,33 @@ class LinkFooter extends PureComponent {
text: 1,
marginVertical5PX: 1,
paddingRight15PX: 1,
cursorPointer: 1,
colorSubtle: i !== hoveringItemIndex,
noUnderline: i !== hoveringItemIndex,
colorBlack: i === hoveringItemIndex,
underline: i === hoveringItemIndex,
})
if (linkFooterItem.onClick) {
return (
<button
key={`link-footer-item-${i}`}
data-method={linkFooterItem.logout ? 'delete' : null}
onClick={linkFooterItem.onClick || null}
onMouseEnter={() => this.onMouseEnterLinkFooterItem(i)}
onMouseLeave={() => this.onMouseLeaveLinkFooterItem(i)}
className={classes}
>
{linkFooterItem.text}
</button>
)
}
return (
<a
key={`link-footer-item-${i}`}
href={linkFooterItem.to}
data-method={linkFooterItem.logout ? 'delete' : null}
onClick={linkFooterItem.onClick || null}
onMouseEnter={() => this.onMouseEnterLinkFooterItem(i)}
onMouseLeave={() => this.onMouseLeaveLinkFooterItem(i)}
className={classes}
@ -141,19 +156,16 @@ class LinkFooter extends PureComponent {
)
})
}
<span className={[styles.default, styles.text, styles.fontSize13PX, styles.colorSubtle, styles.marginVertical5PX].join(' ')}>© {currentYear} Gab AI, Inc.</span>
</nav>
<p>
{intl.formatMessage(messages.invite, {
gitlab: (
<span>
<a href={source_url} rel='noopener' target='_blank'>{repository}</a>
(v{version})
</span>
)
})}
<p className={[styles.default, styles.text, styles.fontSize13PX, styles.colorSubtle, styles.marginTop10PX].join(' ')}>
<FormattedMessage
id='getting_started.open_source_notice'
defaultMessage='Gab Social is open source software. You can contribute or report issues on our self-hosted GitLab at {gitlab}.'
values={{ gitlab: <a href={source_url} className={[styles.inherit].join(' ')} rel='noopener' target='_blank'>{repository}</a> }}
/>
</p>
<p>© {currentYear} Gab AI Inc.</p>
</div>
)
}

View File

@ -3,24 +3,24 @@ import Header from './header'
export default class PageLayout extends PureComponent {
static propTypes = {
layout: PropTypes.object,
};
}
render() {
const { children, layout } = this.props;
const { children, layout } = this.props
const right = layout.RIGHT || null;
const right = layout.RIGHT || null
return (
<div className={[styles.default, styles.flexRow, styles.width100PC, styles.height100PC].join(' ')}>
<div className={[styles.default, styles.flexRow, styles.width100PC].join(' ')}>
<Header />
<main role='main' className={[styles.default, styles.flexShrink1, styles.flexGrow1, styles.flexRow].join(' ')}>
<div className={[styles.default, styles.width990PX, styles.flexRow].join(' ')}>
<div className={[styles.default, styles.width600PX, styles.z1].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}>
{children}
</div>
</div>
<div className={[styles.default, styles.width350PX].join(' ')}>
<div className={[styles.default, styles.width325PX].join(' ')}>
<div className={styles.default}>
{right}
</div>

View File

@ -1,130 +0,0 @@
.panel {
display: flex;
border-radius: 10px;
flex-direction: column;
box-sizing: border-box;
background: $gab-background-container;
@include size(100%, auto);
body.theme-gabsocial-light & {
// @include light-theme-shadow();
background: $gab-background-container-light;
}
&:not(:last-of-type) {
margin-bottom: 10px;
}
&__content {
width: 100%;
padding-top: 8px;
}
&__list {
padding: 0 5px;
}
&__subtitle {
display: block;
padding: 0 15px;
color: $secondary-text-color;
}
&__form {
display: block;
padding: 15px;
&.button {
width: 100%;
}
}
.wtf-panel-list-item {
display: block;
padding-bottom: 10px;
&:not(:first-of-type) {
margin-top: 12px;
}
&:not(:last-of-type) {
border-bottom: 1px solid lighten($ui-base-color, 8%);
}
&__content {
display: flex;
flex-direction: row;
min-height: 46px;
margin-left: 58px;
}
&__account-block {
display: flex;
position: relative;
align-items: baseline;
padding-right: 10px;
&__avatar {
background-color: red;
left: -58px;
position: absolute;
@include size(46px);
}
&__name {
display: flex;
flex-wrap: wrap;
flex-direction: column;
margin-top: 6px;
&__name {
color: $primary-text-color;
margin-bottom: 2px;
max-height: 32px; //2 lines of text
overflow: hidden;
@include text-sizing(14px, 700, 16px);
}
&__username {
color: $lighter-text-color;
@include text-sizing(12px, 400, 14px);
}
}
}
&__follow-block {
margin-left: auto;
padding-top: 6px;
&__button {
display: flex;
}
&__icon {
color: $primary-text-color;
}
}
}
}
.panel-header {
display: flex;
align-items: baseline;
margin-bottom: 10px;
padding: 15px 15px 0 15px;
&__icon {
margin-right: 10px;
}
&__title {
flex: 1 1;
color: $primary-text-color;
@include text-sizing(16px, 700, 19px);
}
}

View File

@ -1,26 +1,34 @@
import './panel.scss';
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,
}
render() {
const {title, icon, children} = this.props;
const { title, subtitle, icon, hasBackground, children } = this.props
return (
<div className='panel'>
<div className='panel-header'>
{icon && <Icon id={icon} className='panel-header__icon' />}
<span className='panel-header__title'>{title}</span>
</div>
<div className='panel__content'>
<aside className={[styles.default, styles.backgroundSubtle, styles.overflowHidden, styles.radiusSmall, styles.marginBottom15PX].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>
</div>
{subtitle && <span className={[styles.default, styles.text, styles.colorSubtle, styles.fontSize13PX, styles.marginTop5PX].join(' ')}>{subtitle}</span>}
</div>
}
<div className={[styles.default, styles.paddingHorizontal15PX, styles.paddingVertical10PX].join(' ')}>
{children}
</div>
</div>
);
};
};
</aside>
)
}
}

View File

@ -0,0 +1,20 @@
import { me, monthlyExpensesComplete } from '../../initial_state'
import PanelLayout from './panel_layout';
import ProgressBar from '../progress_bar'
export default class ProgressPanel extends PureComponent {
render() {
if (!monthlyExpensesComplete || !me) return null
return (
<PanelLayout
title="Gab's Operational Expenses"
subtitle="We are 100% funded by you"
icon="comments"
hasBackground
>
<ProgressBar progress={monthlyExpensesComplete}/>
</PanelLayout>
)
}
}

View File

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

View File

@ -1,12 +1,13 @@
import { injectIntl, defineMessages } from 'react-intl';
import { me } from '../../initial_state';
import PanelLayout from './panel_layout';
import { injectIntl, defineMessages } from 'react-intl'
import { me } from '../../initial_state'
import Button from '../button'
import PanelLayout from './panel_layout'
const messages = defineMessages({
title: { id: 'signup_panel.title', defaultMessage: 'New to Gab?' },
subtitle: { id: 'signup_panel.subtitle', defaultMessage: 'Sign up now to speak freely.' },
register: { id: 'account.register', defaultMessage: 'Sign up?' },
});
register: { id: 'account.register', defaultMessage: 'Sign up' },
})
export default @injectIntl
class SignUpPanel extends PureComponent {
@ -15,16 +16,17 @@ class SignUpPanel extends PureComponent {
}
render() {
if (me) return null;
// : TESTING :
if (!me) return null
const { intl } = this.props;
const { intl } = this.props
return (
<PanelLayout title={intl.formatMessage(messages.title)}>
<span className='panel__subtitle'>{intl.formatMessage(messages.subtitle)}</span>
<div className='panel__form'>
<a className='button' href="/auth/sign_up">{intl.formateMessage(messages.register)}</a>
</div>
<PanelLayout
title={intl.formatMessage(messages.title)}
subtitle={intl.formatMessage(messages.subtitle)}
>
<Button href="/auth/sign_up">{intl.formatMessage(messages.register)}</Button>
</PanelLayout>
)
}

View File

@ -1,4 +1,4 @@
import classNames from 'classnames';
import classNames from 'classnames';
export default class Permalink extends PureComponent {
@ -9,30 +9,26 @@ export default class Permalink extends PureComponent {
static propTypes = {
className: PropTypes.string,
href: PropTypes.string.isRequired,
to: PropTypes.string.isRequired,
children: PropTypes.node,
onInterceptClick: PropTypes.func,
blank: PropTypes.boolean,
button: PropTypes.boolean,
};
handleClick = e => {
if (this.props.onInterceptClick && this.props.onInterceptClick()) {
e.preventDefault();
return;
}
if (this.context.router && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
e.preventDefault();
this.context.router.history.push(this.props.to);
this.context.router.history.push(this.props.href);
}
}
render () {
const { href, children, className, onInterceptClick, ...other } = this.props;
const { href, children, className, blank, ...other } = this.props;
const classes = classNames('permalink', className);
const target = blank ? '_blank' : null;
return (
<a target='_blank' href={href} onClick={this.handleClick} className={classes} {...other}>
<a target={target} href={href} onClick={this.handleClick} className={classes} {...other}>
{children}
</a>
);

View File

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

View File

@ -0,0 +1,21 @@
export default class ProgressBar extends PureComponent {
static propTypes = {
progress: PropTypes.number,
}
render() {
const { progress } = this.props
const completed = Math.min(parseFloat(progress), 100)
const style = {
width: `${completed}%`,
}
return (
<a href='https://shop.dissenter.com/category/donations' className={[styles.default, styles.backgroundPanel, styles.noUnderline, styles.circle, styles.overflowHidden, styles.height22PX, styles.cursorPointer].join(' ')}>
<div className={[styles.default, styles.backgroundColorBrandDark, styles.circle, styles.height22PX].join(' ')} style={style} />
<span className={[styles.default, styles.text, styles.width100PC, styles.textAlignCenter, styles.colorWhite, styles.fontSize13PX, styles.positionAbsolute, styles.fontWeightBold, styles.displayFlex, styles.height100PC, styles.justifyContentCenter].join(' ')}>{completed}% covered this month</span>
</a>
)
}
}

View File

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

View File

@ -1,28 +0,0 @@
import { monthlyExpensesComplete } from '../../initial_state';
export default class ProgressPanel extends PureComponent {
render() {
if (!monthlyExpensesComplete) return null;
const completed = Math.min(monthlyExpensesComplete, 100);
const style = {
width: `${completed}%`,
};
return (
<div className='wtf-panel progress-panel'>
<div className='wtf-panel-header progress-panel-header'>
<div className='wtf-panel-header__label'>Gab's Operational Expenses</div>
</div>
<div className='wtf-panel__content progress-panel__content'>
<span className='progress-panel__text'>We are 100% funded by you.</span>
<div className='progress-panel__bar-container'>
<a className='progress-panel__bar' style={style} href='https://shop.dissenter.com/category/donations'>
<span className='progress-panel__bar__text'>{completed}% covered this month</span>
</a>
</div>
</div>
</div>
)
}
}

View File

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

View File

@ -1,43 +0,0 @@
import { FormattedMessage } from 'react-intl';
import Icon from '../icon';
import './promo_panel.scss';
export default class PromoPanel extends PureComponent {
render() {
return (
<div className='promo-panel'>
<div className='promo-panel__container'>
<div className='promo-panel-item'>
<a className='promo-panel-item__btn button button-alternative-2' href='https://invest.gab.com'>
<Icon id='check-circle' className='promo-panel-item__icon' fixedWidth />
<FormattedMessage id='promo.invest_heading' defaultMessage='Invest in Gab' />
</a>
<p className='promo-panel-item__message'>
<FormattedMessage
id='promo.invest_message'
defaultMessage='Learn more about investing in Gab and our vision for the future.'
/>
</p>
</div>
<div className='promo-panel-item'>
<a className='promo-panel-item__btn button button-alternative-2' href='/invites'>
<Icon id='envelope' className='promo-panel-item__icon' fixedWidth />
<FormattedMessage id='promo.invite_heading' defaultMessage='Invite Friends' />
</a>
<p className='promo-panel-item__message promo-panel-item__message--dark'>
<FormattedMessage
id='promo.invite_message'
defaultMessage='Invite others to be a member of Gab.'
/>
</p>
</div>
</div>
</div>
);
}
}

View File

@ -1,29 +0,0 @@
.promo-panel {
margin-top: 10px;
padding: 10px 10px 20px 10px;
border-bottom: 1px solid lighten($ui-base-color, 4%);
}
.promo-panel-item {
display: block;
&:not(:first-of-type) {
margin-top: 20px;
}
&__icon {
margin-right: 12px;
}
&__message {
display: block;
margin-top: 6px;
color: $primary-text-color;
@include text-sizing(14px, 400, 16px);
&--dark {
color: $ui-secondary-color;
}
}
}

View File

@ -1,7 +1,11 @@
import { Fragment } from 'react';
// import GroupSidebarPanel from '../features/groups/sidebar_panel';
import GroupSidebarPanel from '../features/groups/sidebar_panel';
import LinkFooter from '../components/link_footer';
// import PromoPanel from '../components/promo_panel';
import PromoPanel from '../components/panel/promo_panel';
import SignUpPanel from '../components/panel/sign_up_panel';
import WhoToFollowPanel from '../components/panel/who_to_follow_panel';
import TrendsPanel from '../components/panel/trends_panel';
import ProgressPanel from '../components/panel/progress_panel';
// import UserPanel from '../components/user_panel';
import PageLayout from '../components/page_layout';
// import TimelineComposeBlock from '../components/timeline_compose_block';
@ -15,15 +19,20 @@ export default class HomePage extends PureComponent {
layout={{
RIGHT: (
<Fragment>
{/*<GroupSidebarPanel />*/}
{/*<PromoPanel />*/}
{ /* <SearchInput /> */ }
{ /* <GroupSidebarPanel /> */ }
<PromoPanel />
<SignUpPanel />
{ /* <WhoToFollowPanel /> */ }
{ /* <TrendsPanel /> */ }
<ProgressPanel />
<LinkFooter />
</Fragment>
),
}}
>
{/*<TimelineComposeBlock size={46} shouldCondense={true} autoFocus={false} />*/}
{/*children*/}
{ /* children */ }
</PageLayout>
)
}

View File

@ -30,6 +30,10 @@ body {
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", sans-serif;
}
.font {
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", sans-serif;
}
.wrap {
white-space: pre-wrap;
word-wrap: break-word;
@ -77,12 +81,19 @@ body {
}
.circle { border-radius: 9999px; }
.radiusSmall { border-radius: 4px; }
.borderColorSubtle { border-color: #e4e4e4; }
.borderRight1PX { border-right-width: 1px; }
.borderBottom1PX { border-bottom-width: 1px; }
.borderLeft1PX { border-left-width: 1px; }
.marginAuto { margin: auto; }
.displayNone { display: none; }
.displayBlock { display: block; }
.displayInline { display: inline; }
.displayFlex { display: flex !important; }
.displayInlineBlock { display: inline-block; }
.cursorPointer { cursor: pointer }
@ -90,10 +101,18 @@ body {
.pointerEventsAuto > * { pointer-events: auto;}
.pointerEventsNone { pointer-events: none !important; }
.backgroundPanel { background-color: #aaa; }
.backgroundSubtle { background-color: #F5F8FA; }
.backgroundWhite { background-color: #fff; }
.backgroundColorBrandLightOpaque { background-color: rgba(54, 233, 145, 0.1); }
.backgroundColorBrandLight { background-color: #36e991; }
.backgroundColorBrand { background-color: #21cf7a; }
.backgroundColorBrandDark { background-color: #38A16B; }
.colorBlack { color: #000; }
.colorWhite { color: #fff; }
.colorSubtle { color: #666; }
.colorBrand { color: rgb(29, 161, 242); }
.colorBrand { color: #21cf7a }
.fillColorBlack { fill: #000; }
.fillColorBrand { fill: #21cf7a; }
.bottom0 { bottom: 0; }
@ -110,14 +129,16 @@ body {
.noSelect { user-select: none; }
.height100VH { height: 100vh; }
.height100PC { height: 100%; }
.height50PX { height: 50px; }
.height72PX { height: 72px; }
.height22PX { height: 22px; }
.width990PX { width: 990px; }
.width600PX { width: 600px; }
.width350PX { width: 350px; }
.width275PX { width: 275px; }
.width1015PX { width: 1015px; }
.width660PX { width: 660px; }
.width325PX { width: 325px; }
.width250PX { width: 250px; }
.width100PC { width: 100%; }
.width72PX { width: 72px; }
.maxWidth100PC { max-width: 100%; }
@ -128,10 +149,12 @@ body {
.textAlignCenter { text-align: center; }
.fontSize19PX { font-size: 19px; }
.fontSize15PX { font-size: 15px; }
.fontSize13PX { font-size: 13px; }
.fontWeightNormal { font-weight: 400; }
.fontWeightBold { font-weight: 600; }
.fontWeightExtraBold { font-weight: 800; }
.noUnderline { text-decoration: none; }
.underline { text-decoration: underline; }
@ -140,16 +163,38 @@ body {
.z2 { z-index: 2; }
.z3 { z-index: 3; }
.marginRight10PX { margin-right: 10px; }
.marginVertical5PX {
margin-top: 5px;
margin-bottom: 5px;
}
.marginVertical10PX {
margin-top: 10px;
margin-bottom: 10px;
}
.marginBottom15PX { margin-bottom: 15px; }
.marginTop10PX { margin-top: 10px; }
.marginTop5PX { margin-top: 5px; }
.paddingHorizontal15PX {
padding-left: 15px;
padding-right: 15px;
}
.paddingRight15PX { padding-right: 15px; }
.paddingVertical10PX {
padding-top: 10px;
padding-bottom: 10px;
}
.paddingVertical15PX {
padding-top: 15px;
padding-bottom: 15px;
}
.paddingHoizontal10PX {
padding-left: 10px;
padding-right: 10px;
@ -159,7 +204,3 @@ body {
padding-left: 20px;
padding-right: 20px;
}
.paddingRight15PX {
padding-right: 15px;
}

View File

@ -10,7 +10,7 @@
.landing-columns--left
.landing__brand
= link_to root_url, class: 'brand' do
= image_pack_tag 'gab_logo.svg', alt: 'Gab Social'
-# = image_pack_tag 'gab_logo.svg', alt: 'Gab Social'
%span.brand__tagline=t 'about.tagline'
.landing-columns--right

View File

@ -9,7 +9,7 @@
.header-container
.nav-left
= link_to root_url, class: 'brand' do
= image_pack_tag 'gab_logo.svg', alt: 'Gab Social'
-# = image_pack_tag 'gab_logo.svg', alt: 'Gab Social'
= link_to t('home'), root_url, class: 'nav-link optional'
= link_to t('about.about_this'), about_path, class: 'nav-link'
= link_to 'Apps', 'https://apps.gab.com', class: 'nav-link'