Commit fa66d082 authored by mgabdev's avatar mgabdev

Progress

parent df346596
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>
);
}
......
......@@ -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,
};
render () {
const { id, className, fixedWidth, ...other } = this.props;
static defaultProps = {
width: '26px',
height: '26px',
};
const classes = classNames('fa', `fa-${id}`, className, {
'fa-fw': fixedWidth,
});
render () {
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>
);
}
......
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>
)
}
......
......@@ -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>
......
.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);
}
}
\ No newline at end of file
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>
);
};
};
\ No newline at end of file
</aside>
)
}
}
\ No newline at end of file
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>
)
}
}
\ No newline at end of file
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>
)
}
}
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>
)
}
......
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>
);
......
export { default } from './progress_bar';
\ No newline at end of file
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>
)
}
}
\ No newline at end of file
export { default } from './progress_panel';
\ No newline at end of file