This commit is contained in:
mgabdev 2020-02-13 19:40:04 -05:00
parent 389b189d64
commit cdde454915
44 changed files with 783 additions and 819 deletions

@ -132,8 +132,8 @@ class Account extends ImmutablePureComponent {
</div>
<div className={[styles.default, styles.marginLeftAuto].join(' ')}>
<button className={[styles.default, styles.circle, styles.backgroundSubtle2, styles.paddingVertical5PX, styles.paddingHorizontal5PX, styles.cursorPointer].join(' ')}>
<Icon id='close' width='8px' height='8px' />
<button className={[styles.default, styles.circle, styles.backgroundTransparent, styles.paddingVertical5PX, styles.paddingHorizontal5PX, styles.cursorPointer].join(' ')}>
<Icon className={styles.fillColorSubtle} id='close' width='8px' height='8px' />
</button>
</div>

@ -1,33 +1,20 @@
import { NavLink } from 'react-router-dom'
import classNames from 'classnames/bind'
export default class Button extends PureComponent {
static propTypes = {
children: PropTypes.node,
text: PropTypes.node,
to: PropTypes.string,
href: PropTypes.string,
onClick: PropTypes.func,
disabled: PropTypes.bool,
block: PropTypes.bool,
secondary: PropTypes.bool,
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) {
@ -44,8 +31,7 @@ export default class Button extends PureComponent {
}
render () {
const { secondary, block, className, disabled, text, children, href } = this.props
const { hovering } = this.state
const { block, className, disabled, text, to, children, href } = this.props
const cx = classNames.bind(styles)
@ -60,21 +46,22 @@ export default class Button extends PureComponent {
paddingVertical10PX: 1,
paddingHorizontal15PX: 1,
width100PC: block,
backgroundColorBrand: !hovering,
backgroundColorBrandDark: hovering,
backgroundColorBrand: 1,
backgroundColorBrandDark_onHover: 1,
})
if (href) {
return (
<a
className={classes}
href={href}
onMouseEnter={() => this.handleOnMouseEnter()}
onMouseLeave={() => this.handleOnMouseLeave()}
>
<a className={classes} href={href}>
{text || children}
</a>
)
} else if (to) {
return (
<NavLink className={classes} to={to}>
{text || children}
</NavLink>
)
}
return (
@ -83,8 +70,6 @@ export default class Button extends PureComponent {
disabled={disabled}
onClick={this.handleClick}
className={classes}
onMouseEnter={() => this.handleOnMouseEnter()}
onMouseLeave={() => this.handleOnMouseLeave()}
>
{text || children}
</button>

@ -26,7 +26,7 @@ export default class Column extends PureComponent {
);
return (
<div role='region' aria-labelledby={columnHeaderId} className='column'>
<div role='region' aria-labelledby={columnHeaderId} className={[styles.default].join(' ')}>
{ backBtn && <ColumnBackButton slim={backBtn === 'slim'} />}
{children}
</div>

@ -0,0 +1,145 @@
import { injectIntl, defineMessages } from 'react-intl'
import Icon from './icon'
const messages = defineMessages({
show: { id: 'column_header.show_settings', defaultMessage: 'Show settings' },
hide: { id: 'column_header.hide_settings', defaultMessage: 'Hide settings' },
})
export default @injectIntl
class ColumnHeader extends PureComponent {
static contextTypes = {
router: PropTypes.object,
}
static propTypes = {
intl: PropTypes.object.isRequired,
title: PropTypes.node,
icon: PropTypes.string,
active: PropTypes.bool,
children: PropTypes.node,
}
state = {
collapsed: true,
}
historyBack = () => {
if (window.history && window.history.length === 1) {
this.context.router.history.push('/home') // homehack
} else {
this.context.router.history.goBack()
}
}
handleToggleClick = (e) => {
e.stopPropagation()
this.setState({
collapsed: !this.state.collapsed,
})
}
handleBackClick = () => {
this.historyBack()
}
render () {
const { title, icon, active, children, intl: { formatMessage } } = this.props
const { collapsed } = this.state
return (
<div className={[styles.default, styles.height100PC, styles.flexRow].join(' ')}>
{ /* <button className={[styles.default, styles.cursorPointer, styles.backgroundTransparent, styles.alignItemsCenter, styles.marginRight10PX, styles.justifyContentCenter].join(' ')}>
<Icon className={[styles.marginRight5PX, styles.fillColorBrand].join(' ')} id='back' width='24px' height='24px' />
</button> */ }
<h1 className={[styles.default, styles.height100PC, styles.justifyContentCenter].join(' ')}>
<span className={[styles.default, styles.text, styles.fontSize24PX, styles.fontWeight500, styles.colorBlack].join(' ')}>
{title}
</span>
</h1>
<div className={[styles.default, styles.backgroundTransparent, styles.flexRow, styles.alignItemsCenter, styles.justifyContentCenter, styles.marginLeftAuto].join(' ')}>
<button className={[styles.default, styles.marginLeft5PX, styles.cursorPointer, styles.backgroundSubtle2, styles.paddingHorizontal10PX, styles.paddingVertical5PX, styles.radiusSmall].join(' ')}>
<Icon className={styles.fillColorSubtle} id='ellipsis' width='24px' height='24px' />
</button>
<button className={[styles.default, styles.marginLeft5PX, styles.cursorPointer, styles.backgroundSubtle2, styles.paddingHorizontal10PX, styles.paddingVertical5PX, styles.radiusSmall].join(' ')}>
<Icon className={styles.fillColorSubtle} id='ellipsis' width='24px' height='24px' />
</button>
<button className={[styles.default, styles.marginLeft5PX, styles.cursorPointer, styles.backgroundSubtle2, styles.paddingHorizontal10PX, styles.paddingVertical5PX, styles.radiusSmall].join(' ')}>
<Icon className={styles.fillColorSubtle} id='ellipsis' width='24px' height='24px' />
</button>
</div>
</div>
)
// const wrapperClassName = classNames('column-header__wrapper', {
// 'column-header__wrapper--active': active,
// })
// const buttonClassName = classNames('column-header', {
// 'column-header--active': active,
// })
// const btnTitle = formatMessage(collapsed ? messages.show : messages.hide)
// const hasTitle = icon && title
// const hasChildren = !!children
// if (!hasChildren && !hasTitle) {
// return null
// } else if (!hasChildren && hasTitle) {
// return (
// <div className={wrapperClassName}>
// <h1 className={buttonClassName}>
// <Icon id={icon} fixedWidth className='column-header__icon' />
// {title}
// </h1>
// </div>
// )
// }
// const collapsibleClassName = classNames('column-header__collapsible', {
// 'column-header__collapsible--collapsed': collapsed,
// })
// const collapsibleButtonClassName = classNames('column-header__button', {
// 'column-header__button--active': !collapsed,
// })
// return (
// <div className={wrapperClassName}>
// <h1 className={buttonClassName}>
// {
// hasTitle && (
// <Fragment>
// <Icon id={icon} fixedWidth className='column-header__icon' />
// {title}
// </Fragment>
// )
// }
// <button
// className={collapsibleButtonClassName}
// title={btnTitle}
// aria-label={btnTitle}
// aria-pressed={!collapsed}
// onClick={this.handleToggleClick}
// >
// <Icon id='sliders' />
// </button>
// </h1>
// <div className={collapsibleClassName} tabIndex={collapsed ? -1 : null}>
// <div className='column-header__collapsible-inner'>
// {
// !collapsed &&
// <div key='extra-content' className='column-header__collapsible__extra'>
// {children}
// </div>
// }
// </div>
// </div>
// </div>
// )
}
}

@ -1,123 +0,0 @@
import { Fragment } from 'react';
import classNames from 'classnames';
import { injectIntl, defineMessages } from 'react-intl';
import Icon from '../icon';
const messages = defineMessages({
show: { id: 'column_header.show_settings', defaultMessage: 'Show settings' },
hide: { id: 'column_header.hide_settings', defaultMessage: 'Hide settings' },
});
export default @injectIntl
class ColumnHeader extends PureComponent {
static contextTypes = {
router: PropTypes.object,
};
static propTypes = {
intl: PropTypes.object.isRequired,
title: PropTypes.node,
icon: PropTypes.string,
active: PropTypes.bool,
children: PropTypes.node,
};
state = {
collapsed: true,
};
historyBack = () => {
if (window.history && window.history.length === 1) {
this.context.router.history.push('/home'); // homehack
} else {
this.context.router.history.goBack();
}
}
handleToggleClick = (e) => {
e.stopPropagation();
this.setState({
collapsed: !this.state.collapsed,
});
}
handleBackClick = () => {
this.historyBack();
}
render () {
const { title, icon, active, children, intl: { formatMessage } } = this.props;
const { collapsed } = this.state;
const wrapperClassName = classNames('column-header__wrapper', {
'column-header__wrapper--active': active,
});
const buttonClassName = classNames('column-header', {
'column-header--active': active,
});
const btnTitle = formatMessage(collapsed ? messages.show : messages.hide);
const hasTitle = icon && title;
const hasChildren = !!children;
if (!hasChildren && !hasTitle) {
return null;
} else if (!hasChildren && hasTitle) {
return (
<div className={wrapperClassName}>
<h1 className={buttonClassName}>
<Icon id={icon} fixedWidth className='column-header__icon' />
{title}
</h1>
</div>
);
}
const collapsibleClassName = classNames('column-header__collapsible', {
'column-header__collapsible--collapsed': collapsed,
});
const collapsibleButtonClassName = classNames('column-header__button', {
'column-header__button--active': !collapsed,
});
return (
<div className={wrapperClassName}>
<h1 className={buttonClassName}>
{
hasTitle && (
<Fragment>
<Icon id={icon} fixedWidth className='column-header__icon' />
{title}
</Fragment>
)
}
<button
className={collapsibleButtonClassName}
title={btnTitle}
aria-label={btnTitle}
aria-pressed={!collapsed}
onClick={this.handleToggleClick}
>
<Icon id='sliders' />
</button>
</h1>
<div className={collapsibleClassName} tabIndex={collapsed ? -1 : null}>
<div className='column-header__collapsible-inner'>
{
!collapsed &&
<div key='extra-content' className='column-header__collapsible__extra'>
{children}
</div>
}
</div>
</div>
</div>
);
}
}

@ -1,145 +0,0 @@
.column-header {
display: flex;
font-size: 16px;
flex: 0 0 auto;
cursor: pointer;
position: relative;
z-index: 2;
outline: 0;
overflow: hidden;
background: $gab-background-container;
body.theme-gabsocial-light & {
background: $gab-background-container-light;
color: $gab-default-text-light;
}
&--active {
box-shadow: 0 1px 0 rgba($highlight-text-color, 0.3);
.column-header__icon {
color: $highlight-text-color;
text-shadow: 0 0 10px rgba($highlight-text-color, 0.4);
}
}
&:focus,
&:active {
outline: 0;
}
&__wrapper {
position: relative;
flex: 0 0 auto;
overflow: hidden;
&--active {
&::before {
pointer-events: none;
z-index: 1;
background: radial-gradient(ellipse, rgba($ui-highlight-color, 0.23) 0%, rgba($ui-highlight-color, 0) 60%);
@include pseudo;
@include size(60%, 28px);
@include abs-position(35px, 0, auto, 0, false);
@include margin-center;
}
}
}
&__icon {
display: inline-block;
margin-right: 5px;
}
&__expansion {
overflow-x: scroll;
overflow-y: hidden;
white-space: nowrap;
max-height: 0px;
&--open {
max-height: 55px;
}
}
&__button {
margin-left: auto;
cursor: pointer;
border: 0;
padding: 0 15px;
font-size: 16px;
color: #fff;
background: $gab-background-container;
body.theme-gabsocial-light & {
color: $gab-default-text-light;
background: $gab-background-container-light;
}
&:hover {
color: lighten($darker-text-color, 7%);
}
&--active {
color: $primary-text-color;
background: lighten($ui-base-color, 8%);
&:hover {
color: $primary-text-color;
background: lighten($ui-base-color, 8%);
}
}
}
&__collapsible-inner {
background: #3f3f3f;
padding: 15px;
body.theme-gabsocial-light & {
background: #e6e6e6;
}
}
&__collapsible {
max-height: 70vh;
overflow: hidden;
overflow-y: auto;
color: $darker-text-color;
transition: max-height 150ms linear;
&--collapsed {
max-height: 0;
}
hr {
height: 0;
background: transparent;
border: 0;
border-top: 1px solid lighten($ui-base-color, 12%);
margin: 10px 0;
}
}
}
.column-header-btn {
padding: 15px;
color: inherit;
text-decoration: none;
white-space: nowrap;
&--sub {
font-size: 14px;
padding: 6px s10px;
}
&--grouped {
margin: 6px;
}
&--active {
color: $primary-text-color;
border-radius: 10px;
background-color: rgba($highlight-text-color, .1);
}
}

@ -1,168 +0,0 @@
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import classNames from 'classnames';
import { injectIntl, defineMessages } from 'react-intl';
import { Link } from 'react-router-dom';
import { createSelector } from 'reselect';
import { fetchLists } from '../../actions/lists';
import Icon from '../icon';
const messages = defineMessages({
show: { id: 'column_header.show_settings', defaultMessage: 'Show settings' },
hide: { id: 'column_header.hide_settings', defaultMessage: 'Hide settings' },
homeTitle: { id: 'home_column_header.home', defaultMessage: 'Home' },
allTitle: { id: 'home_column_header.all', defaultMessage: 'All' },
listTitle: { id: 'home_column.lists', defaultMessage: 'Lists' },
});
const getOrderedLists = createSelector([state => state.get('lists')], lists => {
if (!lists) {
return lists;
}
return lists.toList().filter(item => !!item).sort((a, b) => a.get('title').localeCompare(b.get('title')));
});
const mapStateToProps = state => {
return {
lists: getOrderedLists(state),
};
};
export default @connect(mapStateToProps)
@injectIntl
class HomeColumnHeader extends ImmutablePureComponent {
static contextTypes = {
router: PropTypes.object,
};
static propTypes = {
intl: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired,
active: PropTypes.bool,
children: PropTypes.node,
activeItem: PropTypes.string,
activeSubItem: PropTypes.string,
lists: ImmutablePropTypes.list,
};
state = {
collapsed: true,
listsExpanded: false,
};
componentDidMount() {
this.props.dispatch(fetchLists());
}
handleToggleClick = (e) => {
e.stopPropagation();
this.setState({ collapsed: !this.state.collapsed });
}
expandLists = () => {
this.setState({ listsExpanded: !this.state.listsExpanded });
}
render () {
const { active, children, intl: { formatMessage }, activeItem, activeSubItem, lists } = this.props;
const { collapsed, listsExpanded } = this.state;
const wrapperClassName = classNames('column-header__wrapper', {
'column-header__wrapper--active': active,
});
const buttonClassName = classNames('column-header', {
'column-header--active': active,
});
const collapsibleClassName = classNames('column-header__collapsible', {
'column-header__collapsible--collapsed': collapsed,
});
const collapsibleButtonClassName = classNames('column-header__button', {
'column-header__button--active': !collapsed,
});
const expansionClassName = classNames('column-header column-header__expansion', {
'column-header__expansion--open': listsExpanded,
});
const btnTitle = formatMessage(collapsed ? messages.show : messages.hide);
let expandedContent = null;
if ((listsExpanded || activeItem === 'lists') && lists) {
expandedContent = lists.map((list) => {
const listId = list.get('id');
const linkUrl = `/list/${listId}`;
const classes = classNames('column-header-btn column-header-btn--sub column-header-btn--grouped', {
'column-header-btn--active': listId === activeSubItem,
});
return (
<Link key={listId} to={linkUrl} className={classes}>
{list.get('title')}
</Link>
);
});
}
return (
<div className={wrapperClassName}>
<h1 className={buttonClassName}>
<Link to='/home' className={classNames('column-header-btn column-header-btn--grouped', { 'column-header-btn--active': 'home' === activeItem })}>
<Icon id='home' fixedWidth className='column-header__icon' />
{formatMessage(messages.homeTitle)}
</Link>
<Link to='/timeline/all' className={classNames('column-header-btn column-header-btn--grouped', { 'column-header-btn--active': 'all' === activeItem })}>
<Icon id='globe' fixedWidth className='column-header__icon' />
{formatMessage(messages.allTitle)}
</Link>
{ lists.size > 0 &&
<a onClick={this.expandLists} className={classNames('column-header-btn column-header-btn--grouped', { 'column-header-btn--active': 'lists' === activeItem })}>
<Icon id='list' fixedWidth className='column-header__icon' />
{formatMessage(messages.listTitle)}
</a>
}
{ lists.size == 0 &&
<Link to='/lists' className='column-header-btn column-header-btn--grouped'>
<Icon id='list' fixedWidth className='column-header__icon' />
{formatMessage(messages.listTitle)}
</Link>
}
<div className='column-header__buttons'>
<button
className={collapsibleButtonClassName}
title={btnTitle}
aria-label={btnTitle}
aria-pressed={collapsed ? 'false' : 'true'}
onClick={this.handleToggleClick}
>
<Icon id='sliders' />
</button>
</div>
</h1>
<h1 className={expansionClassName}>
{expandedContent}
</h1>
<div className={collapsibleClassName} tabIndex={collapsed ? -1 : null}>
<div className='column-header__collapsible-inner'>
{
!collapsed &&
<div key='extra-content' className='column-header__collapsible__extra'>
{children}
</div>
}
</div>
</div>
</div>
);
}
}

@ -1,7 +0,0 @@
import ColumnHeader from './column_header';
import HomeColumnHeader from './home_column_header';
export {
ColumnHeader,
HomeColumnHeader,
}

@ -0,0 +1,43 @@
import { defineMessages, injectIntl } from 'react-intl'
import Icon from './icon'
const messages = defineMessages({
loading: { id: 'loading_indicator.label', defaultMessage: 'Loading..' },
missing: { id: 'missing_indicator.sublabel', defaultMessage: 'This resource could not be found.' },
})
export default @injectIntl
class ColumnIndicator extends PureComponent {
static propTypes = {
intl: PropTypes.object.isRequired,
type: PropTypes.oneOf([
'loading',
'missing',
'error',
]),
message: PropTypes.oneOfType([
PropTypes.string,
PropTypes.object,
]),
}
render() {
const { type, message, intl } = this.props
const title = type !== 'error' ? intl.formatMessage(messages[type]) : message
return (
<div className={[styles.default, styles.width100PC, styles.justifyContentCenter, styles.alignItemsCenter, styles.paddingVertical15PX].join(' ')}>
<Icon id={type} width='52px' height='52px' />
{
type !== 'loading' &&
<span className={[styles.default, styles.marginTop10PX, styles.text, styles.displayFlex, styles.colorBrand, styles.fontWeightNormal, styles.fontSize14PX].join(' ')}>
{title}
</span>
}
</div>
)
}
}

@ -1,40 +0,0 @@
import { defineMessages, injectIntl } from 'react-intl';
import Column from '../column';
const messages = defineMessages({
loading: { id: 'loading_indicator.label', defaultMessage: 'Loading...' },
missing: { id: 'missing_indicator.sublabel', defaultMessage: 'This resource could not be found.' },
});
export default @injectIntl
class ColumnIndicator extends PureComponent {
static propTypes = {
intl: PropTypes.object.isRequired,
type: PropTypes.oneOf([
'loading',
'missing',
'error',
]),
message: PropTypes.oneOfType([
PropTypes.string,
PropTypes.object,
]),
};
render() {
const { type, message, intl } = this.props;
const title = type !== 'error' ? intl.formatMessage(messages[type]) : message;
return (
<Column>
<div className={`column-indicator column-indicator--${type}`}>
<div className='column-indicator__figure' />
<span className='column-indicator__title'>{title}</span>
</div>
</Column>
);
}
};

@ -1,93 +0,0 @@
.column-indicator {
overflow: visible;
transform: translate(-50%, -50%);
@include abs-position(50%, auto, auto, 50%);
&--loading & {
&__figure {
border: 6px solid lighten($ui-base-color, 26%);
}
}
&--missing & {
&__figure {
&:before {
@include pseudo('!');
@include text-sizing(40px, 600, 1, center);
@include abs-position(0, 0, 0, 0, false);
}
}
}
&__figure {
transform: translate(-50%, -50%);
box-sizing: border-box;
background-color: transparent;
@include circle(42px);
@include abs-position(50%, auto, auto, 50%);
}
span {
display: block;
float: left;
margin-left: 50%;
transform: translateX(-50%);
margin: 82px 0 0 50%;
white-space: nowrap;
color: $dark-text-color;
@include text-sizing(14px, 400);
}
}
.no-reduce-motion .column-indicator--loading span {
animation: loader-label 1.15s infinite cubic-bezier(0.215, 0.610, 0.355, 1.000);
}
.no-reduce-motion .column-indicator--loading .column-indicator__figure {
animation: loader-figure 1.15s infinite cubic-bezier(0.215, 0.610, 0.355, 1.000);
}
@keyframes loader-label {
0% {
opacity: 0.25;
}
30% {
opacity: 1;
}
100% {
opacity: 0.25;
}
}
@keyframes loader-figure {
0% {
background-color: lighten($ui-base-color, 26%);
@include size(0);
}
29% {
background-color: lighten($ui-base-color, 26%);
}
30% {
background-color: transparent;
border-width: 21px;
opacity: 1;
@include size(42px);
}
100% {
border-width: 0;
opacity: 0;
background-color: transparent;
@include size(42px);
}
}

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

@ -105,32 +105,39 @@ class Header extends ImmutablePureComponent {
const menuItems = [
{
title: 'Home',
icon: <Icon id='home' />,
icon: 'home',
to: '/',
count: 0,
},
{
title: 'Notifications',
icon: <Icon id='notifications' />,
icon: 'notifications',
to: '/notifications',
count: 40,
},
{
title: 'Groups',
icon: <Icon id='groups' />,
icon: 'group',
to: '/groups',
},
{
title: 'Lists',
icon: <Icon id='lists' />,
icon: 'lists',
to: '/lists',
},
{
title: 'Chat',
icon: <Icon id='chat' />,
icon: 'chat',
to: '/',
},
{
title: 'Profile',
icon: <Icon id='profile' />,
icon: 'profile',
to: '/',
},
{
title: 'More',
icon: 'plus',
to: '/',
},
]
@ -138,70 +145,88 @@ class Header extends ImmutablePureComponent {
const shortcutItems = [
{
title: 'Meme Group',
icon: <Icon id='group' />,
icon: 'group',
to: '/',
count: 0,
},
{
title: 'Andrew',
icon: <Icon id='user' />,
icon: 'user',
to: '/',
count: 3,
},
]
const exploreItems = [
{
title: 'Trends',
icon: <Icon id='trends' />,
icon: 'trends',
to: '/',
},
{
title: 'Dissenter',
icon: <Icon id='dissenter' />,
icon: 'dissenter',
to: '/',
},
{
title: 'Apps',
icon: <Icon id='apps' />,
icon: 'apps',
to: '/',
},
{
title: 'Shop',
icon: <Icon id='shop' />,
icon: 'shop',
to: '/',
},
]
const cx = classNames.bind(styles)
const titleClasses = cx({
default: 1,
text: 1,
colorSubtle: 1,
displayBlock: 1,
fontSize13PX: 1,
paddingVertical5PX: 1,
marginTop10PX: 1,
paddingHorizontal10PX: 1,
fontWeightBold: 1,
})
return (
<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, styles.marginVertical10PX].join(' ')}>
<div className={[styles.default, styles.height100PC, styles.width250PX, styles.paddingHorizontal15PX, 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.paddingHorizontal10PX].join(' ')}>
<GabLogo />
</NavLink>
</h1>
<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>
<span className={titleClasses}>Menu</span>
{
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>
<span className={titleClasses}>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>
<span className={titleClasses}>Explore</span>
{
exploreItems.map((exploreItem, i) => (
<HeaderMenuItem {...exploreItem} key={`header-item-explore-${i}`} />
))
}
</nav>
<Button className={[styles.paddingVertical15PX, styles.fontSize15PX, styles.fontWeightBold].join(' ')}>Gab</Button>
<Button block className={[styles.paddingVertical15PX, styles.fontSize15PX, styles.fontWeightBold].join(' ')}>
Gab
</Button>
</div>
</div>
</div>
@ -215,7 +240,7 @@ class HeaderMenuItem extends PureComponent {
static propTypes = {
to: PropTypes.string,
active: PropTypes.bool,
icon: PropTypes.node,
icon: PropTypes.string,
title: PropTypes.string,
}
@ -247,9 +272,10 @@ class HeaderMenuItem extends PureComponent {
paddingVertical5PX: 1,
paddingHorizontal10PX: 1,
alignItemsCenter: 1,
radiusSmall: 1,
// border1PX: shouldShowActive,
// borderColorSubtle: shouldShowActive,
backgroundWhite: shouldShowActive,
backgroundColorBrandLightOpaque: shouldShowActive,
})
const textClasses = cx({
@ -257,6 +283,7 @@ class HeaderMenuItem extends PureComponent {
fontWeightNormal: 1,
fontSize15PX: 1,
text: 1,
fontWeight500: shouldShowActive,
colorBrand: shouldShowActive,
colorBlack: !hovering && !active,
})
@ -275,7 +302,7 @@ class HeaderMenuItem extends PureComponent {
>
<div className={containerClasses}>
<div className={[styles.default]}>
<Icon className={iconClasses} width='16px' height='16px' />
<Icon id={icon} className={iconClasses} width='15px' height='15px' />
</div>
<div className={[styles.default, styles.paddingHorizontal10PX, styles.textOverflowEllipsis, styles.overflowWrapBreakWord, styles.displayInline].join(' ')}>
<span className={textClasses}>{title}</span>

@ -1,3 +1,5 @@
import BackIcon from './svgs/back_icon'
import CalendarIcon from './svgs/calendar_icon'
import CloseIcon from './svgs/close_icon'
import CommentIcon from './svgs/comment_icon'
import EllipsisIcon from './svgs/ellipsis_icon'
@ -5,11 +7,15 @@ import GlobeIcon from './svgs/globe_icon'
import GroupIcon from './svgs/group_icon'
import HomeIcon from './svgs/home_icon'
import LikeIcon from './svgs/like_icon'
import LoadingIcon from './svgs/loading_icon'
import MediaIcon from './svgs/media_icon'
import NotificationsIcon from './svgs/notifications_icon'
import PollIcon from './svgs/poll_icon'
import RepostIcon from './svgs/repost_icon'
import SearchIcon from './svgs/search_icon'
import ShareIcon from './svgs/share_icon'
import VerifiedIcon from './svgs/verified_icon'
import WarningIcon from './svgs/warning_icon'
export default class Icon extends PureComponent {
@ -23,9 +29,11 @@ export default class Icon extends PureComponent {
render() {
const { id, ...options } = this.props
console.log("id:", id)
switch (id) {
case 'back':
return <BackIcon {...options} />
case 'calendar':
return <CalendarIcon {...options} />
case 'close':
return <CloseIcon {...options} />
case 'comment':
@ -40,17 +48,24 @@ export default class Icon extends PureComponent {
return <HomeIcon {...options} />
case 'like':
return <LikeIcon {...options} />
case 'loading':
return <LoadingIcon {...options} />
case 'media':
return <MediaIcon {...options} />
case 'notifications':
return <NotificationsIcon {...options} />
case 'poll':
return <PollIcon {...options} />
case 'repost':
return <RepostIcon {...options} />
case 'search':
return <SearchIcon {...options} />
case 'share':
return <ShareIcon {...options} />
case 'share':
return <ShareIcon {...options} />
case 'verified':
return <VerifiedIcon {...options} />
case 'warning':
return <WarningIcon {...options} />
default:
return <NotificationsIcon {...options} />
}

@ -0,0 +1,26 @@
const BackIcon = ({
className = '',
width = '16px',
height = '16px',
viewBox = '0 0 60 60',
title = 'Back',
}) => (
<svg
className={className}
version='1.1'
xmlns='http://www.w3.org/2000/svg'
x='0px'
y='0px'
width={width}
height={height}
viewBox={viewBox}
xmlSpace='preserve'
aria-label={title}
>
<g>
<path d="M 19.570312 9.542969 C 20.417969 8.695312 21.75 8.695312 22.59375 9.542969 C 23.410156 10.359375 23.410156 11.71875 22.59375 12.535156 L 7.265625 27.867188 L 57.851562 27.867188 C 59.03125 27.867188 60 28.804688 60 29.984375 C 60 31.164062 59.03125 32.128906 57.851562 32.128906 L 7.265625 32.128906 L 22.59375 47.433594 C 23.410156 48.277344 23.410156 49.640625 22.59375 50.457031 C 21.75 51.300781 20.417969 51.300781 19.570312 50.457031 L 0.613281 31.496094 C -0.207031 30.679688 -0.207031 29.316406 0.613281 28.503906 Z M 19.570312 9.542969" />
</g>
</svg>
)
export default BackIcon

@ -0,0 +1,26 @@
const CalendarIcon = ({
className = '',
width = '26px',
height = '26px',
viewBox = '0 0 48 48',
title = 'Calendar',
}) => (
<svg
className={className}
version='1.1'
xmlns='http://www.w3.org/2000/svg'
x='0px'
y='0px'
width={width}
height={height}
viewBox={viewBox}
xmlSpace='preserve'
aria-label={title}
>
<g>
<path d="M 45.234375 7.871094 C 44.558594 7.191406 43.753906 6.851562 42.828125 6.851562 L 39.402344 6.851562 L 39.402344 4.28125 C 39.402344 3.105469 38.980469 2.097656 38.144531 1.257812 C 37.304688 0.417969 36.296875 0 35.117188 0 L 33.40625 0 C 32.226562 0 31.21875 0.417969 30.378906 1.257812 C 29.542969 2.097656 29.121094 3.105469 29.121094 4.28125 L 29.121094 6.851562 L 18.84375 6.851562 L 18.84375 4.28125 C 18.84375 3.105469 18.425781 2.097656 17.585938 1.257812 C 16.746094 0.417969 15.738281 0 14.5625 0 L 12.847656 0 C 11.671875 0 10.664062 0.417969 9.824219 1.257812 C 8.984375 2.097656 8.566406 3.105469 8.566406 4.28125 L 8.566406 6.851562 L 5.140625 6.851562 C 4.210938 6.851562 3.410156 7.191406 2.730469 7.871094 C 2.050781 8.546875 1.714844 9.351562 1.714844 10.277344 L 1.714844 44.539062 C 1.714844 45.46875 2.050781 46.269531 2.730469 46.949219 C 3.410156 47.625 4.210938 47.964844 5.140625 47.964844 L 42.824219 47.964844 C 43.753906 47.964844 44.558594 47.625 45.234375 46.949219 C 45.914062 46.269531 46.253906 45.46875 46.253906 44.539062 L 46.253906 10.277344 C 46.253906 9.351562 45.914062 8.546875 45.234375 7.871094 Z M 12.847656 44.539062 L 5.140625 44.539062 L 5.140625 36.832031 L 12.847656 36.832031 Z M 12.847656 35.117188 L 5.140625 35.117188 L 5.140625 26.554688 L 12.847656 26.554688 Z M 12.847656 24.839844 L 5.140625 24.839844 L 5.140625 17.128906 L 12.847656 17.128906 Z M 12.246094 12.59375 C 12.078125 12.425781 11.992188 12.222656 11.992188 11.992188 L 11.992188 4.28125 C 11.992188 4.050781 12.078125 3.851562 12.246094 3.679688 C 12.414062 3.511719 12.617188 3.425781 12.847656 3.425781 L 14.5625 3.425781 C 14.792969 3.425781 14.996094 3.511719 15.164062 3.679688 C 15.332031 3.851562 15.417969 4.050781 15.417969 4.28125 L 15.417969 11.992188 C 15.417969 12.222656 15.332031 12.425781 15.164062 12.59375 C 14.992188 12.761719 14.792969 12.847656 14.5625 12.847656 L 12.847656 12.847656 C 12.617188 12.847656 12.414062 12.761719 12.246094 12.59375 Z M 23.125 44.539062 L 14.5625 44.539062 L 14.5625 36.832031 L 23.125 36.832031 Z M 23.125 35.117188 L 14.5625 35.117188 L 14.5625 26.554688 L 23.125 26.554688 Z M 23.125 24.839844 L 14.5625 24.839844 L 14.5625 17.128906 L 23.125 17.128906 Z M 33.40625 44.539062 L 24.839844 44.539062 L 24.839844 36.832031 L 33.40625 36.832031 Z M 33.40625 35.117188 L 24.839844 35.117188 L 24.839844 26.554688 L 33.40625 26.554688 Z M 33.40625 24.839844 L 24.839844 24.839844 L 24.839844 17.128906 L 33.40625 17.128906 Z M 32.804688 12.59375 C 32.632812 12.425781 32.550781 12.222656 32.550781 11.992188 L 32.550781 4.28125 C 32.550781 4.050781 32.632812 3.851562 32.804688 3.679688 C 32.972656 3.511719 33.171875 3.425781 33.40625 3.425781 L 35.117188 3.425781 C 35.351562 3.425781 35.550781 3.511719 35.71875 3.679688 C 35.890625 3.851562 35.972656 4.050781 35.972656 4.28125 L 35.972656 11.992188 C 35.972656 12.222656 35.890625 12.425781 35.71875 12.59375 C 35.550781 12.761719 35.351562 12.847656 35.117188 12.847656 L 33.40625 12.847656 C 33.171875 12.847656 32.972656 12.761719 32.804688 12.59375 Z M 42.824219 44.539062 L 35.117188 44.539062 L 35.117188 36.832031 L 42.824219 36.832031 Z M 42.824219 35.117188 L 35.117188 35.117188 L 35.117188 26.554688 L 42.824219 26.554688 Z M 42.824219 24.839844 L 35.117188 24.839844 L 35.117188 17.128906 L 42.824219 17.128906 Z M 42.824219 24.839844" />
</g>
</svg>
)
export default CalendarIcon

@ -18,7 +18,7 @@ const GlobeIcon = ({
aria-label={title}
>
<g>
<circle fill='none' stroke='#616770' stroke-width='1px' cx='14' cy='14' r='13.5' />
<circle fill='none' stroke='#616770' strokeWidth='1px' cx='14' cy='14' r='13.5' />
<path fill='#616770' d='M 16 5 L 18 4 L 20 5 L 22 5 L 23 4 C 24 5 25 6 25 7 L 25 7 L 22 8 L 20 7 L 19 6 L 16 6 L 14 7 L 15 11 L 18 12 L 20 12 L 21 13 L 21 14 L 22 16 L 23 18 L 23 20 L 26.4 21 C 20 27 12 29 6 25 C 1 21 0 14 1 8 L 2 11 L 3 12 L 5 13 L 4 14 L 4 15 L 5 17 L 7 17 L 7 22 L 8 24 L 9 25 L 9 22 L 11 21 L 11 20 L 13 18 L 14 15 L 12 15 L 10 13 L 7 13 L 6 11 L 5 13 L 5 11 L 4 10 L 4 8 L 7 8 L 9 7 L 11 4 L 12 4 L 13 2 L 10 2 L 10 1 C 12 0 16 0 18 1 L 18 2 L 16 2 L 15 4 Z M 16 5' />
{/*<path fill='#616770' d='M 14 27 C 21 28 28 21 28 14 C 28 6 21 0 14 0 C 8 0 3 3 1 8 L 0 8 L 1 8 C -1 15 1 24 8 26 C 10 27 12 28 14 27 Z M 24 21 L 24 20 L 24 18 C 24 18 24 18 24 18 L 22 14 L 22 13 C 22 12 22 12 22 12 L 20 11 C 20 11 20 11 20 11 L 18 12 L 15 10 L 15 8 L 16 7 L 18 7 L 19 8 C 19 8 19 8 19 8 L 22 8 C 22 8 22 8 23 8 L 25 7 C 27 12 27 17 24 21 Z M 23 4 L 22 5 L 20 4 L 18 4 C 18 4 18 4 18 4 L 16 4 L 16 4 L 16 3 L 18 3 C 18 3 18 3 18 3 L 20 2 C 21 2 22 3 23 4 Z M 8 1 L 10 2 C 10 2 10 2 10 2 L 12 3 L 12 3 L 11 4 C 10 4 10 4 10 4 L 9 6 L 7 7 L 4 8 C 3 8 3 8 3 8 L 3 10 C 3 10 3 10 3 10 L 4 11 L 4 12 L 2 10 L 2 8 C 3 5 5 3 8 1 Z M 7 17 L 5 16 L 4 15 L 4 14 L 6 12 L 7 13 C 7 13 7 14 7 14 L 10 14 L 11 16 C 11 16 11 16 12 16 L 13 16 L 13 17 L 11 19 C 11 19 11 19 11 20 L 11 21 L 9 22 C 9 22 9 22 9 22 L 9 24 L 8 24 L 7 22 L 7 17 C 7 17 7 17 7 17 Z M 1 10 L 1 11 C 1 11 1 11 2 11 L 4 13 L 3 13 C 3 13 3 13 3 14 L 3 15 C 3 15 3 15 3 15 L 4 17 C 4 17 4 17 5 17 L 7 18 L 7 22 C 7 22 7 22 7 22 L 7 24 C 8 25 8 25 8 25 L 9 25 C 9 25 9 25 9 25 C 10 25 10 25 10 25 L 10 23 L 11 21 C 12 21 12 21 12 21 L 12 20 L 13 18 C 13 18 13 18 13 18 L 14 15 C 14 15 14 15 14 15 C 14 15 14 15 14 15 L 12 15 L 11 13 C 11 13 10 13 10 13 L 7 13 L 6 11 C 6 11 6 11 6 11 C 6 11 6 11 6 11 L 5 11 L 5 11 C 5 11 5 10 5 10 L 4 10 L 4 9 L 7 8 C 7 8 7 8 7 8 L 10 7 C 10 7 10 7 10 7 L 11 5 L 12 4 C 12 4 12 4 13 4 L 13 2 C 13 2 13 2 13 2 C 13 2 13 2 13 2 L 10 1 L 9 1 C 12 0 16 0 18 1 L 18 2 L 16 2 C 16 2 15 2 15 2 L 14 4 C 14 4 14 4 15 5 C 15 5 15 5 15 5 L 16 5 C 16 5 16 5 16 5 L 18 5 L 19 5 C 19 5 19 5 19 5 L 22 6 C 22 6 22 6 22 5 L 23 5 C 24 5 24 6 25 7 L 22 7 L 20 7 L 19 6 C 19 6 19 6 19 6 L 16 6 C 16 6 16 6 16 6 L 14 7 C 14 7 14 7 14 8 L 14 11 C 14 11 15 11 15 11 L 18 13 C 18 13 18 13 18 13 L 20 12 L 21 13 L 21 14 C 21 15 21 15 21 15 L 23 18 L 23 20 C 23 20 23 20 23 20 L 24 21 C 20 27 11 28 6 24 C 1 21 0 15 1 10 Z M 1 10' /> */}
</g>

@ -0,0 +1,83 @@
const LoadingIcon = ({
className = '',
width = '24px',
height = '24px',
viewBox = '0 0 100 100',
}) => (
<svg
className={className}
xmlns='http://www.w3.org/2000/svg'
style={{shapeRendering: 'auto'}}
width={width}
height={height}
viewBox={viewBox}
preserveAspectRatio='xMidYMid'
>
<g transform='translate(82,50)'>
<g transform='rotate(0)'>
<circle cx='0' cy='0' r='6' fill='#30ce7d' fill-opacity='1' transform='scale(1.03405 1.03405)'>
<animateTransform attributeName='transform' type='scale' begin='-1.0294117647058822s' values='1.5 1.5;1 1' keyTimes='0;1' dur='1.176470588235294s' repeatCount='indefinite'></animateTransform>
<animate attributeName='fill-opacity' keyTimes='0;1' dur='1.176470588235294s' repeatCount='indefinite' values='1;0' begin='-1.0294117647058822s'></animate>
</circle>
</g>
</g>
<g transform='translate(72.62741699796952,72.62741699796952)'>
<g transform='rotate(45)'>
<circle cx='0' cy='0' r='6' fill='#30ce7d' fill-opacity='0.875' transform='scale(1.09655 1.09655)'>
<animateTransform attributeName='transform' type='scale' begin='-0.8823529411764705s' values='1.5 1.5;1 1' keyTimes='0;1' dur='1.176470588235294s' repeatCount='indefinite'></animateTransform>
<animate attributeName='fill-opacity' keyTimes='0;1' dur='1.176470588235294s' repeatCount='indefinite' values='1;0' begin='-0.8823529411764705s'></animate>
</circle>
</g>
</g>
<g transform='translate(50,82)'>
<g transform='rotate(90)'>
<circle cx='0' cy='0' r='6' fill='#30ce7d' fill-opacity='0.75' transform='scale(1.15905 1.15905)'>
<animateTransform attributeName='transform' type='scale' begin='-0.7352941176470588s' values='1.5 1.5;1 1' keyTimes='0;1' dur='1.176470588235294s' repeatCount='indefinite'></animateTransform>
<animate attributeName='fill-opacity' keyTimes='0;1' dur='1.176470588235294s' repeatCount='indefinite' values='1;0' begin='-0.7352941176470588s'></animate>
</circle>
</g>
</g>
<g transform='translate(27.37258300203048,72.62741699796952)'>
<g transform='rotate(135)'>
<circle cx='0' cy='0' r='6' fill='#30ce7d' fill-opacity='0.625' transform='scale(1.22155 1.22155)'>
<animateTransform attributeName='transform' type='scale' begin='-0.588235294117647s' values='1.5 1.5;1 1' keyTimes='0;1' dur='1.176470588235294s' repeatCount='indefinite'></animateTransform>
<animate attributeName='fill-opacity' keyTimes='0;1' dur='1.176470588235294s' repeatCount='indefinite' values='1;0' begin='-0.588235294117647s'></animate>
</circle>
</g>
</g>
<g transform='translate(18,50)'>
<g transform='rotate(180)'>
<circle cx='0' cy='0' r='6' fill='#30ce7d' fill-opacity='0.5' transform='scale(1.28405 1.28405)'>
<animateTransform attributeName='transform' type='scale' begin='-0.4411764705882352s' values='1.5 1.5;1 1' keyTimes='0;1' dur='1.176470588235294s' repeatCount='indefinite'></animateTransform>
<animate attributeName='fill-opacity' keyTimes='0;1' dur='1.176470588235294s' repeatCount='indefinite' values='1;0' begin='-0.4411764705882352s'></animate>
</circle>
</g>
</g>
<g transform='translate(27.372583002030474,27.37258300203048)'>
<g transform='rotate(225)'>
<circle cx='0' cy='0' r='6' fill='#30ce7d' fill-opacity='0.375' transform='scale(1.34655 1.34655)'>
<animateTransform attributeName='transform' type='scale' begin='-0.2941176470588235s' values='1.5 1.5;1 1' keyTimes='0;1' dur='1.176470588235294s' repeatCount='indefinite'></animateTransform>
<animate attributeName='fill-opacity' keyTimes='0;1' dur='1.176470588235294s' repeatCount='indefinite' values='1;0' begin='-0.2941176470588235s'></animate>
</circle>
</g>
</g>
<g transform='translate(50,18)'>
<g transform='rotate(270)'>
<circle cx='0' cy='0' r='6' fill='#30ce7d' fill-opacity='0.25' transform='scale(1.40905 1.40905)'>
<animateTransform attributeName='transform' type='scale' begin='-0.14705882352941174s' values='1.5 1.5;1 1' keyTimes='0;1' dur='1.176470588235294s' repeatCount='indefinite'></animateTransform>
<animate attributeName='fill-opacity' keyTimes='0;1' dur='1.176470588235294s' repeatCount='indefinite' values='1;0' begin='-0.14705882352941174s'></animate>
</circle>
</g>
</g>
<g transform='translate(72.62741699796952,27.372583002030474)'>
<g transform='rotate(315)'>
<circle cx='0' cy='0' r='6' fill='#30ce7d' fill-opacity='0.125' transform='scale(1.47155 1.47155)'>
<animateTransform attributeName='transform' type='scale' begin='0s' values='1.5 1.5;1 1' keyTimes='0;1' dur='1.176470588235294s' repeatCount='indefinite'></animateTransform>
<animate attributeName='fill-opacity' keyTimes='0;1' dur='1.176470588235294s' repeatCount='indefinite' values='1;0' begin='0s'></animate>
</circle>
</g>
</g>
</svg>
)
export default LoadingIcon

@ -0,0 +1,29 @@
const MediaIcon = ({
className = '',
width = '16px',
height = '16px',
viewBox = '0 0 48 48',
title = 'Media',
}) => (
<svg
className={className}
version='1.1'
xmlns='http://www.w3.org/2000/svg'
x='0px'
y='0px'
width={width}
height={height}
viewBox={viewBox}
xmlSpace='preserve'
aria-label={title}
>
<g>
<path d="M 24.867188 37.953125 C 23.472656 37.933594 22.125 37.429688 21.058594 36.535156 L 14.019531 30.332031 C 12.910156 29.296875 11.214844 29.214844 10.015625 30.140625 L 0.00390625 37.503906 L 0.00390625 42.28125 C -0.0664062 43.5625 0.914062 44.660156 2.195312 44.734375 C 2.261719 44.738281 2.328125 44.738281 2.394531 44.734375 L 37.847656 44.734375 C 39.269531 44.734375 40.691406 43.703125 40.691406 42.28125 L 40.691406 29.753906 L 27.96875 37.113281 C 27.03125 37.675781 25.957031 37.96875 24.867188 37.953125 Z M 24.867188 37.953125" />
<path d="M 27.191406 22.582031 C 27.191406 24.152344 25.921875 25.425781 24.351562 25.425781 C 22.78125 25.425781 21.507812 24.152344 21.507812 22.582031 C 21.507812 21.015625 22.78125 19.742188 24.351562 19.742188 C 25.921875 19.742188 27.191406 21.015625 27.191406 22.582031 Z M 27.191406 22.582031" />
<path d="M 47.40625 8.3125 C 46.996094 7.75 46.359375 7.394531 45.664062 7.34375 L 10.464844 3.273438 C 9.78125 3.214844 9.09375 3.398438 8.527344 3.789062 C 8.027344 4.226562 7.6875 4.816406 7.558594 5.46875 L 6.980469 10.507812 L 37.847656 10.507812 C 40.792969 10.574219 43.171875 12.925781 43.273438 15.867188 L 43.273438 38.664062 C 43.273438 38.535156 43.855469 38.40625 44.113281 38.148438 C 44.648438 37.726562 44.957031 37.085938 44.953125 36.40625 L 47.988281 10.183594 C 48.054688 9.507812 47.84375 8.832031 47.40625 8.3125 Z M 47.40625 8.3125" />
<path d="M 37.847656 13.089844 L 2.394531 13.089844 C 0.972656 13.089844 0.00390625 14.445312 0.00390625 15.867188 L 0.00390625 34.273438 L 8.527344 28.074219 C 10.691406 26.46875 13.6875 26.601562 15.695312 28.394531 L 22.800781 34.597656 C 23.871094 35.503906 25.402344 35.636719 26.609375 34.917969 L 40.691406 26.71875 L 40.691406 15.867188 C 40.59375 14.351562 39.367188 13.152344 37.847656 13.089844 Z M 24.351562 28.007812 C 21.355469 28.007812 18.925781 25.578125 18.925781 22.582031 C 18.925781 19.585938 21.355469 17.160156 24.351562 17.160156 C 27.347656 17.160156 29.777344 19.585938 29.777344 22.582031 C 29.777344 25.578125 27.347656 28.007812 24.351562 28.007812 Z M 24.351562 28.007812" />
</g>
</svg>
)
export default MediaIcon

@ -0,0 +1,26 @@
const PollIcon = ({
className = '',
width = '16px',
height = '16px',
viewBox = '0 0 48 48',
title = 'Poll',
}) => (
<svg
className={className}
version='1.1'
xmlns='http://www.w3.org/2000/svg'
x='0px'
y='0px'
width={width}
height={height}
viewBox={viewBox}
xmlSpace='preserve'
aria-label={title}
>
<g>
<path d="M 42.667969 0 L 5.332031 0 C 2.386719 0 0 2.386719 0 5.332031 L 0 42.667969 C 0 45.613281 2.386719 48 5.332031 48 L 42.667969 48 C 45.613281 48 48 45.613281 48 42.667969 L 48 5.332031 C 48 2.386719 45.613281 0 42.667969 0 Z M 16 37.332031 L 10.667969 37.332031 L 10.667969 18.667969 L 16 18.667969 Z M 26.667969 37.332031 L 21.332031 37.332031 L 21.332031 10.667969 L 26.667969 10.667969 Z M 37.332031 37.332031 L 32 37.332031 L 32 26.667969 L 37.332031 26.667969 Z M 37.332031 37.332031" />
</g>
</svg>
)
export default PollIcon

@ -21,8 +21,8 @@ const ShareIcon = ({
<circle cx="18" cy="5" r="3"/>
<circle cx="6" cy="12" r="3"/>
<circle cx="18" cy="19" r="3"/>
<line stroke='#666' stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="8.59" x2="15.42" y1="13.51" y2="17.49" />
<line stroke='#666' stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="15.41" x2="8.59" y1="6.51" y2="10.49" />
<line stroke='#666' strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" x1="8.59" x2="15.42" y1="13.51" y2="17.49" />
<line stroke='#666' strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" x1="15.41" x2="8.59" y1="6.51" y2="10.49" />
</g>
</svg>
)

@ -0,0 +1,26 @@
const WarningIcon = ({
className = '',
width = '16px',
height = '16px',
viewBox = '0 0 48 48',
title = 'Warning',
}) => (
<svg
className={className}
version='1.1'
xmlns='http://www.w3.org/2000/svg'
x='0px'
y='0px'
width={width}
height={height}
viewBox={viewBox}
xmlSpace='preserve'
aria-label={title}
>
<g>
<path d="M 47.003906 35.996094 L 30.046875 4.824219 C 27.324219 0.238281 20.679688 0.234375 17.953125 4.824219 L 1 35.996094 C -1.785156 40.683594 1.585938 46.617188 7.042969 46.617188 L 40.957031 46.617188 C 46.410156 46.617188 49.789062 40.6875 47.003906 35.996094 Z M 24 40.992188 C 22.449219 40.992188 21.1875 39.730469 21.1875 38.179688 C 21.1875 36.628906 22.449219 35.367188 24 35.367188 C 25.550781 35.367188 26.8125 36.628906 26.8125 38.179688 C 26.8125 39.730469 25.550781 40.992188 24 40.992188 Z M 26.8125 29.742188 C 26.8125 31.292969 25.550781 32.554688 24 32.554688 C 22.449219 32.554688 21.1875 31.292969 21.1875 29.742188 L 21.1875 15.679688 C 21.1875 14.128906 22.449219 12.867188 24 12.867188 C 25.550781 12.867188 26.8125 14.128906 26.8125 15.679688 Z M 26.8125 29.742188" />
</g>
</svg>
)
export default WarningIcon

@ -164,7 +164,7 @@ class Item extends ImmutablePureComponent {
thumbnail = (
<a
className='media-item__thumbnail'
className={[styles.default, styles.overflowHidden].join(' ')}
href={attachment.get('remote_url') || originalUrl}
onClick={this.handleClick}
target='_blank'
@ -184,9 +184,9 @@ class Item extends ImmutablePureComponent {
const autoPlay = !isIOS() && autoPlayGif !== false;
thumbnail = (
<div className={classNames('media-gallery__gifv', { autoplay: autoPlay })}>
<div className={[styles.default, styles.overflowHidden, styles.heigh100PC, styles.width100PC].join(' ')}>
<video
className='media-item__gifv'
className={[styles.default, styles.cursorPointer, styles.objectFitCover, styles.width100PC, styles.height100PC, styles.z1].join(' ')}
aria-label={attachment.get('description')}
title={attachment.get('description')}
role='application'
@ -208,8 +208,8 @@ class Item extends ImmutablePureComponent {
}
return (
<div className={classNames('media-gallery__item', { standalone })} key={attachment.get('id')} style={{ position, float, left, top, right, bottom, height, width: `${width}%` }}>
<canvas width={32} height={32} ref={this.setCanvasRef} className={classNames('media-gallery__preview', { 'media-gallery__preview--hidden': visible && this.state.loaded })} />
<div className={[styles.defeault, styles.positionAbsolute].join(' ')} key={attachment.get('id')} style={{ position, float, left, top, right, bottom, height, width: `${width}%` }}>
<canvas width={0} height={0} ref={this.setCanvasRef} className={styles.displayNone} />
{visible && thumbnail}
</div>
);
@ -512,10 +512,15 @@ class MediaGallery extends PureComponent {
}
return (
<div className='media-gallery' style={style} ref={this.handleRef}>
<div
className={[styles.default, styles.overflowHidden, styles.borderColorSubtle, styles.borderTop1PX, styles.borderBottom1PX].join(' ')}
style={style}
ref={this.handleRef}
>
{ /*
<div className={classNames('spoiler-button', { 'spoiler-button--minified': visible })}>
{spoilerButton}
</div>
</div> */ }
{children}
</div>

@ -1,43 +1,55 @@
import ColumnHeader from './column_header'
import Header from './header'
export default class PageLayout extends PureComponent {
static propTypes = {
layout: PropTypes.object,
title: PropTypes.string,
showBackBtn: PropTypes.boolean,
}
render() {
const { children, layout } = this.props
const { children, title, showBackBtn, layout } = this.props
const right = layout.RIGHT || null
const headerRight = layout.HEADER_RIGHT || null
return (
<div className={[styles.default, styles.flexRow, styles.width100PC, styles.backgroundColorSubtle3].join(' ')}>
<Header />
<main role='main' className={[styles.default, styles.flexShrink1, styles.flexGrow1, styles.borderColorSubtle2, styles.borderLeft1PX].join(' ')}>
<div className={[styles.default, styles.height53PX, styles.borderBottom1PX, styles.borderColorSubtle2, styles.backgroundColorSubtle3].join(' ')}>
<div className={[styles.default, styles.height53PX, styles.borderBottom1PX, styles.borderColorSubtle2, styles.backgroundColorSubtle3, styles.z3, styles.top0, styles.positionFixed].join(' ')}>
<div className={[styles.default, styles.height53PX, styles.paddingLeft15PX, styles.width1015PX, styles.flexRow, styles.justifyContentSpaceBetween].join(' ')}>
<div className={[styles.default, styles.width660PX].join(' ')}>
test
<ColumnHeader title={title} />
</div>
<div className={[styles.default, styles.width325PX].join(' ')}>
{ headerRight }
</div>
</div>
</div>
<div className={[styles.default, styles.height53PX].join(' ')}></div>
<div className={[styles.default, styles.width1015PX, styles.flexRow, styles.justifyContentSpaceBetween, styles.paddingLeft15PX, styles.paddingVertical15PX].join(' ')}>
<div className={[styles.default, styles.width660PX, styles.z1].join(' ')}>
<div className={styles.default}>
{children}
</div>
</div>
<div className={[styles.default, styles.width325PX].join(' ')}>
<div className={styles.default}>
{right}
</div>
</div>
</div>
</main>
</div>
)
}

@ -1,4 +1,4 @@
import classNames from 'classnames';
import classNames from 'classnames/bind';
import Overlay from 'react-overlays/lib/Overlay';
import Icon from '../icon';
import SearchPopout from '../search_popout';
@ -41,7 +41,19 @@ export default class Search extends PureComponent {
const { expanded } = this.state;
const hasValue = value ? value.length > 0 || submitted : 0;
const iconClass = hasValue ? 'active' : '';
const cx = classNames.bind(styles)
const btnClasses = cx({
default: 1,
cursorPointer: 1,
marginRight5PX: 1,
paddingHorizontal10PX: 1,
paddingVertical10PX: 1,
circle: 1,
backgroundColorBrandLight: 1,
displayNone: !hasValue,
})
return (
<div className={[styles.default, styles.justifyContentCenter, styles.height53PX].join(' ')}>
@ -57,7 +69,7 @@ export default class Search extends PureComponent {
onFocus={this.handleFocus}
onBlur={this.handleBlur}
/>
<div role='button' tabIndex='0' className={[styles.default, styles.cursorPointer, styles.marginRight5PX, styles.paddingHorizontal10PX, styles.paddingVertical10PX, styles.circle, styles.backgroundColorBrandLight].join(' ')} onClick={handleClear}>
<div role='button' tabIndex='0' className={btnClasses} onClick={handleClear}>
<Icon id='close' width='10px' height='10px' className={styles.fillColorWhite} aria-label={placeholder} />
</div>
</div>

@ -265,6 +265,8 @@ class Status extends ImmutablePureComponent {
const { intl, hidden, featured, otherAccounts, unread, showThread, group, promoted } = this.props;
// console.log("replies:", this.props.replies)
let { status, account, ...other } = this.props;
if (status === null) return null;
@ -391,6 +393,7 @@ class Status extends ImmutablePureComponent {
);
}
} else if (status.get('spoiler_text').length === 0 && status.get('card')) {
console.log("card:", status.get('card'))
media = (
<Card
onOpenMedia={this.props.onOpenMedia}
@ -424,8 +427,6 @@ class Status extends ImmutablePureComponent {
const statusUrl = `/${status.getIn(['account', 'acct'])}/posts/${status.get('id')}`;
console.log("const replies = state.getIn(['contexts', 'replies', id]);", state.getIn(['contexts', 'replies', id]))
return (
<HotKeys handlers={handlers}>
<div
@ -472,7 +473,7 @@ class Status extends ImmutablePureComponent {
<Icon id='globe' width='12px' height='12px' className={[styles.default, styles.displayInline, styles.marginLeft5PX, styles.fillColorSubtle].join(' ')}/>
{
status.get('group') &&
!!status.get('group') &&
<Fragment>
<span className={[styles.default, styles.text, styles.fontSize12PX, styles.marginLeft5PX, styles.colorSubtle].join(' ')}></span>
<NavLink
@ -502,7 +503,7 @@ class Status extends ImmutablePureComponent {
</div>
</div>
<div className={[styles.default, styles.paddingHorizontal15PX, styles.marginBottom15PX].join(' ')}>
<div className={styles.default}>
<StatusContent
status={status}
reblogContent={reblogContent}
@ -513,7 +514,7 @@ class Status extends ImmutablePureComponent {
/>
</div>
{ /* media */ }
{ media }
{ /* status.get('quote') && <StatusQuote
id={status.get('quote')}

@ -57,21 +57,8 @@ class StatusActionBarItem extends PureComponent {
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)
@ -90,11 +77,9 @@ class StatusActionBarItem extends PureComponent {
width100PC: 1,
radiusSmall: 1,
outlineFocusBrand: 1,
backgroundTransparent: !hovering,
backgroundSubtle: hovering,
backgroundTransparent: 1,
backgroundSubtle_onHover: 1,
colorSubtle: 1,
// colorSubtle: !hovering,
// colorBrand: hovering,
})
return (
@ -104,9 +89,7 @@ class StatusActionBarItem extends PureComponent {
onClick={onClick}
active={active}
disabled={disabled}
onMouseEnter={() => this.handleOnMouseEnter()}
onMouseLeave={() => this.handleOnMouseLeave()}
>
>
<Icon width='16px' height='16px' id={icon} className={[styles.default, styles.marginRight10PX, styles.fillColorSubtle].join(' ')} />
{title}
</button>
@ -358,25 +341,83 @@ class StatusActionBar extends ImmutablePureComponent {
},
]
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 key={`status-action-bar-item-${i}`} {...item} />
))
}
const hasInteractions = favoriteCount > 0 || replyCount > 0 || reblogCount > 0
const shouldCondense = (!!status.get('card') || status.get('media_attachments').size > 0) && !hasInteractions
{/*<div className='status-action-bar__dropdown'>
<DropdownMenuContainer
status={status}
items={menu}
icon='ellipsis-h'
size={18}
direction='right'
title={formatMessage(messages.more)}
/>
</div>*/}
const cx = classNames.bind(styles)
const containerClasses = cx({
default: 1,
paddingHorizontal10PX: 1,
marginTop10PX: !shouldCondense,
marginTop5PX: shouldCondense,
})
const innerContainerClasses = cx({
default: 1,
paddingVertical2PX: 1,
flexRow: 1,
width100PC: 1,
borderTop1PX: !shouldCondense,
borderColorSubtle: !shouldCondense,
marginTop5PX: hasInteractions,
})
const interactionBtnClasses = cx({
default: 1,
text: 1,
colorSubtle: 1,
cursorPointer: 1,
fontSize15PX: 1,
fontWeightNormal: 1,
marginRight10PX: 1,
paddingVertical5PX: 1,
})
return (
<div className={containerClasses}>
{
hasInteractions &&
<div className={[styles.default, styles.flexRow, styles.paddingHorizontal5PX].join(' ')}>
{ favoriteCount > 0 &&
<button className={interactionBtnClasses}>
{favoriteCount}
&nbsp;Likes
</button>
}
{ replyCount > 0 &&
<button className={interactionBtnClasses}>
{replyCount}
&nbsp;Comments
</button>
}
{ reblogCount > 0 &&
<button className={interactionBtnClasses}>
{reblogCount}
&nbsp;Reposts
</button>
}
</div>
}
<div className={innerContainerClasses}>
<div className={[styles.default, styles.flexRow, styles.paddingVertical2PX, styles.width100PC].join(' ')}>
{
items.map((item, i) => (
<StatusActionBarItem key={`status-action-bar-item-${i}`} {...item} />
))
}
{/*<div className='status-action-bar__dropdown'>
<DropdownMenuContainer
status={status}
items={menu}
icon='ellipsis-h'
size={18}
direction='right'
title={formatMessage(messages.more)}
/>
</div>*/}
</div>
</div>
<div className='status-action-bar__comment'>
{/*<ComposeFormContainer shouldCondense statusId={status.get('id')} />*/}

@ -2,7 +2,7 @@ import { Fragment } from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { FormattedMessage, injectIntl, defineMessages } from 'react-intl';
import classnames from 'classnames';
import classNames from 'classnames/bind'
import { isRtl } from '../../utils/rtl';
import Permalink from '../permalink/permalink';
import Icon from '../icon';
@ -12,6 +12,7 @@ const MAX_HEIGHT = 200;
const messages = defineMessages({
showMore: { id: 'status.show_more', defaultMessage: 'Show more' },
showLess: { id: 'status.show_less', defaultMessage: 'Show less' },
readMore: { id: 'status.read_more', defaultMessage: 'Read more' },
})
export default
@ -161,16 +162,20 @@ class StatusContent extends ImmutablePureComponent {
const content = { __html: this.getHtmlContent() };
const spoilerContent = { __html: status.get('spoilerHtml') };
const directionStyle = { direction: 'ltr' };
const classNames = classnames('status__content', {
'status__content--with-action': this.props.onClick && this.context.router,
'status__content--with-spoiler': status.get('spoiler_text').length > 0,
'status__content--collapsed': this.state.collapsed === true,
});
// const classNames = '';
// classnames('status__content', {
// 'status__content--with-action': this.props.onClick && this.context.router,
// 'status__content--with-spoiler': status.get('spoiler_text').length > 0,
// 'status__content--collapsed': this.state.collapsed === true,
// // styles.paddingHorizontal15PX, styles.marginBottom15PX
// });
if (isRtl(status.get('search_index'))) {
directionStyle.direction = 'rtl';
}
const cx = classNames.bind(styles)
if (status.get('spoiler_text').length > 0) {
let mentionsPlaceholder = '';
@ -187,7 +192,7 @@ class StatusContent extends ImmutablePureComponent {
}
return (
<div className={classNames} ref={this.setRef} tabIndex='0' style={directionStyle} onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp}>
<div className={[].join(' ')} ref={this.setRef} tabIndex='0' style={directionStyle} onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp}>
<p style={{ marginBottom: hidden && status.get('mentions').isEmpty() ? '0px' : null }}>
<span dangerouslySetInnerHTML={spoilerContent} lang={status.get('language')} />
{' '}
@ -200,8 +205,15 @@ class StatusContent extends ImmutablePureComponent {
</div>
);
} else if (this.props.onClick) {
const hasMarginBottom = !!status.get('card') || !!status.get('poll') || status.get('media_attachments').size > 0
const containerClasses = cx({
paddingHorizontal15PX: 1,
marginBottom15PX: hasMarginBottom,
})
return (
<Fragment>
<div className={containerClasses}>
<div
ref={this.setRef}
tabIndex='0'
@ -212,17 +224,16 @@ class StatusContent extends ImmutablePureComponent {
onMouseDown={this.handleMouseDown}
onMouseUp={this.handleMouseUp}
/>
{
this.state.collapsed &&
<button
className={[styles.default].join(' ')}
className={[styles.default, styles.displayFlex, styles.cursorPointer, styles.paddingVertical2PX, styles.text, styles.colorBlack, styles.fontWeightBold, styles.fontSize15PX].join(' ')}
onClick={this.props.onClick}
>
<FormattedMessage id='status.read_more' defaultMessage='Read more' />
{intl.formatMessage(messages.readMore)}
</button>
}
</Fragment>
</div>
)
}
@ -230,7 +241,7 @@ class StatusContent extends ImmutablePureComponent {
<div
tabIndex='0'
ref={this.setRef}
className={[styles.statusContent].join(' ')}
className={[styles.paddingHorizontal15PX, styles.marginBottom15PX, styles.statusContent].join(' ')}
style={directionStyle}
dangerouslySetInnerHTML={content}
lang={status.get('language')}

@ -1,8 +1,8 @@
import ImmutablePureComponent from 'react-immutable-pure-component';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { me } from '../../initial_state';
import ComposeFormContainer from '../../features/compose/containers/compose_form_container';
import Avatar from '../avatar';
import { me } from '../initial_state';
import ComposeFormContainer from '../features/compose/containers/compose_form_container';
import Avatar from './avatar';
const mapStateToProps = state => {
return {
@ -26,7 +26,7 @@ class TimelineComposeBlock extends ImmutablePureComponent {
const { account, size, ...rest } = this.props;
return (
<section className={[styles.default, styles.radiusSmall, styles.border1PX, styles.borderColorSubtle, styles.backgroundWhite, styles.marginBottom15PX].join(' ')}>
<section className={[styles.default, styles.overflowHidden, styles.radiusSmall, styles.border1PX, styles.borderColorSubtle, styles.backgroundWhite, styles.marginBottom15PX].join(' ')}>
<div className={[styles.default, styles.backgroundSubtle, styles.borderBottom1PX, styles.borderColorSubtle, styles.paddingHorizontal15PX, styles.paddingVertical2PX].join(' ')}>
<h1 className={[styles.default, styles.text, styles.colorSubtle, styles.fontSize12PX, styles.fontWeight500, styles.lineHeight2, styles.paddingVertical2PX].join(' ')}>
Create Post

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

@ -1,19 +0,0 @@
.timeline-compose-block {
display: flex;
align-items: flex-start;
padding: 20px;
margin-bottom: 20px;
@include gab-container-standards();
.emoji-picker-wrapper {
.emoji-picker-dropdown {
top: 10px;
}
}
.compose-form {
flex: 1 1;
padding: 0 0 0 20px !important;
position: relative;
}
}

@ -50,6 +50,7 @@ const makeMapStateToProps = () => {
const mapStateToProps = (state, props) => ({
status: getStatus(state, props),
replies: state.getIn(['contexts', 'replies', props.id]),
});
return mapStateToProps;

@ -7,7 +7,7 @@ import { expandAccountFeaturedTimeline, expandAccountTimeline } from '../../acti
import { fetchAccountIdentityProofs } from '../../actions/identity_proofs';
import { me } from '../../initial_state';
import StatusList from '../../components/status_list/status_list';
import ColumnIndicator from '../../components/column_indicator/column_indicator';
import ColumnIndicator from '../../components/column_indicator';
import Column from '../../components/column';
import SectionHeadlineBar from '../../components/section_headline_bar' ;

@ -1,23 +1,36 @@
import { length } from 'stringz';
import classNames from 'classnames';
import { length } from 'stringz'
export default class CharacterCounter extends PureComponent {
static propTypes = {
text: PropTypes.string.isRequired,
max: PropTypes.number.isRequired,
};
}
render () {
const diff = this.props.max - length(this.props.text);
const classes = classNames('character-counter', {
'character-counter--over': (diff < 0),
});
const radius = 12
const circumference = 2 * Math.PI * radius
const diff = length(this.props.text) / this.props.max
const dashoffset = circumference * (1 - diff)
return (
<div className='character-counter__wrapper'>
<span className={classes}>{diff}</span>
<div className={[styles.default, styles.marginRight10PX, styles.justifyContentCenter, styles.alignItemsCenter].join(' ')}>
<svg width="32" height="32" viewBox="0 0 32 32">
<circle fill='none' cx="16" cy="16" r="12" fill="none" stroke="#e6e6e6" stroke-width="2" />
<circle style={{
// transform: 'rotate(-90deg)',
strokeDashoffset: dashoffset,
strokeDasharray: circumference,
}}
fill='none'
cx="16"
cy="16"
r="12"
strokeWidth="2"
strokeLinecap='round'
stroke='#21cf7a'
/>
</svg>
</div>
)
}

@ -59,7 +59,7 @@ export default class ComposeExtraButton extends PureComponent {
onMouseEnter={() => this.handleOnMouseEnter()}
onMouseLeave={() => this.handleOnMouseLeave()}
>
<Icon icon={icon} width='18px' height='18px' />
<Icon id={icon} width='18px' height='18px' className={styles.fillColorSubtle} />
<span className={titleClasses}>
{title}
</span>

@ -299,6 +299,7 @@ class ComposeForm extends ImmutablePureComponent {
</div>
<CharacterCounter max={maxPostCharacterCount} text={text} />
<Button
className={[styles.fontSize15PX, styles.paddingHorizontal15PX].join(' ')}
text={intl.formatMessage(scheduledAt ? messages.schedulePost : messages.publish)}
onClick={this.handleSubmit}
disabled={disabledButton}

@ -54,7 +54,7 @@ class PollButton extends PureComponent {
title={intl.formatMessage(active ? messages.remove_poll : messages.title)}
disabled={disabled}
onClick={this.handleClick}
icon='tasks'
icon='poll'
/>
)
}

@ -65,7 +65,7 @@ class UploadButton extends ImmutablePureComponent {
title={intl.formatMessage(messages.title)}
disabled={disabled}
onClick={this.handleClick}
icon='upload'
icon='media'
>
<label>
<span className={styles.displayNone}>{intl.formatMessage(messages.upload)}</span>

@ -5,7 +5,7 @@ import { fetchPinnedStatuses } from '../../actions/pin_statuses';
import { meUsername } from '../../initial_state';
import Column from '../../components/column';
import StatusList from '../../components/status_list/status_list';
import ColumnIndicator from '../../components/column_indicator/column_indicator';
import ColumnIndicator from '../../components/column_indicator';
const mapStateToProps = (state, { params: { username } }) => {
return {

@ -115,113 +115,131 @@ export default class Card extends ImmutablePureComponent {
}
renderVideo () {
const { card } = this.props;
const content = { __html: addAutoPlay(card.get('html')) };
const { width } = this.state;
const ratio = card.get('width') / card.get('height');
const height = width / ratio;
const { card } = this.props
const content = { __html: addAutoPlay(card.get('html')) }
const { width } = this.state
const ratio = card.get('width') / card.get('height')
const height = width / ratio
return (
<div
ref={this.setRef}
className='status-card__image status-card-video'
className={[styles.default, styles.backgroundColorSubtle3, styles.positionAbsolute, styles.top0, styles.right0, styles.bottom0, styles.left0, styles.statusCardVideo].join(' ')}
dangerouslySetInnerHTML={content}
style={{
height,
paddingBottom: 0,
}}
/>
);
)
}
render () {
const { card } = this.props;
const { width, embedded } = this.state;
const { card } = this.props
const { width, embedded } = this.state
if (card === null) {
return null;
}
if (card === null) return null
const maxDescription = 160
const cardImg = card.get('image')
const provider = card.get('provider_name').length === 0 ? decodeIDNA(getHostname(card.get('url'))) : card.get('provider_name')
const horizontal = (card.get('width') > card.get('height') && (card.get('width') + 100 >= width)) || card.get('type') !== 'link' || embedded
const interactive = card.get('type') !== 'link'
const maxDescription = 150;
const cardImg = card.get('image');
const provider = card.get('provider_name').length === 0 ? decodeIDNA(getHostname(card.get('url'))) : card.get('provider_name');
const horizontal = (card.get('width') > card.get('height') && (card.get('width') + 100 >= width)) || card.get('type') !== 'link' || embedded;
const interactive = card.get('type') !== 'link';
const className = classnames('status-card', {
horizontal,
interactive,
compact: !cardImg && !interactive,
});
const title = interactive ?
<a className='status-card__title' href={card.get('url')} title={card.get('title')} rel='noopener' target='_blank'>
<strong>{card.get('title')}</strong>
</a>
: <strong className='status-card__title' title={card.get('title')}>{card.get('title')}</strong>;
(
<a
className={[styles.default, styles.displayFlex, styles.text, styles.noUnderline, styles.overflowWrapBreakWord, styles.colorBlack, styles.fontSize15PX, styles.fontWeight500].join(' ')}
href={card.get('url')}
title={card.get('title')}
rel='noopener'
target='_blank'
>
{card.get('title')}
</a>
)
: (
<span className={[styles.default, styles.displayFlex, styles.text, styles.overflowWrapBreakWord, styles.colorBlack, styles.fontSize15PX, styles.fontWeight500].join(' ')}>
{card.get('title')}
</span>
)
const description = (
<div className='status-card__content'>
<div className={[styles.default, styles.flexNormal, styles.paddingHorizontal10PX, styles.paddingVertical10PX, styles.borderColorSubtle, styles.borderLeft1PX].join(' ')}>
{title}
{!horizontal && <p className='status-card__description'>{trim(card.get('description') || '', maxDescription)}</p>}
<span className='status-card__host'>
<Icon id='link' fixedWidth />
{' '}
<p className={[styles.default, styles.displayFlex, styles.text, styles.marginVertical5PX, styles.overflowWrapBreakWord, styles.colorSubtle, styles.fontSize13PX, styles.fontWeightNormal].join(' ')}>
{trim(card.get('description') || '', maxDescription)}
</p>
<span className={[styles.default, styles.marginTopAuto, styles.flexRow, styles.alignItemsCenter, styles.colorSubtle, styles.text, styles.displayFlex, styles.textOverflowEllipsis, styles.fontSize13PX].join(' ')}>
<Icon id='link' width='12px' height='12px' className={[styles.fillColorSubtle, styles.marginRight5PX].join(' ')} fixedWidth />
{provider}
</span>
</div>
);
)
let embed = '';
let thumbnail = card ? <div style={{ backgroundImage: `url(${cardImg})` }} className='status-card__image-image' /> : thumbnail = <div className='status-card__image-image' />;
let embed = ''
let thumbnail = interactive ?
<img src={cardImg} className={[styles.default, styles.objectFitCover, styles.positionAbsolute, styles.width100PC, styles.height100PC, styles.top0, styles.right0, styles.bottom0, styles.left0].join(' ')} />
:
<img src={cardImg} className={[styles.default, styles.objectFitCover, styles.width400PX, styles.height260PX].join(' ')} />
if (interactive) {
if (embedded) {
embed = this.renderVideo();
} else {
let iconVariant = 'play';
embed = this.renderVideo()
}
if (card.get('type') === 'photo') {
iconVariant = 'search-plus';
}
let iconVariant = 'play'
embed = (
<div className='status-card__image'>
{thumbnail}
<div className='status-card__actions'>
<div>
<button onClick={this.handleEmbedClick}><Icon id={iconVariant} /></button>
{horizontal && <a href={card.get('url')} target='_blank' rel='noopener'><Icon id='external-link' /></a>}
</div>
</div>
</div>
);
if (card.get('type') === 'photo') {
iconVariant = 'search-plus'
}
return (
<div className={className} ref={this.setRef}>
{embed}
{description}
<div className={[styles.default, styles.width100PC, styles.paddingHorizontal10PX].join(' ')}>
<div className={[styles.default, styles.overflowHidden, styles.width100PC, styles.borderColorSubtle2, styles.border1PX, styles.radiusSmall].join(' ')}>
<div className={[styles.default, styles.width100PC].join(' ')}>
<div className={[styles.default, styles.width100PC, styles.paddingTop5625PC].join(' ')}>
{ !!embed && embed}
{ !embed && thumbnail}
{ !embed &&
<div className={[styles.default, styles.positionAbsolute, styles.top0, styles.right0, styles.left0, styles.bottom0, styles.alignItemsCenter, styles.justifyContentCenter].join(' ')}>
<button
className={[styles.default, styles.cursorPointer, styles.backgroundColorOpaque, styles.radiusSmall, styles.paddingVertical15PX, styles.paddingHorizontal15PX].join(' ')}
onClick={this.handleEmbedClick}
>
<Icon id={iconVariant} className={[styles.fillColorWhite].join(' ')}/>
</button>
</div>
}
</div>
</div>
{description}
</div>
</div>
);
)
} else if (cardImg) {
embed = (
<div className='status-card__image'>
<div className={[styles.default].join(' ')}>
{thumbnail}
</div>
);
)
} else {
embed = (
<div className='status-card__image'>
<Icon id='file-text' />
<div className={[styles.default, styles.paddingVertical15PX, styles.paddingHorizontal15PX, styles.width72PX, styles.alignItemsCenter, styles.justifyContentCenter].join(' ')}>
<Icon id='file-text' width='22px' height='22px' className={styles.fillColorSubtle} />
</div>
);
)
}
return (
<a href={card.get('url')} className={className} target='_blank' rel='noopener' ref={this.setRef}>
{embed}
{description}
</a>
);
<div className={[styles.default, styles.width100PC, styles.paddingHorizontal10PX].join(' ')}>
<a
href={card.get('url')}
className={[styles.default, styles.cursorPointer, styles.flexRow, styles.overflowHidden, styles.noUnderline, styles.width100PC, styles.borderColorSubtle2, styles.border1PX, styles.radiusSmall].join(' ')}
rel='noopener'
ref={this.setRef}
>
{embed}
{description}
</a>
</div>
)
}
}

@ -32,7 +32,7 @@ import HomePage from '../../pages/home_page';
// import GroupSidebarPanel from '../groups/sidebar_panel';
import {
// Status,
Status,
// GettingStarted,
// CommunityTimeline,
// AccountTimeline,
@ -230,10 +230,10 @@ class SwitchingColumnsArea extends PureComponent {
<Redirect from='/@:username/pins' to='/:username/pins' />
<WrappedRoute path='/:username/pins' component={PinnedStatuses} page={ProfilePage} content={children} />
*/ }
<Redirect from='/@:username/posts/:statusId' to='/:username/posts/:statusId' exact />
<WrappedRoute path='/:username/posts/:statusId' publicRoute exact layout={LAYOUT.STATUS} component={Status} content={children} />
{ /*
<Redirect from='/@:username/posts/:statusId/reblogs' to='/:username/posts/:statusId/reblogs' />
<WrappedRoute path='/:username/posts/:statusId/reblogs' layout={LAYOUT.STATUS} component={Reblogs} content={children} />

@ -54,7 +54,7 @@ export default class WrappedRoute extends Component {
}
renderLoading = () => {
return <ColumnIndicator type='loading' />;
return <div />
}
renderError = (props) => {

@ -16,8 +16,8 @@ export default class HomePage extends PureComponent {
return (
<PageLayout
title='Home'
layout={{
HEADER: <span>hello</span>,
HEADER_RIGHT: <Search />,
RIGHT: (
<Fragment>

@ -16,6 +16,11 @@ body {
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", sans-serif;
}
.statusCardVideo iframe {
height: 100% !important;
width: 100% !important;
}
.default {
display: flex;
flex-basis: auto;
@ -93,7 +98,7 @@ body {
.resizeNone { resize: none; }
.circle { border-radius: 9999px; }
.radiusSmall { border-radius: 4px; }
.radiusSmall { border-radius: 8px; }
.borderColorSubtle2 { border-color: #e5e9ed; }
.borderColorSubtle { border-color: #ECECED; }
@ -122,13 +127,16 @@ body {
.backgroundTransparent { background-color: transparent; }
.backgroundPanel { background-color: #aaa; }
.backgroundSubtle { background-color: #F5F8FA; }
.backgroundSubtle_onHover:hover { background-color: #F5F8FA; }
.backgroundSubtle2 { background-color: #e8ecef; }
.backgroundColorSubtle3 { background-color: #F6F6F9; }
.backgroundWhite { background-color: #fff; }
.backgroundColorBrandLightOpaque { background-color: rgba(54, 233, 145, 0.1); }
.backgroundColorOpaque { background-color: rgba(0,0,0, 0.4); }
.backgroundColorBrandLight { background-color: #36e991; }
.backgroundColorBrand { background-color: #21cf7a; }
.backgroundColorBrandDark { background-color: #38A16B; }
.backgroundColorBrandDark_onHover:hover { background-color: #38A16B; }
.colorBlack { color: #000; }
.colorWhite { color: #fff; }
.colorSubtle { color: #666; }
@ -161,9 +169,11 @@ body {
.height53PX { height: 53px; }
.height72PX { height: 72px; }
.height122PX { height: 122px; }
.height260PX { height: 260px; }
.width1015PX { width: 1015px; }
.width660PX { width: 660px; }
.width400PX { width: 400px; }
.width325PX { width: 325px; }
.width250PX { width: 250px; }
.width100PC { width: 100%; }
@ -175,6 +185,7 @@ body {
.textAlignCenter { text-align: center; }
.fontSize24PX { font-size: 24px; }
.fontSize19PX { font-size: 19px; }
.fontSize16PX { font-size: 16px; }
.fontSize15PX { font-size: 15px; }
@ -189,6 +200,7 @@ body {
.noUnderline { text-decoration: none; }
.underline { text-decoration: underline; }
.underline_onHover:hover { text-decoration: underline; }
.objectFitCover { object-fit: cover; }
@ -218,8 +230,11 @@ body {
.marginBottom10PX { margin-bottom: 10px; }
.marginTop10PX { margin-top: 10px; }
.marginTop5PX { margin-top: 5px; }
.marginTopAuto { margin-top: auto; }
.marginTopNeg30PX { margin-top: -30px; }
.paddingTop5625PC { padding-top: 56.25%; }
.paddingHorizontal15PX {
padding-left: 15px;
padding-right: 15px;