Progress
This commit is contained in:
parent
0bd1eb2c77
commit
3ca4ffcc6b
|
@ -1,10 +0,0 @@
|
|||
export const DROPDOWN_MENU_OPEN = 'DROPDOWN_MENU_OPEN';
|
||||
export const DROPDOWN_MENU_CLOSE = 'DROPDOWN_MENU_CLOSE';
|
||||
|
||||
export function openDropdownMenu(id, placement, keyboard) {
|
||||
return { type: DROPDOWN_MENU_OPEN, id, placement, keyboard };
|
||||
}
|
||||
|
||||
export function closeDropdownMenu(id) {
|
||||
return { type: DROPDOWN_MENU_CLOSE, id };
|
||||
}
|
|
@ -10,7 +10,7 @@ export const FAVOURITED_STATUSES_EXPAND_REQUEST = 'FAVOURITED_STATUSES_EXPAND_RE
|
|||
export const FAVOURITED_STATUSES_EXPAND_SUCCESS = 'FAVOURITED_STATUSES_EXPAND_SUCCESS';
|
||||
export const FAVOURITED_STATUSES_EXPAND_FAIL = 'FAVOURITED_STATUSES_EXPAND_FAIL';
|
||||
|
||||
export function fetchFavouritedStatuses() {
|
||||
export function fetchFavoritedStatuses() {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
|
@ -18,26 +18,26 @@ export function fetchFavouritedStatuses() {
|
|||
return;
|
||||
}
|
||||
|
||||
dispatch(fetchFavouritedStatusesRequest());
|
||||
dispatch(fetchFavoritedStatusesRequest());
|
||||
|
||||
api(getState).get('/api/v1/favourites').then(response => {
|
||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||
dispatch(importFetchedStatuses(response.data));
|
||||
dispatch(fetchFavouritedStatusesSuccess(response.data, next ? next.uri : null));
|
||||
dispatch(fetchFavoritedStatusesSuccess(response.data, next ? next.uri : null));
|
||||
}).catch(error => {
|
||||
dispatch(fetchFavouritedStatusesFail(error));
|
||||
dispatch(fetchFavoritedStatusesFail(error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchFavouritedStatusesRequest() {
|
||||
export function fetchFavoritedStatusesRequest() {
|
||||
return {
|
||||
type: FAVOURITED_STATUSES_FETCH_REQUEST,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchFavouritedStatusesSuccess(statuses, next) {
|
||||
export function fetchFavoritedStatusesSuccess(statuses, next) {
|
||||
return {
|
||||
type: FAVOURITED_STATUSES_FETCH_SUCCESS,
|
||||
statuses,
|
||||
|
@ -46,7 +46,7 @@ export function fetchFavouritedStatusesSuccess(statuses, next) {
|
|||
};
|
||||
};
|
||||
|
||||
export function fetchFavouritedStatusesFail(error) {
|
||||
export function fetchFavoritedStatusesFail(error) {
|
||||
return {
|
||||
type: FAVOURITED_STATUSES_FETCH_FAIL,
|
||||
error,
|
||||
|
@ -54,7 +54,7 @@ export function fetchFavouritedStatusesFail(error) {
|
|||
};
|
||||
};
|
||||
|
||||
export function expandFavouritedStatuses() {
|
||||
export function expandFavoritedStatuses() {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
|
@ -64,25 +64,25 @@ export function expandFavouritedStatuses() {
|
|||
return;
|
||||
}
|
||||
|
||||
dispatch(expandFavouritedStatusesRequest());
|
||||
dispatch(expandFavoritedStatusesRequest());
|
||||
|
||||
api(getState).get(url).then(response => {
|
||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||
dispatch(importFetchedStatuses(response.data));
|
||||
dispatch(expandFavouritedStatusesSuccess(response.data, next ? next.uri : null));
|
||||
dispatch(expandFavoritedStatusesSuccess(response.data, next ? next.uri : null));
|
||||
}).catch(error => {
|
||||
dispatch(expandFavouritedStatusesFail(error));
|
||||
dispatch(expandFavoritedStatusesFail(error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function expandFavouritedStatusesRequest() {
|
||||
export function expandFavoritedStatusesRequest() {
|
||||
return {
|
||||
type: FAVOURITED_STATUSES_EXPAND_REQUEST,
|
||||
};
|
||||
};
|
||||
|
||||
export function expandFavouritedStatusesSuccess(statuses, next) {
|
||||
export function expandFavoritedStatusesSuccess(statuses, next) {
|
||||
return {
|
||||
type: FAVOURITED_STATUSES_EXPAND_SUCCESS,
|
||||
statuses,
|
||||
|
@ -90,7 +90,7 @@ export function expandFavouritedStatusesSuccess(statuses, next) {
|
|||
};
|
||||
};
|
||||
|
||||
export function expandFavouritedStatusesFail(error) {
|
||||
export function expandFavoritedStatusesFail(error) {
|
||||
return {
|
||||
type: FAVOURITED_STATUSES_EXPAND_FAIL,
|
||||
error,
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
import api from '../api';
|
||||
import { importFetchedStatuses } from './importer';
|
||||
import { me } from '../initial_state';
|
||||
|
||||
export const PINNED_STATUSES_FETCH_REQUEST = 'PINNED_STATUSES_FETCH_REQUEST';
|
||||
export const PINNED_STATUSES_FETCH_SUCCESS = 'PINNED_STATUSES_FETCH_SUCCESS';
|
||||
export const PINNED_STATUSES_FETCH_FAIL = 'PINNED_STATUSES_FETCH_FAIL';
|
||||
|
||||
export function fetchPinnedStatuses() {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(fetchPinnedStatusesRequest());
|
||||
|
||||
api(getState).get(`/api/v1/accounts/${me}/statuses`, { params: { pinned: true } }).then(response => {
|
||||
dispatch(importFetchedStatuses(response.data));
|
||||
dispatch(fetchPinnedStatusesSuccess(response.data, null));
|
||||
}).catch(error => {
|
||||
dispatch(fetchPinnedStatusesFail(error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchPinnedStatusesRequest() {
|
||||
return {
|
||||
type: PINNED_STATUSES_FETCH_REQUEST,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchPinnedStatusesSuccess(statuses, next) {
|
||||
return {
|
||||
type: PINNED_STATUSES_FETCH_SUCCESS,
|
||||
statuses,
|
||||
next,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchPinnedStatusesFail(error) {
|
||||
return {
|
||||
type: PINNED_STATUSES_FETCH_FAIL,
|
||||
error,
|
||||
};
|
||||
};
|
|
@ -0,0 +1,18 @@
|
|||
export const POPOVER_OPEN = 'POPOVER_OPEN'
|
||||
export const POPOVER_CLOSE = 'POPOVER_CLOSE'
|
||||
|
||||
export function openPopover(type, keyboard = false, placement = 'top') {
|
||||
return {
|
||||
keyboard,
|
||||
placement,
|
||||
type: POPOVER_OPEN,
|
||||
popoverType: type,
|
||||
}
|
||||
}
|
||||
|
||||
export function closePopover(type) {
|
||||
return {
|
||||
type: POPOVER_CLOSE,
|
||||
popoverType: type,
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import { Fragment } from 'react'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import classNames from 'classnames';
|
||||
import classNames from 'classnames/bind'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import Textarea from 'react-textarea-autosize';
|
||||
import { isRtl } from '../../utils/rtl';
|
||||
|
@ -8,6 +8,8 @@ import { textAtCursorMatchesToken } from '../../utils/cursor_token_match';
|
|||
import AutosuggestAccount from '../autosuggest_account';
|
||||
import AutosuggestEmoji from '../autosuggest_emoji';
|
||||
|
||||
const cx = classNames.bind(_s)
|
||||
|
||||
export default class AutosuggestTextbox extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
|
@ -30,6 +32,7 @@ export default class AutosuggestTextbox extends ImmutablePureComponent {
|
|||
onFocus: PropTypes.func,
|
||||
onBlur: PropTypes.func,
|
||||
textarea: PropTypes.bool,
|
||||
small: PropTypes.bool,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
|
@ -188,7 +191,7 @@ export default class AutosuggestTextbox extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
render () {
|
||||
const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, children, className, id, maxLength, textarea } = this.props;
|
||||
const { value, small, suggestions, disabled, placeholder, onKeyUp, autoFocus, children, className, id, maxLength, textarea } = this.props;
|
||||
const { suggestionsHidden } = this.state;
|
||||
const style = { direction: 'ltr' };
|
||||
|
||||
|
@ -196,14 +199,30 @@ export default class AutosuggestTextbox extends ImmutablePureComponent {
|
|||
style.direction = 'rtl';
|
||||
}
|
||||
|
||||
const textClasses = cx({
|
||||
default: 1,
|
||||
lineHeight125: 1,
|
||||
resizeNone: 1,
|
||||
text: 1,
|
||||
displayBlock: 1,
|
||||
outlineNone: 1,
|
||||
backgroundColorPrimary: !small,
|
||||
backgroundSubtle: small,
|
||||
paddingVertical15PX: !small,
|
||||
paddingVertical10PX: small,
|
||||
fontSize16PX: !small,
|
||||
fontSize14PX: small,
|
||||
marginRight5PX: small,
|
||||
})
|
||||
|
||||
if (textarea) {
|
||||
return (
|
||||
<Fragment>
|
||||
<div className={[_s.default].join(' ')}>
|
||||
<div className={[_s.default, _s.flexGrow1].join(' ')}>
|
||||
<div className={[_s.default, _s.marginLeft5PX].join(' ')}>
|
||||
<Textarea
|
||||
inputRef={this.setTextbox}
|
||||
className={[_s.default, _s.backgroundColorPrimary, _s.lineHeight125, _s.resizeNone, _s.paddingVertical15PX, _s.outlineFocusBrand, _s.fontSize16PX, _s.text, _s.displayBlock].join(' ')}
|
||||
className={textClasses}
|
||||
disabled={disabled}
|
||||
placeholder={placeholder}
|
||||
autoFocus={autoFocus}
|
||||
|
|
|
@ -4,7 +4,8 @@ import { Map as ImmutableMap } from 'immutable'
|
|||
import { autoPlayGif } from '../initial_state'
|
||||
import Image from './image'
|
||||
|
||||
export default class Avatar extends ImmutablePureComponent {
|
||||
export default
|
||||
class Avatar extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
account: ImmutablePropTypes.map,
|
||||
|
@ -24,6 +25,7 @@ export default class Avatar extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
handleMouseEnter = () => {
|
||||
// : todo : user popover
|
||||
this.setState({ hovering: true })
|
||||
}
|
||||
|
||||
|
|
|
@ -140,7 +140,6 @@ export default class Button extends PureComponent {
|
|||
}
|
||||
|
||||
if (tagName === 'NavLink' && !!to) {
|
||||
console.log("to: ", to)
|
||||
return (
|
||||
<NavLink {...options}>
|
||||
{theChildren}
|
||||
|
|
|
@ -1,44 +1,120 @@
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import { debounce } from 'lodash'
|
||||
import classNames from 'classnames/bind'
|
||||
import { openPopover, closePopover } from '../actions/popover'
|
||||
import Badge from './badge'
|
||||
import Icon from './icon'
|
||||
|
||||
export default class DisplayName extends ImmutablePureComponent {
|
||||
const cx = classNames.bind(_s)
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
openUserInfoPopover() {
|
||||
dispatch(openPopover('USER_INFO'))
|
||||
},
|
||||
closeUserInfoPopover() {
|
||||
dispatch(closePopover())
|
||||
}
|
||||
})
|
||||
|
||||
export default
|
||||
@connect(null, mapDispatchToProps)
|
||||
class DisplayName extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
account: ImmutablePropTypes.map.isRequired,
|
||||
openUserInfoPopover: PropTypes.func.isRequired,
|
||||
closeUserInfoPopover: PropTypes.func.isRequired,
|
||||
multiline: PropTypes.bool,
|
||||
large: PropTypes.bool,
|
||||
noHover: PropTypes.bool,
|
||||
}
|
||||
|
||||
render () {
|
||||
const { account, multiline } = this.props
|
||||
handleMouseEnter = debounce(() => {
|
||||
console.log("SHOW - USER POPOVER")
|
||||
this.props.openUserInfoPopover()
|
||||
}, 50, { leading: true })
|
||||
|
||||
handleMouseLeave = () => {
|
||||
console.log("HIDE - USER POPOVER")
|
||||
// this.props.closeUserInfoPopover()
|
||||
}
|
||||
|
||||
render() {
|
||||
const { account, multiline, large, noHover } = this.props
|
||||
|
||||
if (!account) return null
|
||||
|
||||
const containerOptions = {
|
||||
className: cx({
|
||||
default: 1,
|
||||
maxWidth100PC: 1,
|
||||
alignItemsCenter: !multiline,
|
||||
flexRow: !multiline,
|
||||
cursorPointer: !noHover,
|
||||
}),
|
||||
onMouseEnter: noHover ? undefined : this.handleMouseEnter,
|
||||
onMouseLeave: noHover ? undefined : this.handleMouseLeave,
|
||||
}
|
||||
|
||||
const displayNameClasses = cx({
|
||||
text: 1,
|
||||
overflowWrapBreakWord: 1,
|
||||
whiteSpaceNoWrap: 1,
|
||||
fontWeightBold: 1,
|
||||
colorPrimary: 1,
|
||||
lineHeight125: 1,
|
||||
marginRight2PX: 1,
|
||||
fontSize15PX: !large,
|
||||
fontSize24PX: large,
|
||||
})
|
||||
|
||||
const usernameClasses = cx({
|
||||
text: 1,
|
||||
displayFlex: 1,
|
||||
flexNormal: 1,
|
||||
flexShrink1: 1,
|
||||
overflowWrapBreakWord: 1,
|
||||
textOverflowEllipsis: 1,
|
||||
colorSecondary: 1,
|
||||
fontWeightNormal: 1,
|
||||
lineHeight15: multiline,
|
||||
lineHeight125: !multiline,
|
||||
marginLeft5PX: !multiline,
|
||||
fontSize15PX: !large,
|
||||
fontSize16PX: large
|
||||
})
|
||||
|
||||
const iconSize = !!large ? '19px' : '16px'
|
||||
|
||||
// : todo :
|
||||
return (
|
||||
<span className={[_s.default, _s.flexRow, _s.maxWidth100PC, _s.alignItemsCenter].join(' ')}>
|
||||
<bdi className={[_s.text, _s.whiteSpaceNoWrap, _s.textOverflowEllipsis].join(' ')}>
|
||||
<strong
|
||||
className={[_s.text, _s.overflowWrapBreakWord, _s.whiteSpaceNoWrap, _s.fontSize15PX, _s.fontWeightBold, _s.colorPrimary, _s.lineHeight125, _s.marginRight2PX].join(' ')}
|
||||
dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }}
|
||||
/>
|
||||
</bdi>
|
||||
{
|
||||
account.get('is_verified') &&
|
||||
<Icon id='verified' width='16px' height='16px' className={_s.default} title='Verified Account' />
|
||||
}
|
||||
{ /*
|
||||
account.get('is_pro') &&
|
||||
<Icon id='verified' width='16px' height='16px' className={_s.default} title='Gab PRO' />
|
||||
*/ }
|
||||
{ /*
|
||||
account.get('is_donor') &&
|
||||
<Icon id='verified' width='16px' height='16px' className={_s.default} title='Gab Donor' />
|
||||
*/ }
|
||||
{ /*
|
||||
account.get('is_investor') &&
|
||||
<Icon id='verified' width='16px' height='16px' className={_s.default} title='Gab Investor' />
|
||||
*/ }
|
||||
<span className={[_s.text, _s.displayFlex, _s.flexNormal, _s.flexShrink1, _s.fontSize15PX, _s.overflowWrapBreakWord, _s.textOverflowEllipsis, _s.marginLeft5PX, _s.colorSecondary, _s.fontWeightNormal, _s.lineHeight125].join(' ')}>
|
||||
<span {...containerOptions}>
|
||||
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}>
|
||||
<bdi className={[_s.text, _s.whiteSpaceNoWrap, _s.textOverflowEllipsis].join(' ')}>
|
||||
<strong
|
||||
className={displayNameClasses}
|
||||
dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }}
|
||||
/>
|
||||
</bdi>
|
||||
{
|
||||
account.get('is_verified') &&
|
||||
<Icon id='verified' width={iconSize} height={iconSize} className={_s.default} title='Verified Account' />
|
||||
}
|
||||
{ /*
|
||||
account.get('is_pro') &&
|
||||
<Icon id='verified' width='16px' height='16px' className={_s.default} title='Gab PRO' />
|
||||
*/ }
|
||||
{ /*
|
||||
account.get('is_donor') &&
|
||||
<Icon id='verified' width='16px' height='16px' className={_s.default} title='Gab Donor' />
|
||||
*/ }
|
||||
{ /*
|
||||
account.get('is_investor') &&
|
||||
<Icon id='verified' width='16px' height='16px' className={_s.default} title='Gab Investor' />
|
||||
*/ }
|
||||
</div>
|
||||
<span className={usernameClasses}>
|
||||
@{account.get('acct')}
|
||||
</span>
|
||||
</span>
|
||||
|
|
|
@ -1,297 +0,0 @@
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import detectPassiveEvents from 'detect-passive-events';
|
||||
import Overlay from 'react-overlays/lib/Overlay';
|
||||
import spring from 'react-motion/lib/spring';
|
||||
import { openDropdownMenu, closeDropdownMenu } from '../../actions/dropdown_menu';
|
||||
import { openModal, closeModal } from '../../actions/modal';
|
||||
import { isUserTouching } from '../../utils/is_mobile';
|
||||
import Motion from '../../features/ui/util/optional_motion'
|
||||
import IconButton from '../icon_button';
|
||||
|
||||
const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false;
|
||||
let id = 0;
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
isModalOpen: state.get('modal').modalType === 'ACTIONS',
|
||||
dropdownPlacement: state.getIn(['dropdown_menu', 'placement']),
|
||||
openDropdownId: state.getIn(['dropdown_menu', 'openId']),
|
||||
openedViaKeyboard: state.getIn(['dropdown_menu', 'keyboard']),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch, { status, items }) => ({
|
||||
onOpen(id, onItemClick, dropdownPlacement, keyboard) {
|
||||
dispatch(isUserTouching() ? openModal('ACTIONS', {
|
||||
status,
|
||||
actions: items,
|
||||
onClick: onItemClick,
|
||||
}) : openDropdownMenu(id, dropdownPlacement, keyboard));
|
||||
},
|
||||
onClose(id) {
|
||||
dispatch(closeModal());
|
||||
dispatch(closeDropdownMenu(id));
|
||||
},
|
||||
});
|
||||
|
||||
class DropdownMenu extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
items: PropTypes.array.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
style: PropTypes.object,
|
||||
placement: PropTypes.string,
|
||||
arrowOffsetLeft: PropTypes.string,
|
||||
arrowOffsetTop: PropTypes.string,
|
||||
openedViaKeyboard: PropTypes.bool,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
style: {},
|
||||
placement: 'bottom',
|
||||
};
|
||||
|
||||
state = {
|
||||
mounted: false,
|
||||
};
|
||||
|
||||
handleDocumentClick = e => {
|
||||
if (this.node && !this.node.contains(e.target)) {
|
||||
this.props.onClose();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
document.addEventListener('click', this.handleDocumentClick, false);
|
||||
document.addEventListener('keydown', this.handleKeyDown, false);
|
||||
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
|
||||
|
||||
if (this.focusedItem && this.props.openedViaKeyboard) this.focusedItem.focus();
|
||||
|
||||
this.setState({ mounted: true });
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
document.removeEventListener('click', this.handleDocumentClick, false);
|
||||
document.removeEventListener('keydown', this.handleKeyDown, false);
|
||||
document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
|
||||
}
|
||||
|
||||
setRef = c => {
|
||||
this.node = c;
|
||||
}
|
||||
|
||||
setFocusRef = c => {
|
||||
this.focusedItem = c;
|
||||
}
|
||||
|
||||
handleKeyDown = e => {
|
||||
const items = Array.from(this.node.getElementsByTagName('a'));
|
||||
const index = items.indexOf(document.activeElement);
|
||||
let element;
|
||||
|
||||
switch(e.key) {
|
||||
case 'ArrowDown':
|
||||
element = items[index+1];
|
||||
if (element) element.focus();
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
element = items[index-1];
|
||||
if (element) element.focus();
|
||||
break;
|
||||
case 'Home':
|
||||
element = items[0];
|
||||
if (element) element.focus();
|
||||
break;
|
||||
case 'End':
|
||||
element = items[items.length-1];
|
||||
if (element) element.focus();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
handleItemKeyDown = e => {
|
||||
if (e.key === 'Enter') {
|
||||
this.handleClick(e);
|
||||
}
|
||||
}
|
||||
|
||||
handleClick = e => {
|
||||
const i = Number(e.currentTarget.getAttribute('data-index'));
|
||||
const { action, to } = this.props.items[i];
|
||||
|
||||
this.props.onClose();
|
||||
|
||||
if (typeof action === 'function') {
|
||||
e.preventDefault();
|
||||
action(e);
|
||||
} else if (to) {
|
||||
e.preventDefault();
|
||||
this.context.router.history.push(to);
|
||||
}
|
||||
}
|
||||
|
||||
renderItem (option, i) {
|
||||
if (option === null) {
|
||||
return <li key={`sep-${i}`} className='dropdown-menu__separator' />;
|
||||
}
|
||||
|
||||
const { text, href = '#', newTab, isLogout } = option;
|
||||
|
||||
return (
|
||||
<li className='dropdown-menu__item' key={`${text}-${i}`}>
|
||||
<a
|
||||
href={href}
|
||||
role='button'
|
||||
tabIndex='0'
|
||||
ref={i === 0 ? this.setFocusRef : null}
|
||||
onClick={this.handleClick}
|
||||
onKeyDown={this.handleItemKeyDown}
|
||||
data-index={i}
|
||||
target={newTab ? '_blank' : null}
|
||||
data-method={isLogout ? 'delete' : null}
|
||||
>
|
||||
{text}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
render () {
|
||||
const { items, style, placement, arrowOffsetLeft, arrowOffsetTop } = this.props;
|
||||
const { mounted } = this.state;
|
||||
return (
|
||||
<Motion defaultStyle={{ opacity: 0, scaleX: 1, scaleY: 1 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
|
||||
{({ opacity, scaleX, scaleY }) => (
|
||||
// It should not be transformed when mounting because the resulting
|
||||
// size will be used to determine the coordinate of the menu by
|
||||
// react-overlays
|
||||
<div className={`dropdown-menu ${placement}`} style={{ ...style, opacity: opacity, transform: mounted ? `scale(${scaleX}, ${scaleY})` : null }} ref={this.setRef}>
|
||||
<div className={`dropdown-menu__arrow ${placement}`} style={{ left: arrowOffsetLeft, top: arrowOffsetTop }} />
|
||||
<ul>
|
||||
{items.map((option, i) => this.renderItem(option, i))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</Motion>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default
|
||||
@connect(mapStateToProps, mapDispatchToProps)
|
||||
class Dropdown extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
icon: PropTypes.string.isRequired,
|
||||
items: PropTypes.array.isRequired,
|
||||
size: PropTypes.number.isRequired,
|
||||
title: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
status: ImmutablePropTypes.map,
|
||||
isUserTouching: PropTypes.func,
|
||||
isModalOpen: PropTypes.bool.isRequired,
|
||||
onOpen: PropTypes.func.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
dropdownPlacement: PropTypes.string,
|
||||
openDropdownId: PropTypes.number,
|
||||
openedViaKeyboard: PropTypes.bool,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
title: 'Menu',
|
||||
};
|
||||
|
||||
state = {
|
||||
id: id++,
|
||||
};
|
||||
|
||||
handleClick = ({ target, type }) => {
|
||||
if (this.state.id === this.props.openDropdownId) {
|
||||
this.handleClose();
|
||||
} else {
|
||||
const { top } = target.getBoundingClientRect();
|
||||
const placement = top * 2 < innerHeight ? 'bottom' : 'top';
|
||||
|
||||
this.props.onOpen(this.state.id, this.handleItemClick, placement, type !== 'click');
|
||||
}
|
||||
}
|
||||
|
||||
handleClose = () => {
|
||||
this.props.onClose(this.state.id);
|
||||
}
|
||||
|
||||
handleKeyDown = e => {
|
||||
switch(e.key) {
|
||||
case ' ':
|
||||
case 'Enter':
|
||||
this.handleClick(e);
|
||||
e.preventDefault();
|
||||
break;
|
||||
case 'Escape':
|
||||
this.handleClose();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
handleItemClick = e => {
|
||||
const i = Number(e.currentTarget.getAttribute('data-index'));
|
||||
const { action, to } = this.props.items[i];
|
||||
|
||||
this.handleClose();
|
||||
|
||||
if (typeof action === 'function') {
|
||||
e.preventDefault();
|
||||
action();
|
||||
} else if (to) {
|
||||
e.preventDefault();
|
||||
this.context.router.history.push(to);
|
||||
}
|
||||
}
|
||||
|
||||
setTargetRef = c => {
|
||||
this.target = c;
|
||||
}
|
||||
|
||||
findTarget = () => {
|
||||
return this.target;
|
||||
}
|
||||
|
||||
componentWillUnmount = () => {
|
||||
if (this.state.id === this.props.openDropdownId) {
|
||||
this.handleClose();
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { icon, items, size, title, disabled, dropdownPlacement, openDropdownId, openedViaKeyboard } = this.props;
|
||||
const open = this.state.id === openDropdownId;
|
||||
|
||||
return (
|
||||
<div onKeyDown={this.handleKeyDown}>
|
||||
<IconButton
|
||||
icon={icon}
|
||||
title={title}
|
||||
active={open}
|
||||
disabled={disabled}
|
||||
size={size}
|
||||
ref={this.setTargetRef}
|
||||
onClick={this.handleClick}
|
||||
/>
|
||||
|
||||
<Overlay show={open} placement={dropdownPlacement} target={this.findTarget}>
|
||||
<DropdownMenu items={items} onClose={this.handleClose} openedViaKeyboard={openedViaKeyboard} />
|
||||
</Overlay>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
.dropdown-menu {
|
||||
z-index: 9999;
|
||||
position: absolute;
|
||||
background: $gab-background-container;
|
||||
padding: 4px 0;
|
||||
box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.5);
|
||||
|
||||
@include border-design($gab-placeholder-accent, 1px, 4px);
|
||||
|
||||
&.left {
|
||||
transform-origin: 100% 50%;
|
||||
}
|
||||
|
||||
&.top {
|
||||
transform-origin: 50% 100%;
|
||||
}
|
||||
|
||||
&.bottom {
|
||||
transform-origin: 50% 0;
|
||||
}
|
||||
|
||||
&.right {
|
||||
transform-origin: 0 50%;
|
||||
}
|
||||
|
||||
&__arrow {
|
||||
position: absolute;
|
||||
border: 0 solid transparent;
|
||||
|
||||
@include size(0);
|
||||
|
||||
&.left {
|
||||
right: -5px;
|
||||
margin-top: -5px;
|
||||
border-width: 5px 0 5px 5px;
|
||||
border-left-color: $gab-placeholder-accent;
|
||||
}
|
||||
|
||||
&.top {
|
||||
bottom: -5px;
|
||||
margin-left: -5px;
|
||||
border-width: 5px 5px 0;
|
||||
border-top-color: $gab-placeholder-accent;
|
||||
}
|
||||
|
||||
&.bottom {
|
||||
top: -5px;
|
||||
margin-left: -5px;
|
||||
border-width: 0 5px 5px;
|
||||
border-bottom-color: $gab-placeholder-accent;
|
||||
}
|
||||
|
||||
&.right {
|
||||
left: -5px;
|
||||
margin-top: -5px;
|
||||
border-width: 5px 5px 5px 0;
|
||||
border-right-color: $gab-placeholder-accent;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
overflow: hidden;
|
||||
padding: 6px 0;
|
||||
}
|
||||
|
||||
&__item a {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
padding: 3px 10px 1px;
|
||||
text-decoration: none;
|
||||
text-transform: capitalize;
|
||||
color: $gab-secondary-text;
|
||||
|
||||
@include text-overflow(nowrap);
|
||||
@include text-sizing(13px, 400, 26px);
|
||||
|
||||
&:focus,
|
||||
&:hover,
|
||||
&:active {
|
||||
outline: 0;
|
||||
color: $gab-text-highlight;
|
||||
background: $gab-background-base !important;
|
||||
box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
&__separator {
|
||||
display: block;
|
||||
margin: 10px !important;
|
||||
height: 1px;
|
||||
background: $gab-background-base;
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export { default } from './dropdown_menu';
|
|
@ -9,6 +9,7 @@ export default class ListItem extends PureComponent {
|
|||
isLast: PropTypes.bool,
|
||||
to: PropTypes.string,
|
||||
title: PropTypes.string,
|
||||
onClick: PropTypes.func,
|
||||
}
|
||||
|
||||
render() {
|
||||
|
|
|
@ -1,9 +1,19 @@
|
|||
import ModalBase from './modal_base'
|
||||
import { closeModal } from '../../actions/modal'
|
||||
import { cancelReplyCompose } from '../../actions/compose'
|
||||
import Bundle from '../../features/ui/util/bundle'
|
||||
import {
|
||||
MuteModal,
|
||||
ReportModal,
|
||||
EmbedModal,
|
||||
// ListEditor,
|
||||
// ListAdder,
|
||||
StatusRevisionModal,
|
||||
} from '../../features/ui/util/async-components'
|
||||
import ModalBase from './modal_base'
|
||||
import BundleModalError from '../bundle_modal_error'
|
||||
import ActionsModal from './actions_modal'
|
||||
import MediaModal from './media_modal'
|
||||
// import VideoModal from './video_modal'
|
||||
import VideoModal from './video_modal'
|
||||
import BoostModal from './boost_modal'
|
||||
import ConfirmationModal from './confirmation_modal'
|
||||
import FocalPointModal from './focal_point_modal'
|
||||
|
@ -12,35 +22,46 @@ import ComposeModal from './compose_modal'
|
|||
import UnauthorizedModal from './unauthorized_modal'
|
||||
import ProUpgradeModal from './pro_upgrade_modal'
|
||||
import ModalLoading from './modal_loading'
|
||||
import {
|
||||
MuteModal,
|
||||
ReportModal,
|
||||
EmbedModal,
|
||||
ListEditor,
|
||||
ListAdder,
|
||||
StatusRevisionModal,
|
||||
} from '../../features/ui/util/async-components'
|
||||
|
||||
const MODAL_COMPONENTS = {
|
||||
'MEDIA': () => Promise.resolve({ default: MediaModal }),
|
||||
// 'VIDEO': () => Promise.resolve({ default: VideoModal }),
|
||||
'BOOST': () => Promise.resolve({ default: BoostModal }),
|
||||
'CONFIRM': () => Promise.resolve({ default: ConfirmationModal }),
|
||||
'MUTE': MuteModal,
|
||||
'REPORT': ReportModal,
|
||||
'ACTIONS': () => Promise.resolve({ default: ActionsModal }),
|
||||
'EMBED': EmbedModal,
|
||||
'LIST_EDITOR': ListEditor,
|
||||
'FOCAL_POINT': () => Promise.resolve({ default: FocalPointModal }),
|
||||
'LIST_ADDER': ListAdder,
|
||||
'HOTKEYS': () => Promise.resolve({ default: HotkeysModal }),
|
||||
'STATUS_REVISION': StatusRevisionModal,
|
||||
'BOOST': () => Promise.resolve({ default: BoostModal }),
|
||||
'COMPOSE': () => Promise.resolve({ default: ComposeModal }),
|
||||
'UNAUTHORIZED': () => Promise.resolve({ default: UnauthorizedModal }),
|
||||
'CONFIRM': () => Promise.resolve({ default: ConfirmationModal }),
|
||||
'EMBED': EmbedModal,
|
||||
'FOCAL_POINT': () => Promise.resolve({ default: FocalPointModal }),
|
||||
'HOTKEYS': () => Promise.resolve({ default: HotkeysModal }),
|
||||
'MEDIA': () => Promise.resolve({ default: MediaModal }),
|
||||
'MUTE': MuteModal,
|
||||
'PRO_UPGRADE': () => Promise.resolve({ default: ProUpgradeModal }),
|
||||
'REPORT': ReportModal,
|
||||
'STATUS_REVISION': StatusRevisionModal,
|
||||
'UNAUTHORIZED': () => Promise.resolve({ default: UnauthorizedModal }),
|
||||
'VIDEO': () => Promise.resolve({ default: VideoModal }),
|
||||
// 'LIST_EDITOR': ListEditor,
|
||||
// 'LIST_ADDER': ListAdder,
|
||||
// group create
|
||||
// group members
|
||||
}
|
||||
|
||||
export default class ModalRoot extends PureComponent {
|
||||
const mapStateToProps = state => ({
|
||||
type: state.get('modal').modalType,
|
||||
props: state.get('modal').modalProps,
|
||||
})
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
onClose (optionalType) {
|
||||
if (optionalType === 'COMPOSE') {
|
||||
dispatch(cancelReplyCompose())
|
||||
}
|
||||
|
||||
dispatch(closeModal())
|
||||
},
|
||||
})
|
||||
|
||||
export default
|
||||
@connect(mapStateToProps, mapDispatchToProps)
|
||||
class ModalRoot extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
type: PropTypes.string,
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
import { defineMessages, injectIntl } from 'react-intl'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import { me } from '../../initial_state'
|
||||
import { shortNumberFormat } from '../../utils/numbers'
|
||||
import PanelLayout from './panel_layout'
|
||||
import UserStat from '../user_stat'
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'about', defaultMessage: 'About' },
|
||||
})
|
||||
|
||||
export default
|
||||
@injectIntl
|
||||
class GroupInfoPanel extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
group: ImmutablePropTypes.list.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
render() {
|
||||
const { intl, group } = this.props
|
||||
|
||||
return (
|
||||
<PanelLayout title={intl.formatMessage(messages.title)}>
|
||||
|
||||
</PanelLayout>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,71 +1,53 @@
|
|||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import { fetchSuggestions, dismissSuggestion } from '../../actions/suggestions';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import AccountContainer from '../../containers/account_container';
|
||||
import PanelLayout from './panel_layout';
|
||||
import { defineMessages, injectIntl } from 'react-intl'
|
||||
import { fetchSuggestions, dismissSuggestion } from '../../actions/suggestions'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import AccountContainer from '../../containers/account_container'
|
||||
import PanelLayout from './panel_layout'
|
||||
|
||||
const messages = defineMessages({
|
||||
dismissSuggestion: { id: 'suggestions.dismiss', defaultMessage: 'Dismiss suggestion' },
|
||||
title: { id: 'who_to_follow.title', defaultMessage: 'Who to Follow' },
|
||||
show_more: { id: 'who_to_follow.more', defaultMessage: 'Show more' },
|
||||
});
|
||||
title: { id: 'media_gallery_panel.title', defaultMessage: 'Media' },
|
||||
show_all: { id: 'media_gallery_panel.all', defaultMessage: 'Show all' },
|
||||
})
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
suggestions: state.getIn(['suggestions', 'items']),
|
||||
});
|
||||
})
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
fetchSuggestions: () => dispatch(fetchSuggestions()),
|
||||
dismissSuggestion: account => dispatch(dismissSuggestion(account.get('id'))),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default
|
||||
@connect(mapStateToProps, mapDispatchToProps)
|
||||
@injectIntl
|
||||
class WhoToFollowPanel extends ImmutablePureComponent {
|
||||
class MediaGalleryPanel extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
suggestions: ImmutablePropTypes.list.isRequired,
|
||||
fetchSuggestions: PropTypes.func.isRequired,
|
||||
dismissSuggestion: PropTypes.func.isRequired,
|
||||
account: ImmutablePropTypes.map.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this.props.fetchSuggestions();
|
||||
componentDidMount() {
|
||||
// this.props.fetchSuggestions()
|
||||
}
|
||||
|
||||
render() {
|
||||
const { intl, /* suggestions, */ dismissSuggestion } = this.props;
|
||||
// : testing!!! :
|
||||
const suggestions = [
|
||||
"1",
|
||||
]
|
||||
// if (suggestions.isEmpty()) {
|
||||
// return null;
|
||||
// }
|
||||
const { intl, account } = this.props
|
||||
|
||||
console.log("account:", account)
|
||||
|
||||
return (
|
||||
<PanelLayout
|
||||
title={intl.formatMessage(messages.title)}
|
||||
footerButtonTitle={intl.formatMessage(messages.show_more)}
|
||||
footerButtonTo='/explore'
|
||||
headerButtonTitle={intl.formatMessage(messages.show_all)}
|
||||
headerButtonTo='/explore'
|
||||
>
|
||||
<div className={_s.default}>
|
||||
{suggestions && suggestions.map(accountId => (
|
||||
<AccountContainer
|
||||
key={accountId}
|
||||
id={accountId}
|
||||
actionIcon='times'
|
||||
actionTitle={intl.formatMessage(messages.dismissSuggestion)}
|
||||
onActionClick={dismissSuggestion}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
</PanelLayout>
|
||||
);
|
||||
};
|
||||
};
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,71 +1,157 @@
|
|||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import { fetchSuggestions, dismissSuggestion } from '../../actions/suggestions';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import AccountContainer from '../../containers/account_container';
|
||||
import PanelLayout from './panel_layout';
|
||||
import { defineMessages, injectIntl } from 'react-intl'
|
||||
import { fetchSuggestions, dismissSuggestion } from '../../actions/suggestions'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import { List as ImmutableList } from 'immutable'
|
||||
import classNames from 'classnames/bind'
|
||||
import PanelLayout from './panel_layout'
|
||||
import Icon from '../icon'
|
||||
import Text from '../text'
|
||||
|
||||
const cx = classNames.bind(_s)
|
||||
|
||||
const messages = defineMessages({
|
||||
dismissSuggestion: { id: 'suggestions.dismiss', defaultMessage: 'Dismiss suggestion' },
|
||||
title: { id: 'who_to_follow.title', defaultMessage: 'Who to Follow' },
|
||||
show_more: { id: 'who_to_follow.more', defaultMessage: 'Show more' },
|
||||
});
|
||||
title: { id: 'about', defaultMessage: 'About' },
|
||||
linkVerifiedOn: { id: 'account.link_verified_on', defaultMessage: 'Ownership of this link was checked on {date}' },
|
||||
account_locked: { id: 'account.locked_info', defaultMessage: 'This account privacy status is set to locked. The owner manually reviews who can follow them.' },
|
||||
bot: { id: 'account.badges.bot', defaultMessage: 'Bot' },
|
||||
memberSince: { id: 'account.member_since', defaultMessage: 'Member since {date}' },
|
||||
})
|
||||
|
||||
const mapStateToProps = (state, { account }) => {
|
||||
const identityProofs = !!account ? state.getIn(['identity_proofs', account.get('id')], ImmutableList()) : ImmutableList();
|
||||
return {
|
||||
identityProofs,
|
||||
domain: state.getIn(['meta', 'domain']),
|
||||
};
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
suggestions: state.getIn(['suggestions', 'items']),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
fetchSuggestions: () => dispatch(fetchSuggestions()),
|
||||
dismissSuggestion: account => dispatch(dismissSuggestion(account.get('id'))),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default
|
||||
@connect(mapStateToProps, mapDispatchToProps)
|
||||
@injectIntl
|
||||
class WhoToFollowPanel extends ImmutablePureComponent {
|
||||
class ProfileInfoPanel extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
suggestions: ImmutablePropTypes.list.isRequired,
|
||||
fetchSuggestions: PropTypes.func.isRequired,
|
||||
dismissSuggestion: PropTypes.func.isRequired,
|
||||
identityProofs: ImmutablePropTypes.list,
|
||||
account: ImmutablePropTypes.map.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this.props.fetchSuggestions();
|
||||
componentDidMount() {
|
||||
this.props.fetchSuggestions()
|
||||
}
|
||||
|
||||
render() {
|
||||
const { intl, /* suggestions, */ dismissSuggestion } = this.props;
|
||||
// : testing!!! :
|
||||
const suggestions = [
|
||||
"1",
|
||||
]
|
||||
// if (suggestions.isEmpty()) {
|
||||
// return null;
|
||||
// }
|
||||
const { intl, account, identityProofs } = this.props
|
||||
|
||||
const fields = !account ? null : account.get('fields')
|
||||
const content = !account ? null : { __html: account.get('note_emojified') }
|
||||
const memberSinceDate = !account ? null : intl.formatDate(account.get('created_at'), { month: 'long', year: 'numeric' });
|
||||
const hasNote = !!content ? (account.get('note').length > 0 && account.get('note') !== '<p></p>') : false
|
||||
|
||||
const lineClasses = cx({
|
||||
default: 1,
|
||||
flexRow: 1,
|
||||
alignItemsCenter: 1,
|
||||
borderTop1PX: 1,
|
||||
borderColorSecondary: 1,
|
||||
marginTop10PX: 1,
|
||||
paddingTop10PX: 1,
|
||||
})
|
||||
|
||||
const memberLineClasses = cx({
|
||||
default: 1,
|
||||
flexRow: 1,
|
||||
alignItemsCenter: 1,
|
||||
borderTop1PX: hasNote,
|
||||
borderColorSecondary: hasNote,
|
||||
marginTop10PX: hasNote,
|
||||
paddingTop10PX: hasNote,
|
||||
})
|
||||
|
||||
return (
|
||||
<PanelLayout
|
||||
title={intl.formatMessage(messages.title)}
|
||||
footerButtonTitle={intl.formatMessage(messages.show_more)}
|
||||
footerButtonTo='/explore'
|
||||
>
|
||||
<div className={_s.default}>
|
||||
{suggestions && suggestions.map(accountId => (
|
||||
<AccountContainer
|
||||
key={accountId}
|
||||
id={accountId}
|
||||
actionIcon='times'
|
||||
actionTitle={intl.formatMessage(messages.dismissSuggestion)}
|
||||
onActionClick={dismissSuggestion}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<PanelLayout title={intl.formatMessage(messages.title)}>
|
||||
{
|
||||
!!account &&
|
||||
<div className={[_s.default].join(' ')}>
|
||||
{
|
||||
hasNote &&
|
||||
<div className={_s.dangerousContent} dangerouslySetInnerHTML={content} />
|
||||
}
|
||||
|
||||
<div className={memberLineClasses}>
|
||||
<Icon id='calendar' width='12px' height='12px' className={_s.fillcolorSecondary} />
|
||||
<Text
|
||||
size='small'
|
||||
color='secondary'
|
||||
className={_s.marginLeft5PX}
|
||||
>
|
||||
{
|
||||
intl.formatMessage(messages.memberSince, {
|
||||
date: memberSinceDate
|
||||
})
|
||||
}
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
{(fields.size > 0 || identityProofs.size > 0) && (
|
||||
<div className={[_s.default]}>
|
||||
|
||||
{identityProofs.map((proof, i) => (
|
||||
<dl className={lineClasses} key={`profile-identity-proof-${i}`}>
|
||||
<dt
|
||||
className={_s.dangerousContent}
|
||||
dangerouslySetInnerHTML={{ __html: proof.get('provider') }}
|
||||
/>
|
||||
|
||||
{ /* : todo : */ }
|
||||
<dd className='verified'>
|
||||
<a href={proof.get('proof_url')} target='_blank' rel='noopener'>
|
||||
<span title={intl.formatMessage(messages.linkVerifiedOn, { date: intl.formatDate(proof.get('updated_at'), dateFormatOptions) })}>
|
||||
<Icon id='check' className='verified__mark' />
|
||||
</span>
|
||||
</a>
|
||||
<a href={proof.get('profile_url')} target='_blank' rel='noopener'>
|
||||
<span
|
||||
className={_s.dangerousContent}
|
||||
dangerouslySetInnerHTML={{ __html: ' ' + proof.get('provider_username') }}
|
||||
/>
|
||||
</a>
|
||||
</dd>
|
||||
</dl>
|
||||
))}
|
||||
|
||||
{
|
||||
fields.map((pair, i) => (
|
||||
<dl className={lineClasses} key={`profile-field-${i}`}>
|
||||
<dt
|
||||
className={[_s.text, _s.dangerousContent].join('')}
|
||||
dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }}
|
||||
title={pair.get('name')}
|
||||
/>
|
||||
<dd
|
||||
className={[_s.dangerousContent, _s.marginLeftAuto].join(' ')}
|
||||
title={pair.get('value_plain')}
|
||||
dangerouslySetInnerHTML={{ __html: pair.get('value_emojified') }}
|
||||
/>
|
||||
</dl>
|
||||
))
|
||||
}
|
||||
|
||||
</div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
}
|
||||
</PanelLayout>
|
||||
);
|
||||
};
|
||||
};
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
import { defineMessages, injectIntl } from 'react-intl'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import { me } from '../../initial_state'
|
||||
import { shortNumberFormat } from '../../utils/numbers'
|
||||
import PanelLayout from './panel_layout'
|
||||
import UserStat from '../user_stat'
|
||||
|
||||
const messages = defineMessages({
|
||||
gabs: { id: 'account.gabs', defaultMessage: 'Gabs' },
|
||||
followers: { id: 'account.followers', defaultMessage: 'Followers' },
|
||||
follows: { id: 'account.follows', defaultMessage: 'Follows' },
|
||||
favorites: { id: 'navigation_bar.favorites', defaultMessage: 'Favorites' },
|
||||
})
|
||||
|
||||
export default
|
||||
@injectIntl
|
||||
class ProfileStatsPanel extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
account: ImmutablePropTypes.list.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
render() {
|
||||
const { intl, account } = this.props
|
||||
|
||||
if (!account) return null
|
||||
|
||||
return (
|
||||
<PanelLayout>
|
||||
{
|
||||
!!account &&
|
||||
<div className={[_s.default, _s.flexRow].join(' ')}>
|
||||
<UserStat
|
||||
title={intl.formatMessage(messages.gabs)}
|
||||
value={shortNumberFormat(account.get('statuses_count'))}
|
||||
to={`/${account.get('acct')}`}
|
||||
/>
|
||||
<UserStat
|
||||
title={intl.formatMessage(messages.follows)}
|
||||
value={shortNumberFormat(account.get('following_count'))}
|
||||
to={`/${account.get('acct')}/following`}
|
||||
/>
|
||||
<UserStat
|
||||
title={intl.formatMessage(messages.followers)}
|
||||
value={shortNumberFormat(account.get('followers_count'))}
|
||||
to={`/${account.get('acct')}/followers`}
|
||||
/>
|
||||
{
|
||||
account.get('id') === me &&
|
||||
<UserStat
|
||||
title={intl.formatMessage(messages.favorites)}
|
||||
value={shortNumberFormat(account.get('favourite_count'))}
|
||||
to={`/${account.get('acct')}/favorites`}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</PanelLayout>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ import { NavLink } from 'react-router-dom'
|
|||
import { injectIntl, defineMessages } from 'react-intl'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import { autoPlayGif, me } from '../../initial_state'
|
||||
import { me } from '../../initial_state'
|
||||
import { makeGetAccount } from '../../selectors'
|
||||
import { shortNumberFormat } from '../../utils/numbers'
|
||||
import DisplayName from '../display_name'
|
||||
|
@ -18,10 +18,8 @@ const messages = defineMessages({
|
|||
})
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const getAccount = makeGetAccount()
|
||||
|
||||
return {
|
||||
account: getAccount(state, me),
|
||||
account: makeGetAccount()(state, me),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,25 +34,6 @@ class UserPanel extends ImmutablePureComponent {
|
|||
|
||||
render() {
|
||||
const { account, intl } = this.props
|
||||
const displayNameHtml = { __html: account.get('display_name_html') }
|
||||
|
||||
const statItems = [
|
||||
{
|
||||
to: `/${account.get('acct')}`,
|
||||
title: intl.formatMessage(messages.gabs),
|
||||
value: shortNumberFormat(account.get('statuses_count')),
|
||||
},
|
||||
{
|
||||
to: `/${account.get('acct')}/followers`,
|
||||
title: intl.formatMessage(messages.followers),
|
||||
value: shortNumberFormat(account.get('followers_count')),
|
||||
},
|
||||
{
|
||||
to: `/${account.get('acct')}/following`,
|
||||
title: intl.formatMessage(messages.follows),
|
||||
value: shortNumberFormat(account.get('following_count')),
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<PanelLayout noPadding>
|
||||
|
@ -76,11 +55,21 @@ class UserPanel extends ImmutablePureComponent {
|
|||
</NavLink>
|
||||
|
||||
<div className={[_s.default, _s.marginBottom15PX, _s.marginTop5PX, _s.flexRow, _s.paddingHorizontal15PX].join(' ')}>
|
||||
{
|
||||
statItems.map((statItem, i) => (
|
||||
<UserStat {...statItem} key={`user-stat-panel-item-${i}`} />
|
||||
))
|
||||
}
|
||||
<UserStat
|
||||
to={`/${account.get('acct')}`}
|
||||
title={intl.formatMessage(messages.gabs)}
|
||||
value={shortNumberFormat(account.get('statuses_count'))}
|
||||
/>
|
||||
<UserStat
|
||||
to={`/${account.get('acct')}/followers`}
|
||||
title={intl.formatMessage(messages.followers)}
|
||||
value={shortNumberFormat(account.get('followers_count'))}
|
||||
/>
|
||||
<UserStat
|
||||
to={`/${account.get('acct')}/following`}
|
||||
title={intl.formatMessage(messages.follows)}
|
||||
value={shortNumberFormat(account.get('following_count'))}
|
||||
/>
|
||||
</div>
|
||||
</PanelLayout>
|
||||
)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export default class Popover extends PureComponent {
|
||||
export default class ContentWarningPopover extends PureComponent {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export default class Popover extends PureComponent {
|
||||
export default class DatePickerPopover extends PureComponent {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import PopoverLayout from './popover_layout'
|
||||
import Text from '../text'
|
||||
|
||||
export default class UserInfoPopover extends PureComponent {
|
||||
render() {
|
||||
return (
|
||||
<PopoverLayout>
|
||||
<Text>testing</Text>
|
||||
</PopoverLayout>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
export default class Popover extends PureComponent {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{ /* */ }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import classnames from 'classnames/bind'
|
||||
import Overlay from 'react-overlays/lib/Overlay'
|
||||
import spring from 'react-motion/lib/spring'
|
||||
import Motion from '../../features/ui/util/optional_motion'
|
||||
import { openPopover, closePopover } from '../../actions/popover'
|
||||
import { openModal, closeModal } from '../../actions/modal'
|
||||
import { isUserTouching } from '../../utils/is_mobile'
|
||||
|
||||
const cx = classnames.bind(_s)
|
||||
|
||||
let id = 0
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
isModalOpen: state.get('modal').modalType === 'ACTIONS',
|
||||
popoverPlacement: state.getIn(['popover', 'placement']),
|
||||
openPopoverType: state.getIn(['popover', 'popoverType']),
|
||||
openedViaKeyboard: state.getIn(['popover', 'keyboard']),
|
||||
})
|
||||
|
||||
const mapDispatchToProps = (dispatch, { status, items }) => ({
|
||||
onOpen(id, onItemClick, popoverPlacement, keyboard) {
|
||||
dispatch(isUserTouching() ? openModal('ACTIONS', {
|
||||
status,
|
||||
actions: items,
|
||||
onClick: onItemClick,
|
||||
}) : openPopover(id, popoverPlacement, keyboard))
|
||||
},
|
||||
onClose(id) {
|
||||
dispatch(closeModal())
|
||||
dispatch(closePopover(id))
|
||||
},
|
||||
})
|
||||
|
||||
export default
|
||||
@connect(mapStateToProps, mapDispatchToProps)
|
||||
class PopoverBase extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
icon: PropTypes.string.isRequired,
|
||||
items: PropTypes.array.isRequired,
|
||||
size: PropTypes.number.isRequired,
|
||||
title: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
status: ImmutablePropTypes.map,
|
||||
isUserTouching: PropTypes.func,
|
||||
isModalOpen: PropTypes.bool.isRequired,
|
||||
onOpen: PropTypes.func.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
popoverPlacement: PropTypes.string,
|
||||
openPopoverType: PropTypes.number,
|
||||
openedViaKeyboard: PropTypes.bool,
|
||||
visible: PropTypes.bool,
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
title: 'Menu',
|
||||
}
|
||||
|
||||
state = {
|
||||
id: id++,
|
||||
}
|
||||
|
||||
handleClick = ({ target, type }) => {
|
||||
if (this.state.id === this.props.openPopoverType) {
|
||||
this.handleClose()
|
||||
} else {
|
||||
const { top } = target.getBoundingClientRect()
|
||||
const placement = top * 2 < innerHeight ? 'bottom' : 'top'
|
||||
|
||||
this.props.onOpen(this.state.id, this.handleItemClick, placement, type !== 'click')
|
||||
}
|
||||
}
|
||||
|
||||
handleClose = () => {
|
||||
this.props.onClose(this.state.id)
|
||||
}
|
||||
|
||||
handleKeyDown = e => {
|
||||
switch (e.key) {
|
||||
case ' ':
|
||||
case 'Enter':
|
||||
this.handleClick(e)
|
||||
e.preventDefault()
|
||||
break
|
||||
case 'Escape':
|
||||
this.handleClose()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
handleItemClick = e => {
|
||||
const i = Number(e.currentTarget.getAttribute('data-index'))
|
||||
const { action, to } = this.props.items[i]
|
||||
|
||||
this.handleClose()
|
||||
|
||||
if (typeof action === 'function') {
|
||||
e.preventDefault()
|
||||
action()
|
||||
} else if (to) {
|
||||
e.preventDefault()
|
||||
this.context.router.history.push(to)
|
||||
}
|
||||
}
|
||||
|
||||
setTargetRef = c => {
|
||||
this.target = c
|
||||
}
|
||||
|
||||
findTarget = () => {
|
||||
return this.target
|
||||
}
|
||||
|
||||
componentWillUnmount = () => {
|
||||
if (this.state.id === this.props.openPopoverType) {
|
||||
this.handleClose()
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { icon, children, visible, items, size, title, disabled, popoverPlacement, openPopoverType, openedViaKeyboard } = this.props
|
||||
const open = this.state.id === openPopoverType
|
||||
|
||||
const containerClasses = cx({
|
||||
default: 1,
|
||||
z4: 1,
|
||||
displayNone: !visible,
|
||||
})
|
||||
|
||||
return (
|
||||
<div onKeyDown={this.handleKeyDown} className={containerClasses}>
|
||||
<div show={open} placement={popoverPlacement} target={this.findTarget}>
|
||||
{ /* <PopoverMenu items={items} onClose={this.handleClose} openedViaKeyboard={openedViaKeyboard} /> */}
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,8 +1,14 @@
|
|||
export default class Popover extends PureComponent {
|
||||
import Block from '../block'
|
||||
|
||||
export default class PopoverLayout extends PureComponent {
|
||||
render() {
|
||||
const { children } = this.props
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ /* */ }
|
||||
<Block>
|
||||
{children}
|
||||
</Block>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,171 @@
|
|||
import detectPassiveEvents from 'detect-passive-events'
|
||||
import { closePopover } from '../../actions/popover'
|
||||
import Bundle from '../../features/ui/util/bundle'
|
||||
import BundleModalError from '../bundle_modal_error'
|
||||
import PopoverBase from './popover_base'
|
||||
import ContentWarningPopover from './content_warning_popover'
|
||||
import DatePickerPopover from './date_picker_popover'
|
||||
import GroupInfoPopover from './group_info_popover'
|
||||
import ProfileOptionsPopover from './profile_options_popover'
|
||||
import SearchPopover from './search_popover'
|
||||
import SidebarMorePopover from './sidebar_more_popover'
|
||||
import StatusOptionsPopover from './status_options_popover'
|
||||
import UserInfoPopover from './user_info_popover'
|
||||
import StatusVisibilityPopover from './status_visibility_popover'
|
||||
|
||||
const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false
|
||||
|
||||
const POPOVER_COMPONENTS = {
|
||||
CONTENT_WARNING: () => Promise.resolve({ default: ContentWarningPopover }),
|
||||
DATE_PICKER: () => Promise.resolve({ default: DatePickerPopover }),
|
||||
GROUP_INFO: () => GroupInfoPopover,
|
||||
PROFILE_OPTIONS: () => ProfileOptionsPopover,
|
||||
SEARCH: () => Promise.resolve({ default: SearchPopover }),
|
||||
SIDEBAR_MORE: () => Promise.resolve({ default: SidebarMorePopover }),
|
||||
STATUS_OPTIONS: () => Promise.resolve({ default: StatusOptionsPopover }),
|
||||
STATUS_VISIBILITY: () => Promise.resolve({ default: StatusVisibilityPopover }),
|
||||
USER_INFO: () => UserInfoPopover,
|
||||
}
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
type: state.get('popover').popoverType,
|
||||
props: state.get('popover').popoverProps,
|
||||
})
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
onClose(optionalType) {
|
||||
//
|
||||
dispatch(closePopover())
|
||||
},
|
||||
})
|
||||
|
||||
export default
|
||||
@connect(mapStateToProps, mapDispatchToProps)
|
||||
class PopoverRoot extends PureComponent {
|
||||
static propTypes = {
|
||||
type: PropTypes.string,
|
||||
props: PropTypes.object,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
getSnapshotBeforeUpdate() {
|
||||
return { visible: !!this.props.type }
|
||||
}
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
onClose: PropTypes.func.isRequired,
|
||||
style: PropTypes.object,
|
||||
placement: PropTypes.string,
|
||||
openedViaKeyboard: PropTypes.bool,
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
style: {},
|
||||
placement: 'bottom',
|
||||
}
|
||||
|
||||
state = {
|
||||
mounted: false,
|
||||
}
|
||||
|
||||
handleDocumentClick = e => {
|
||||
if (this.node && !this.node.contains(e.target)) {
|
||||
this.props.onClose()
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
document.addEventListener('click', this.handleDocumentClick, false)
|
||||
document.addEventListener('keydown', this.handleKeyDown, false)
|
||||
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions)
|
||||
|
||||
if (this.focusedItem && this.props.openedViaKeyboard) this.focusedItem.focus()
|
||||
|
||||
this.setState({ mounted: true })
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener('click', this.handleDocumentClick, false)
|
||||
document.removeEventListener('keydown', this.handleKeyDown, false)
|
||||
document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions)
|
||||
}
|
||||
|
||||
setRef = c => {
|
||||
this.node = c
|
||||
}
|
||||
|
||||
setFocusRef = c => {
|
||||
this.focusedItem = c
|
||||
}
|
||||
|
||||
handleKeyDown = e => {
|
||||
const items = Array.from(this.node.getElementsByTagName('a'))
|
||||
const index = items.indexOf(document.activeElement)
|
||||
let element
|
||||
|
||||
switch (e.key) {
|
||||
case 'ArrowDown':
|
||||
element = items[index + 1]
|
||||
if (element) element.focus()
|
||||
break
|
||||
case 'ArrowUp':
|
||||
element = items[index - 1]
|
||||
if (element) element.focus()
|
||||
break
|
||||
case 'Home':
|
||||
element = items[0]
|
||||
if (element) element.focus()
|
||||
break
|
||||
case 'End':
|
||||
element = items[items.length - 1]
|
||||
if (element) element.focus()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
handleItemKeyDown = e => {
|
||||
if (e.key === 'Enter') {
|
||||
this.handleClick(e)
|
||||
}
|
||||
}
|
||||
|
||||
handleClick = e => {
|
||||
const i = Number(e.currentTarget.getAttribute('data-index'))
|
||||
const { action, to } = this.props.items[i]
|
||||
|
||||
this.props.onClose()
|
||||
|
||||
if (typeof action === 'function') {
|
||||
e.preventDefault()
|
||||
action(e)
|
||||
} else if (to) {
|
||||
e.preventDefault()
|
||||
this.context.router.history.push(to)
|
||||
}
|
||||
}
|
||||
|
||||
renderError = (props) => {
|
||||
return <BundleModalError />
|
||||
}
|
||||
|
||||
render() {
|
||||
const { type, style, placement } = this.props
|
||||
const { mounted } = this.state
|
||||
const visible = !!type
|
||||
|
||||
console.log("popover root - type, visible:", type, visible)
|
||||
|
||||
return (
|
||||
<PopoverBase className={`popover-menu ${placement}`} visible={visible} ref={this.setRef}>
|
||||
{
|
||||
visible &&
|
||||
<UserInfoPopover />
|
||||
}
|
||||
</PopoverBase>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import PopoverLayout from './popover_layout'
|
||||
import Text from '../text'
|
||||
|
||||
export default class UserInfoPopover extends PureComponent {
|
||||
render() {
|
||||
return (
|
||||
<PopoverLayout>
|
||||
<Text>testing</Text>
|
||||
</PopoverLayout>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
export default class Popover extends PureComponent {
|
||||
export default class SearchPopover extends PureComponent {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export default class Popover extends PureComponent {
|
||||
export default class SidebarMorePopover extends PureComponent {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export default class Popover extends PureComponent {
|
||||
export default class StatusOptionsPopover extends PureComponent {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export default class Popover extends PureComponent {
|
||||
export default class StatusVisibilityPopover extends PureComponent {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import PopoverLayout from './popover_layout'
|
||||
import Text from '../text'
|
||||
|
||||
export default class UserInfoPopover extends PureComponent {
|
||||
render() {
|
||||
return (
|
||||
<PopoverLayout>
|
||||
<Text>testing</Text>
|
||||
</PopoverLayout>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
export default class Popover extends PureComponent {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{ /* */ }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -45,7 +45,7 @@ const mapDispatchToProps = (dispatch) => ({
|
|||
dispatch(closeSidebar())
|
||||
},
|
||||
onOpenComposeModal() {
|
||||
dispatch(openModal('PRO_UPGRADE'))
|
||||
dispatch(openModal('COMPOSE'))
|
||||
},
|
||||
})
|
||||
|
||||
|
@ -98,6 +98,7 @@ class Sidebar extends ImmutablePureComponent {
|
|||
const { sidebarOpen, intl, account } = this.props
|
||||
const { moreOpen } = this.state
|
||||
|
||||
// : todo :
|
||||
if (!me || !account) return null
|
||||
|
||||
const acct = account.get('acct')
|
||||
|
@ -124,11 +125,6 @@ class Sidebar extends ImmutablePureComponent {
|
|||
to: '/notifications',
|
||||
count: 40,
|
||||
},
|
||||
{
|
||||
title: 'Bookmarks',
|
||||
icon: 'bookmarks',
|
||||
to: '/bookmarks',
|
||||
},
|
||||
{
|
||||
title: 'Groups',
|
||||
icon: 'group',
|
||||
|
@ -142,7 +138,8 @@ class Sidebar extends ImmutablePureComponent {
|
|||
{
|
||||
title: 'Chat',
|
||||
icon: 'chat',
|
||||
to: '/',
|
||||
to: '',
|
||||
// href: 'https://chat.gab.com',
|
||||
},
|
||||
{
|
||||
title: 'More',
|
||||
|
@ -175,22 +172,26 @@ class Sidebar extends ImmutablePureComponent {
|
|||
{
|
||||
title: 'Apps',
|
||||
icon: 'apps',
|
||||
to: '/',
|
||||
to: '',
|
||||
// href: 'https://apps.gab.com',
|
||||
},
|
||||
{
|
||||
title: 'Shop',
|
||||
icon: 'shop',
|
||||
to: '/',
|
||||
to: '',
|
||||
// href: 'https://shop.dissenter.com',
|
||||
},
|
||||
{
|
||||
title: 'Trends',
|
||||
icon: 'trends',
|
||||
to: '/',
|
||||
to: '',
|
||||
// href: 'https://trends.gab.com',
|
||||
},
|
||||
{
|
||||
title: 'Dissenter',
|
||||
icon: 'dissenter',
|
||||
to: '/',
|
||||
to: '',
|
||||
// href: 'https://dissenter.com',
|
||||
},
|
||||
]
|
||||
|
||||
|
|
|
@ -15,7 +15,8 @@ const mapStateToProps = state => {
|
|||
}
|
||||
}
|
||||
|
||||
export default @connect(mapStateToProps)
|
||||
export default
|
||||
@connect(mapStateToProps)
|
||||
class SidebarHeader extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { Fragment } from 'react'
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { injectIntl, FormattedMessage } from 'react-intl';
|
||||
import { injectIntl, defineMessages } from 'react-intl';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { HotKeys } from 'react-hotkeys';
|
||||
import classNames from 'classnames';
|
||||
import classNames from 'classnames/bind'
|
||||
import { displayMedia } from '../../initial_state';
|
||||
import Card from '../../features/status/components/card';
|
||||
import { MediaGallery, Video } from '../../features/ui/util/async-components';
|
||||
|
@ -18,11 +18,14 @@ import Block from '../block';
|
|||
import Icon from '../icon';
|
||||
import Poll from '../poll';
|
||||
import StatusHeader from '../status_header'
|
||||
import Text from '../text'
|
||||
|
||||
// We use the component (and not the container) since we do not want
|
||||
// to use the progress bar to show download progress
|
||||
import Bundle from '../../features/ui/util/bundle';
|
||||
|
||||
const cx = classNames.bind(_s)
|
||||
|
||||
export const textForScreenReader = (intl, status, rebloggedByText = false) => {
|
||||
const displayName = status.getIn(['account', 'display_name']);
|
||||
|
||||
|
@ -52,6 +55,12 @@ export const defaultMediaVisibility = status => {
|
|||
return (displayMedia !== 'hide_all' && !status.get('sensitive')) || displayMedia === 'show_all';
|
||||
};
|
||||
|
||||
const messages = defineMessages({
|
||||
filtered: { id: 'status.filtered', defaultMessage: 'Filtered' },
|
||||
promoted: { id:'status.promoted', defaultMessage: 'Promoted gab' },
|
||||
pinned: { id: 'status.pinned', defaultMessage: 'Pinned gab' },
|
||||
})
|
||||
|
||||
export default
|
||||
@injectIntl
|
||||
class Status extends ImmutablePureComponent {
|
||||
|
@ -293,7 +302,7 @@ class Status extends ImmutablePureComponent {
|
|||
return (
|
||||
<HotKeys handlers={minHandlers}>
|
||||
<div className='status__wrapper status__wrapper--filtered focusable' tabIndex='0' ref={this.handleRef}>
|
||||
<FormattedMessage id='status.filtered' defaultMessage='Filtered' />
|
||||
<Text>{intl.formatMessage(messages.filtered)}</Text>
|
||||
</div>
|
||||
</HotKeys>
|
||||
);
|
||||
|
@ -302,17 +311,24 @@ class Status extends ImmutablePureComponent {
|
|||
if (promoted) {
|
||||
prepend = (
|
||||
<button className='status__prepend status__prepend--promoted' onClick={this.handleOpenProUpgradeModal}>
|
||||
<div className='status__prepend-icon-wrapper'><Icon id='star' className='status__prepend-icon' fixedWidth /></div>
|
||||
<FormattedMessage id='status.promoted' defaultMessage='Promoted gab' />
|
||||
<div className='status__prepend-icon-wrapper'>
|
||||
<Icon id='star' className='status__prepend-icon' fixedWidth />
|
||||
</div>
|
||||
<Text>{intl.formatMessage(messages.promoted)}</Text>
|
||||
</button>
|
||||
);
|
||||
} else if (featured) {
|
||||
prepend = (
|
||||
<div className='status__prepend'>
|
||||
<div className='status__prepend-icon-wrapper'>
|
||||
<Icon id='thumb-tack' className='status__prepend-icon' fixedWidth />
|
||||
</div>
|
||||
<FormattedMessage id='status.pinned' defaultMessage='Pinned gab' />
|
||||
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter, _s.borderBottom1PX, _s.borderColorSecondary, _s.paddingVertical5PX, _s.paddingHorizontal15PX].join(' ')}>
|
||||
<Icon
|
||||
id='thumb-tack'
|
||||
width='12px'
|
||||
height='12px'
|
||||
className={_s.fillcolorSecondary}
|
||||
/>
|
||||
<Text size='small' color='secondary' className={_s.marginLeft5PX}>
|
||||
{intl.formatMessage(messages.pinned)}
|
||||
</Text>
|
||||
</div>
|
||||
);
|
||||
} else if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') {
|
||||
|
@ -323,7 +339,7 @@ class Status extends ImmutablePureComponent {
|
|||
<div className='status__prepend-icon-wrapper'>
|
||||
<Icon id='retweet' className='status__prepend-icon' fixedWidth />
|
||||
</div>
|
||||
<FormattedMessage
|
||||
{/*<FormattedMessage
|
||||
id='status.reblogged_by'
|
||||
defaultMessage='{name} reposted'
|
||||
values={{
|
||||
|
@ -335,7 +351,7 @@ class Status extends ImmutablePureComponent {
|
|||
</NavLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
/> */ }
|
||||
</div>
|
||||
);
|
||||
|
||||
|
@ -423,10 +439,18 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
const statusUrl = `/${status.getIn(['account', 'acct'])}/posts/${status.get('id')}`;
|
||||
|
||||
const containerClasses = cx({
|
||||
default: 1,
|
||||
marginBottom15PX: 1,
|
||||
paddingBottom15PX: featured,
|
||||
borderBottom1PX: featured,
|
||||
borderColorSecondary: featured,
|
||||
})
|
||||
|
||||
return (
|
||||
<HotKeys handlers={handlers}>
|
||||
<div
|
||||
className={[_s.default, _s.marginBottom15PX].join(' ')}
|
||||
className={containerClasses}
|
||||
tabIndex={this.props.muted ? null : 0}
|
||||
data-featured={featured ? 'true' : null}
|
||||
aria-label={textForScreenReader(intl, status, rebloggedByText)}
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import { defineMessages, injectIntl } from 'react-intl'
|
||||
import classNames from 'classnames/bind'
|
||||
import { Link } from 'react-router-dom';
|
||||
import { openModal } from '../../actions/modal';
|
||||
import { me, isStaff } from '../../initial_state';
|
||||
import Dropdown from '../dropdown_menu'
|
||||
import ComposeFormContainer from '../../features/compose/containers/compose_form_container';
|
||||
import Icon from '../icon';
|
||||
import { openModal } from '../../actions/modal'
|
||||
import { me, isStaff } from '../../initial_state'
|
||||
import ComposeFormContainer from '../../features/compose/containers/compose_form_container'
|
||||
import Icon from '../icon'
|
||||
import StatusActionBarItem from '../status_action_bar_item'
|
||||
|
||||
const messages = defineMessages({
|
||||
delete: { id: 'status.delete', defaultMessage: 'Delete' },
|
||||
|
@ -40,64 +39,16 @@ const messages = defineMessages({
|
|||
copy: { id: 'status.copy', defaultMessage: 'Copy link to status' },
|
||||
group_remove_account: { id: 'status.remove_account_from_group', defaultMessage: 'Remove account from group' },
|
||||
group_remove_post: { id: 'status.remove_post_from_group', defaultMessage: 'Remove status from group' },
|
||||
});
|
||||
})
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
onOpenUnauthorizedModal() {
|
||||
dispatch(openModal('UNAUTHORIZED'));
|
||||
dispatch(openModal('UNAUTHORIZED'))
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
const cx = classNames.bind(_s)
|
||||
|
||||
class StatusActionBarItem extends PureComponent {
|
||||
static propTypes = {
|
||||
title: PropTypes.string.isRequired,
|
||||
onClick: PropTypes.func.isRequired,
|
||||
icon: PropTypes.string.isRequired,
|
||||
active: PropTypes.bool,
|
||||
disabled: PropTypes.bool,
|
||||
}
|
||||
|
||||
render() {
|
||||
const { title, onClick, icon, active, disabled } = this.props
|
||||
|
||||
const btnClasses = cx({
|
||||
default: 1,
|
||||
text: 1,
|
||||
fontSize13PX: 1,
|
||||
fontWeightMedium: 1,
|
||||
cursorPointer: 1,
|
||||
displayFlex: 1,
|
||||
justifyContentCenter: 1,
|
||||
flexRow: 1,
|
||||
alignItemsCenter: 1,
|
||||
paddingVertical10PX: 1,
|
||||
paddingHorizontal10PX: 1,
|
||||
width100PC: 1,
|
||||
radiusSmall: 1,
|
||||
outlineFocusBrand: 1,
|
||||
backgroundTransparent: 1,
|
||||
backgroundSubtle_onHover: 1,
|
||||
colorSecondary: 1,
|
||||
})
|
||||
|
||||
return (
|
||||
<div className={[_s.default, _s.flexGrow1, _s.paddingHorizontal10PX].join(' ')}>
|
||||
<button
|
||||
className={btnClasses}
|
||||
onClick={onClick}
|
||||
active={active}
|
||||
disabled={disabled}
|
||||
>
|
||||
<Icon width='16px' height='16px' id={icon} className={[_s.default, _s.marginRight10PX, _s.fillcolorSecondary].join(' ')} />
|
||||
{title}
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default
|
||||
@connect(null, mapDispatchToProps)
|
||||
@injectIntl
|
||||
|
@ -105,7 +56,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
status: ImmutablePropTypes.map.isRequired,
|
||||
|
@ -125,7 +76,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
withDismiss: PropTypes.bool,
|
||||
withGroupAdmin: PropTypes.bool,
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
// Avoid checking props that are functions (and whose equality will always
|
||||
// evaluate to false. See react-immutable-pure-component for usage.
|
||||
|
@ -136,53 +87,53 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
|
||||
handleReplyClick = () => {
|
||||
if (me) {
|
||||
this.props.onReply(this.props.status, this.context.router.history);
|
||||
this.props.onReply(this.props.status, this.context.router.history)
|
||||
} else {
|
||||
this.props.onOpenUnauthorizedModal();
|
||||
this.props.onOpenUnauthorizedModal()
|
||||
}
|
||||
}
|
||||
|
||||
handleQuoteClick = () => {
|
||||
if (me) {
|
||||
this.props.onQuote(this.props.status, this.context.router.history);
|
||||
this.props.onQuote(this.props.status, this.context.router.history)
|
||||
} else {
|
||||
this.props.onOpenUnauthorizedModal();
|
||||
this.props.onOpenUnauthorizedModal()
|
||||
}
|
||||
}
|
||||
|
||||
handleFavouriteClick = () => {
|
||||
if (me) {
|
||||
this.props.onFavourite(this.props.status);
|
||||
this.props.onFavourite(this.props.status)
|
||||
} else {
|
||||
this.props.onOpenUnauthorizedModal();
|
||||
this.props.onOpenUnauthorizedModal()
|
||||
}
|
||||
}
|
||||
|
||||
handleReblogClick = e => {
|
||||
if (me) {
|
||||
this.props.onReblog(this.props.status, e);
|
||||
this.props.onReblog(this.props.status, e)
|
||||
} else {
|
||||
this.props.onOpenUnauthorizedModal();
|
||||
this.props.onOpenUnauthorizedModal()
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { status, intl: { formatMessage } } = this.props;
|
||||
render() {
|
||||
const { status, intl: { formatMessage } } = this.props
|
||||
|
||||
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
|
||||
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'))
|
||||
|
||||
const replyCount = status.get('replies_count');
|
||||
const replyIcon = (status.get('in_reply_to_id', null) === null) ? 'reply' : 'reply-all';
|
||||
const replyTitle = (status.get('in_reply_to_id', null) === null) ? formatMessage(messages.reply) : formatMessage(messages.replyAll);
|
||||
const replyCount = status.get('replies_count')
|
||||
const replyIcon = (status.get('in_reply_to_id', null) === null) ? 'reply' : 'reply-all'
|
||||
const replyTitle = (status.get('in_reply_to_id', null) === null) ? formatMessage(messages.reply) : formatMessage(messages.replyAll)
|
||||
|
||||
const reblogCount = status.get('reblogs_count');
|
||||
const reblogTitle = !publicStatus ? formatMessage(messages.cannot_reblog) : formatMessage(messages.reblog);
|
||||
const reblogCount = status.get('reblogs_count')
|
||||
const reblogTitle = !publicStatus ? formatMessage(messages.cannot_reblog) : formatMessage(messages.reblog)
|
||||
|
||||
const favoriteCount = status.get('favourites_count');
|
||||
const favoriteCount = status.get('favourites_count')
|
||||
|
||||
const shareButton = ('share' in navigator) && status.get('visibility') === 'public' && (
|
||||
<IconButton className='status-action-bar-button' title={formatMessage(messages.share)} icon='share-alt' onClick={this.handleShareClick} />
|
||||
);
|
||||
)
|
||||
|
||||
const items = [
|
||||
{
|
||||
|
@ -248,19 +199,19 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
{
|
||||
hasInteractions &&
|
||||
<div className={[_s.default, _s.flexRow, _s.paddingHorizontal5PX].join(' ')}>
|
||||
{ favoriteCount > 0 &&
|
||||
{favoriteCount > 0 &&
|
||||
<button className={interactionBtnClasses}>
|
||||
{favoriteCount}
|
||||
Likes
|
||||
</button>
|
||||
}
|
||||
{ replyCount > 0 &&
|
||||
{replyCount > 0 &&
|
||||
<button className={interactionBtnClasses}>
|
||||
{replyCount}
|
||||
Comments
|
||||
</button>
|
||||
}
|
||||
{ reblogCount > 0 &&
|
||||
{reblogCount > 0 &&
|
||||
<button className={interactionBtnClasses}>
|
||||
{reblogCount}
|
||||
Reposts
|
||||
|
@ -277,11 +228,11 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
}
|
||||
</div>
|
||||
</div>
|
||||
<div className='status-action-bar__comment'>
|
||||
{/*<ComposeFormContainer shouldCondense statusId={status.get('id')} />*/}
|
||||
<div className={[_s.default, _s.borderTop1PX, _s.borderColorSecondary, _s.paddingTop10PX, _s.marginBottom10PX].join(' ')}>
|
||||
{ /* <ComposeFormContainer statusId={status.get('id')} shouldCondense /> */ }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
import classNames from 'classnames/bind'
|
||||
import Icon from './icon'
|
||||
|
||||
const cx = classNames.bind(_s)
|
||||
|
||||
export default class StatusActionBarItem extends PureComponent {
|
||||
static propTypes = {
|
||||
title: PropTypes.string.isRequired,
|
||||
onClick: PropTypes.func.isRequired,
|
||||
icon: PropTypes.string.isRequired,
|
||||
active: PropTypes.bool,
|
||||
disabled: PropTypes.bool,
|
||||
}
|
||||
|
||||
render() {
|
||||
const { title, onClick, icon, active, disabled } = this.props
|
||||
|
||||
const btnClasses = cx({
|
||||
default: 1,
|
||||
text: 1,
|
||||
fontSize13PX: 1,
|
||||
fontWeightMedium: 1,
|
||||
cursorPointer: 1,
|
||||
displayFlex: 1,
|
||||
justifyContentCenter: 1,
|
||||
flexRow: 1,
|
||||
alignItemsCenter: 1,
|
||||
paddingVertical10PX: 1,
|
||||
paddingHorizontal10PX: 1,
|
||||
width100PC: 1,
|
||||
radiusSmall: 1,
|
||||
outlineFocusBrand: 1,
|
||||
backgroundTransparent: 1,
|
||||
backgroundSubtle_onHover: 1,
|
||||
colorSecondary: 1,
|
||||
})
|
||||
|
||||
return (
|
||||
<div className={[_s.default, _s.flexGrow1, _s.paddingHorizontal10PX].join(' ')}>
|
||||
<button
|
||||
className={btnClasses}
|
||||
onClick={onClick}
|
||||
active={active}
|
||||
disabled={disabled}
|
||||
>
|
||||
<Icon width='16px' height='16px' id={icon} className={[_s.default, _s.marginRight10PX, _s.fillcolorSecondary].join(' ')} />
|
||||
{title}
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -3,16 +3,17 @@ import TabBarItem from './tab_bar_item'
|
|||
export default class TabBar extends PureComponent {
|
||||
static propTypes = {
|
||||
tabs: PropTypes.array,
|
||||
large: PropTypes.bool,
|
||||
}
|
||||
|
||||
render() {
|
||||
const { tabs } = this.props
|
||||
const { tabs, large } = this.props
|
||||
|
||||
return (
|
||||
<div className={[_s.default, _s.height53PX, _s.paddingHorizontal5PX, _s.flexRow].join(' ')}>
|
||||
{
|
||||
{ !!tabs &&
|
||||
tabs.map((tab, i) => (
|
||||
<TabBarItem key={`tab-bar-item-${i}`} {...tab} />
|
||||
<TabBarItem key={`tab-bar-item-${i}`} {...tab} large={large} />
|
||||
))
|
||||
}
|
||||
</div>
|
||||
|
|
|
@ -11,6 +11,7 @@ class TabBarItem extends PureComponent {
|
|||
location: PropTypes.object.isRequired,
|
||||
title: PropTypes.string,
|
||||
to: PropTypes.string,
|
||||
large: PropTypes.bool,
|
||||
}
|
||||
|
||||
state = {
|
||||
|
@ -30,7 +31,7 @@ class TabBarItem extends PureComponent {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { title, to, location } = this.props
|
||||
const { title, to, location, large } = this.props
|
||||
const { active } = this.state
|
||||
|
||||
const isCurrent = active === -1 ? to === location.pathname : active
|
||||
|
@ -43,23 +44,40 @@ class TabBarItem extends PureComponent {
|
|||
displayFlex: 1,
|
||||
alignItemsCenter: 1,
|
||||
justifyContentCenter: 1,
|
||||
paddingHorizontal10PX: 1,
|
||||
borderBottom2PX: 1,
|
||||
paddingVertical5PX: 1,
|
||||
borderColorTransparent: !isCurrent,
|
||||
borderColorBrand: isCurrent,
|
||||
marginRight5PX: large,
|
||||
})
|
||||
|
||||
const textParentClasses = cx({
|
||||
default: 1,
|
||||
height100PC: 1,
|
||||
alignItemsCenter: 1,
|
||||
justifyContentCenter: 1,
|
||||
radiusSmall: 1,
|
||||
paddingHorizontal10PX: !large,
|
||||
paddingHorizontal15PX: large,
|
||||
backgroundSubtle2Dark_onHover: !isCurrent,
|
||||
})
|
||||
|
||||
const textOptions = {
|
||||
size: 'small',
|
||||
color: isCurrent ? 'brand' : 'primary',
|
||||
weight: isCurrent ? 'bold' : 'normal',
|
||||
size: !!large ? 'normal' : 'small',
|
||||
color: isCurrent ? 'brand' : large ? 'secondary' : 'primary',
|
||||
weight: isCurrent ? 'bold' : large ? 'medium' : 'normal',
|
||||
className: cx({
|
||||
paddingHorizontal5PX: large,
|
||||
}),
|
||||
}
|
||||
|
||||
return (
|
||||
<NavLink to={to} className={containerClasses}>
|
||||
<Text {...textOptions}>
|
||||
{title}
|
||||
</Text>
|
||||
<span className={textParentClasses}>
|
||||
<Text {...textOptions}>
|
||||
{title}
|
||||
</Text>
|
||||
</span>
|
||||
</NavLink>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -44,9 +44,6 @@ class TimelineComposeBlock extends ImmutablePureComponent {
|
|||
</Heading>
|
||||
</div>
|
||||
<div className={[_s.default, _s.flexRow, _s.paddingVertical15PX, _s.paddingHorizontal15PX].join(' ')}>
|
||||
<div className={[_s.default, _s.marginRight10PX].join(' ')}>
|
||||
<Avatar account={account} size={46} />
|
||||
</div>
|
||||
<ComposeFormContainer {...rest} />
|
||||
</div>
|
||||
</Block>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import LoadingBar from 'react-redux-loading-bar';
|
||||
import LoadingBar from 'react-redux-loading-bar'
|
||||
|
||||
const mapStateToProps = (state, ownProps) => ({
|
||||
loading: state.get('loadingBar')[ownProps.scope || 'default'],
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(LoadingBar.WrappedComponent);
|
||||
export default connect(mapStateToProps)(LoadingBar.WrappedComponent)
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
import { closeModal } from '../actions/modal';
|
||||
import { cancelReplyCompose } from '../actions/compose';
|
||||
import ModalRoot from '../components/modal/modal_root';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
type: state.get('modal').modalType,
|
||||
props: state.get('modal').modalProps,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
onClose (optionalType) {
|
||||
if (optionalType === 'COMPOSE') {
|
||||
dispatch(cancelReplyCompose());
|
||||
}
|
||||
|
||||
dispatch(closeModal());
|
||||
},
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ModalRoot);
|
|
@ -1,25 +1,25 @@
|
|||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { load } from '../actions/status_revision_list';
|
||||
import StatusRevisionList from '../components/status_revision_list';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import { load } from '../actions/status_revision_list'
|
||||
import StatusRevisionList from '../components/status_revision_list'
|
||||
|
||||
class StatusRevisionListContainer extends ImmutablePureComponent {
|
||||
componentDidMount() {
|
||||
this.props.load(this.props.id);
|
||||
}
|
||||
componentDidMount() {
|
||||
this.props.load(this.props.id)
|
||||
}
|
||||
|
||||
render() {
|
||||
return <StatusRevisionList {...this.props} />;
|
||||
}
|
||||
render() {
|
||||
return <StatusRevisionList {...this.props} />
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
loading: state.getIn(['status_revision_list', 'loading']),
|
||||
error: state.getIn(['status_revision_list', 'error']),
|
||||
data: state.getIn(['status_revision_list', 'data']),
|
||||
});
|
||||
loading: state.getIn(['status_revision_list', 'loading']),
|
||||
error: state.getIn(['status_revision_list', 'error']),
|
||||
data: state.getIn(['status_revision_list', 'data']),
|
||||
})
|
||||
|
||||
const mapDispatchToProps = {
|
||||
load
|
||||
};
|
||||
load
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(StatusRevisionListContainer);
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(StatusRevisionListContainer)
|
|
@ -8,7 +8,7 @@ import { hydrateStore } from '../actions/store';
|
|||
import initialState from '../initial_state';
|
||||
import PublicTimeline from '../features/standalone/public_timeline';
|
||||
import HashtagTimeline from '../features/standalone/hashtag_timeline';
|
||||
import ModalContainer from './modal_container';
|
||||
import ModalRoot from '../components/modal/modal_root'
|
||||
|
||||
const { localeData, messages } = getLocale();
|
||||
addLocaleData(localeData);
|
||||
|
|
|
@ -8,7 +8,6 @@ import Button from '../../../components/button';
|
|||
import { autoPlayGif, me, isStaff } from '../../../initial_state';
|
||||
import Avatar from '../../../components/avatar';
|
||||
import { shortNumberFormat } from '../../../utils/numbers';
|
||||
import Dropdown from '../../../components/dropdown_menu'
|
||||
import ProfileInfoPanel from './profile_info_panel/profile_info_panel';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import { openModal } from '../../../../actions/modal';
|
||||
import Dropdown from '../../../../components/dropdown_menu'
|
||||
import { meUsername } from '../../../../initial_state';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
|
|
@ -5,27 +5,30 @@ export default class CharacterCounter extends PureComponent {
|
|||
static propTypes = {
|
||||
text: PropTypes.string.isRequired,
|
||||
max: PropTypes.number.isRequired,
|
||||
small: PropTypes.bool,
|
||||
}
|
||||
|
||||
render () {
|
||||
const radius = 12
|
||||
const { text, max, small } = this.props
|
||||
const actualRadius = small ? '10' : '16'
|
||||
const radius = small ? 8 : 12
|
||||
const circumference = 2 * Math.PI * radius
|
||||
const diff = length(this.props.text) / this.props.max
|
||||
const diff = length(text) / max
|
||||
const dashoffset = circumference * (1 - diff)
|
||||
|
||||
return (
|
||||
<div className={[_s.default, _s.marginRight10PX, _s.justifyContentCenter, _s.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" strokeWidth="2" />
|
||||
<svg width={actualRadius * 2} height={actualRadius * 2} viewBox={`0 0 ${actualRadius * 2} ${actualRadius * 2}`}>
|
||||
<circle fill='none' cx={actualRadius} cy={actualRadius} r={radius} fill="none" stroke="#e6e6e6" strokeWidth="2" />
|
||||
<circle style={{
|
||||
// transform: 'rotate(-90deg)',
|
||||
strokeDashoffset: dashoffset,
|
||||
strokeDasharray: circumference,
|
||||
}}
|
||||
fill='none'
|
||||
cx="16"
|
||||
cy="16"
|
||||
r="12"
|
||||
cx={actualRadius}
|
||||
cy={actualRadius}
|
||||
r={radius}
|
||||
strokeWidth="2"
|
||||
strokeLinecap='round'
|
||||
stroke='#21cf7a'
|
||||
|
|
|
@ -9,6 +9,7 @@ export default class ComposeExtraButton extends PureComponent {
|
|||
disabled: PropTypes.bool,
|
||||
onClick: PropTypes.func,
|
||||
icon: PropTypes.string,
|
||||
small: PropTypes.bool,
|
||||
}
|
||||
|
||||
state = {
|
||||
|
@ -24,18 +25,26 @@ export default class ComposeExtraButton extends PureComponent {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { title, disabled, onClick, icon, children } = this.props
|
||||
const { title, disabled, onClick, icon, children, small } = this.props
|
||||
const { hovering } = this.state
|
||||
|
||||
const containerClasses = cx({
|
||||
default: 1,
|
||||
marginRight10PX: !small,
|
||||
marginRight2PX: small,
|
||||
})
|
||||
|
||||
const btnClasses = cx({
|
||||
default: 1,
|
||||
circle: 1,
|
||||
flexRow: 1,
|
||||
paddingVertical10PX: 1,
|
||||
paddingHorizontal10PX: 1,
|
||||
cursorPointer: 1,
|
||||
backgroundSubtle: !hovering,
|
||||
backgroundSubtle2: hovering,
|
||||
paddingVertical10PX: !small,
|
||||
paddingHorizontal10PX: !small,
|
||||
paddingVertical5PX: small,
|
||||
paddingHorizontal5PX: small,
|
||||
})
|
||||
|
||||
const titleClasses = cx({
|
||||
|
@ -49,8 +58,10 @@ export default class ComposeExtraButton extends PureComponent {
|
|||
displayNone: !hovering,
|
||||
})
|
||||
|
||||
const iconSize = !!small ? '12px' : '18px'
|
||||
|
||||
return (
|
||||
<div className={[_s.default, _s.marginRight10PX].join(' ')}>
|
||||
<div className={containerClasses}>
|
||||
<button
|
||||
className={btnClasses}
|
||||
title={title}
|
||||
|
@ -59,10 +70,13 @@ export default class ComposeExtraButton extends PureComponent {
|
|||
onMouseEnter={() => this.handleOnMouseEnter()}
|
||||
onMouseLeave={() => this.handleOnMouseLeave()}
|
||||
>
|
||||
<Icon id={icon} width='18px' height='18px' className={_s.fillcolorSecondary} />
|
||||
<span className={titleClasses}>
|
||||
{title}
|
||||
</span>
|
||||
<Icon id={icon} width={iconSize} height={iconSize} className={_s.fillcolorSecondary} />
|
||||
{
|
||||
!small &&
|
||||
<span className={titleClasses}>
|
||||
{title}
|
||||
</span>
|
||||
}
|
||||
</button>
|
||||
{children}
|
||||
</div>
|
||||
|
|
|
@ -2,7 +2,7 @@ import { defineMessages, injectIntl } from 'react-intl';
|
|||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { length } from 'stringz';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import classNames from 'classnames';
|
||||
import classNames from 'classnames/bind'
|
||||
import CharacterCounter from '../character_counter';
|
||||
import UploadForm from '../upload_form';
|
||||
import ReplyIndicatorContainer from '../../containers/reply_indicator_container';
|
||||
|
@ -18,6 +18,7 @@ import SchedulePostDropdown from '../../components/schedule_post_dropdown';
|
|||
import QuotedStatusPreviewContainer from '../../containers/quoted_status_preview_container';
|
||||
import Icon from '../../../../components/icon';
|
||||
import Button from '../../../../components/button';
|
||||
import Avatar from '../../../../components/avatar'
|
||||
import { isMobile } from '../../../../utils/is_mobile';
|
||||
import { countableText } from '../../util/counter';
|
||||
|
||||
|
@ -32,6 +33,8 @@ const messages = defineMessages({
|
|||
schedulePost: { id: 'compose_form.schedule_post', defaultMessage: 'Schedule Post' }
|
||||
});
|
||||
|
||||
const cx = classNames.bind(_s)
|
||||
|
||||
export default
|
||||
@injectIntl
|
||||
class ComposeForm extends ImmutablePureComponent {
|
||||
|
@ -49,6 +52,8 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
edit: PropTypes.bool.isRequired,
|
||||
text: PropTypes.string.isRequired,
|
||||
suggestions: ImmutablePropTypes.list,
|
||||
account: ImmutablePropTypes.map.isRequired,
|
||||
status: ImmutablePropTypes.map,
|
||||
spoiler: PropTypes.bool,
|
||||
privacy: PropTypes.string,
|
||||
spoilerText: PropTypes.string,
|
||||
|
@ -164,7 +169,7 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
document.removeEventListener("click", this.handleClick, false);
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
componentDidUpdate(prevProps) {
|
||||
if (!this.autosuggestTextarea) return;
|
||||
|
||||
// This statement does several things:
|
||||
|
@ -176,13 +181,13 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
let selectionEnd, selectionStart;
|
||||
|
||||
if (this.props.preselectDate !== prevProps.preselectDate) {
|
||||
selectionEnd = this.props.text.length;
|
||||
selectionEnd = this.props.text.length;
|
||||
selectionStart = this.props.text.search(/\s/) + 1;
|
||||
} else if (typeof this.props.caretPosition === 'number') {
|
||||
selectionStart = this.props.caretPosition;
|
||||
selectionEnd = this.props.caretPosition;
|
||||
selectionEnd = this.props.caretPosition;
|
||||
} else {
|
||||
selectionEnd = this.props.text.length;
|
||||
selectionEnd = this.props.text.length;
|
||||
selectionStart = selectionEnd;
|
||||
}
|
||||
|
||||
|
@ -211,30 +216,65 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
this.props.onPickEmoji(position, data, needsSpace);
|
||||
}
|
||||
|
||||
render () {
|
||||
const { intl, onPaste, showSearch, anyMedia, shouldCondense, autoFocus, isModalOpen, quoteOfId, edit, scheduledAt } = this.props;
|
||||
render() {
|
||||
const {
|
||||
intl,
|
||||
account,
|
||||
onPaste,
|
||||
showSearch,
|
||||
anyMedia,
|
||||
shouldCondense,
|
||||
autoFocus,
|
||||
isModalOpen,
|
||||
quoteOfId,
|
||||
edit,
|
||||
scheduledAt
|
||||
} = this.props
|
||||
const condensed = shouldCondense && !this.props.text && !this.state.composeFocused;
|
||||
const disabled = this.props.isSubmitting;
|
||||
const text = [this.props.spoilerText, countableText(this.props.text)].join('');
|
||||
const text = [this.props.spoilerText, countableText(this.props.text)].join('');
|
||||
const disabledButton = disabled || this.props.isUploading || this.props.isChangingUpload || length(text) > maxPostCharacterCount || (text.length !== 0 && text.trim().length === 0 && !anyMedia);
|
||||
const shouldAutoFocus = autoFocus && !showSearch && !isMobile(window.innerWidth)
|
||||
|
||||
const composeClassNames = classNames({
|
||||
'compose-form': true,
|
||||
'condensed': condensed,
|
||||
});
|
||||
const containerClasses = cx({
|
||||
default: 1,
|
||||
flexGrow1: 1,
|
||||
flexRow: shouldCondense,
|
||||
radiusSmall: shouldCondense,
|
||||
backgroundSubtle: shouldCondense,
|
||||
paddingHorizontal5PX: shouldCondense,
|
||||
})
|
||||
|
||||
const actionsContainerClasses = cx({
|
||||
default: 1,
|
||||
flexRow: 1,
|
||||
alignItemsCenter: 1,
|
||||
marginTop10PX: !shouldCondense,
|
||||
})
|
||||
|
||||
const avatarContainerClasses = cx({
|
||||
default: 1,
|
||||
marginRight10PX: 1,
|
||||
marginTop5PX: shouldCondense,
|
||||
})
|
||||
|
||||
const avatarSize = shouldCondense ? 28 : 46
|
||||
|
||||
return (
|
||||
<div
|
||||
className={[_s.default, _s.flexGrow1].join(' ')}
|
||||
ref={this.setForm}
|
||||
onClick={this.handleClick}
|
||||
>
|
||||
{ /* <WarningContainer /> */ }
|
||||
<div className={[_s.default, _s.flexRow, _s.width100PC].join(' ')}>
|
||||
<div className={avatarContainerClasses}>
|
||||
<Avatar account={account} size={avatarSize} />
|
||||
</div>
|
||||
<div
|
||||
className={containerClasses}
|
||||
ref={this.setForm}
|
||||
onClick={this.handleClick}
|
||||
>
|
||||
{ /* <WarningContainer /> */}
|
||||
|
||||
{ /* !shouldCondense && <ReplyIndicatorContainer /> */ }
|
||||
{ /* !shouldCondense && <ReplyIndicatorContainer /> */}
|
||||
|
||||
{ /*
|
||||
{ /*
|
||||
<div className={`spoiler-input ${this.props.spoiler ? 'spoiler-input--visible' : ''}`}>
|
||||
<AutosuggestTextbox
|
||||
placeholder={intl.formatMessage(messages.spoiler_placeholder)}
|
||||
|
@ -254,63 +294,68 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
</div>
|
||||
*/ }
|
||||
|
||||
{ /*
|
||||
{ /*
|
||||
<div className='emoji-picker-wrapper'>
|
||||
<EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} />
|
||||
</div> */ }
|
||||
|
||||
<AutosuggestTextbox
|
||||
ref={(isModalOpen && shouldCondense) ? null : this.setAutosuggestTextarea}
|
||||
placeholder={intl.formatMessage(messages.placeholder)}
|
||||
disabled={disabled}
|
||||
value={this.props.text}
|
||||
onChange={this.handleChange}
|
||||
suggestions={this.props.suggestions}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
onFocus={this.handleComposeFocus}
|
||||
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
|
||||
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
|
||||
onSuggestionSelected={this.onSuggestionSelected}
|
||||
onPaste={onPaste}
|
||||
autoFocus={shouldAutoFocus}
|
||||
textarea
|
||||
>
|
||||
{ /*
|
||||
<AutosuggestTextbox
|
||||
ref={(isModalOpen && shouldCondense) ? null : this.setAutosuggestTextarea}
|
||||
placeholder={intl.formatMessage(messages.placeholder)}
|
||||
disabled={disabled}
|
||||
value={this.props.text}
|
||||
onChange={this.handleChange}
|
||||
suggestions={this.props.suggestions}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
onFocus={this.handleComposeFocus}
|
||||
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
|
||||
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
|
||||
onSuggestionSelected={this.onSuggestionSelected}
|
||||
onPaste={onPaste}
|
||||
autoFocus={shouldAutoFocus}
|
||||
small={shouldCondense}
|
||||
textarea
|
||||
>
|
||||
{ /*
|
||||
!condensed &&
|
||||
<div className='compose-form__modifiers'>
|
||||
<UploadForm />
|
||||
{!edit && <PollFormContainer />}
|
||||
</div>
|
||||
*/ }
|
||||
</AutosuggestTextbox>
|
||||
</AutosuggestTextbox>
|
||||
|
||||
{ /* quoteOfId && <QuotedStatusPreviewContainer id={quoteOfId} /> */ }
|
||||
{ /* quoteOfId && <QuotedStatusPreviewContainer id={quoteOfId} /> */}
|
||||
|
||||
{
|
||||
/* !condensed && */
|
||||
<div className={[_s.default, _s.flexRow, _s.marginTop10PX].join(' ')}>
|
||||
<div className={actionsContainerClasses}>
|
||||
<div className={[_s.default, _s.flexRow, _s.marginRightAuto].join(' ')}>
|
||||
<UploadButton />
|
||||
<UploadButton small={shouldCondense} />
|
||||
{
|
||||
!edit && <PollButton />
|
||||
!edit && <PollButton small={shouldCondense} />
|
||||
}
|
||||
<PrivacyDropdown />
|
||||
<SpoilerButton />
|
||||
<SchedulePostDropdown position={isModalOpen ? 'top' : undefined} />
|
||||
{
|
||||
!shouldCondense &&
|
||||
<PrivacyDropdown />
|
||||
}
|
||||
<SpoilerButton small={shouldCondense} />
|
||||
<SchedulePostDropdown small={shouldCondense} position={isModalOpen ? 'top' : undefined} />
|
||||
</div>
|
||||
<CharacterCounter max={maxPostCharacterCount} text={text} />
|
||||
<Button
|
||||
className={[_s.fontSize15PX, _s.paddingHorizontal15PX].join(' ')}
|
||||
onClick={this.handleSubmit}
|
||||
disabled={disabledButton}
|
||||
>
|
||||
{intl.formatMessage(scheduledAt ? messages.schedulePost : messages.publish)}
|
||||
</Button>
|
||||
<CharacterCounter max={maxPostCharacterCount} text={text} small={shouldCondense} />
|
||||
{
|
||||
!shouldCondense &&
|
||||
<Button
|
||||
className={[_s.fontSize15PX, _s.paddingHorizontal15PX].join(' ')}
|
||||
onClick={this.handleSubmit}
|
||||
disabled={disabledButton}
|
||||
>
|
||||
{intl.formatMessage(scheduledAt ? messages.schedulePost : messages.publish)}
|
||||
</Button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ class PollButton extends PureComponent {
|
|||
active: PropTypes.bool,
|
||||
onClick: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
small: PropTypes.bool,
|
||||
};
|
||||
|
||||
handleClick = () => {
|
||||
|
@ -45,7 +46,7 @@ class PollButton extends PureComponent {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { intl, active, unavailable, disabled } = this.props;
|
||||
const { intl, active, unavailable, disabled, small } = this.props;
|
||||
|
||||
if (unavailable) return null;
|
||||
|
||||
|
@ -55,6 +56,7 @@ class PollButton extends PureComponent {
|
|||
disabled={disabled}
|
||||
onClick={this.handleClick}
|
||||
icon='poll'
|
||||
small={small}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@ class SchedulePostDropdown extends PureComponent {
|
|||
isPro: PropTypes.bool,
|
||||
onOpenProUpgradeModal: PropTypes.func.isRequired,
|
||||
position: PropTypes.string,
|
||||
small: PropTypes.bool,
|
||||
}
|
||||
|
||||
handleToggle = () => {
|
||||
|
@ -73,7 +74,7 @@ class SchedulePostDropdown extends PureComponent {
|
|||
}
|
||||
|
||||
render () {
|
||||
const { intl, date, isPro, position } = this.props
|
||||
const { intl, date, isPro, position, small } = this.props
|
||||
|
||||
const open = !!date
|
||||
const datePickerDisabled = !isPro
|
||||
|
@ -87,6 +88,7 @@ class SchedulePostDropdown extends PureComponent {
|
|||
icon='calendar'
|
||||
title={intl.formatMessage(messages.schedule_status)}
|
||||
onClick={this.handleToggle}
|
||||
small={small}
|
||||
/>
|
||||
</div>
|
||||
{
|
||||
|
|
|
@ -28,6 +28,7 @@ class SpoilerButton extends PureComponent {
|
|||
static propTypes = {
|
||||
active: PropTypes.bool,
|
||||
intl: PropTypes.map,
|
||||
small: PropTypes.bool,
|
||||
}
|
||||
|
||||
handleClick = (e) => {
|
||||
|
@ -36,13 +37,14 @@ class SpoilerButton extends PureComponent {
|
|||
}
|
||||
|
||||
render () {
|
||||
const { active, intl } = this.props
|
||||
const { active, intl, small } = this.props
|
||||
|
||||
return (
|
||||
<ComposeExtraButton
|
||||
title={intl.formatMessage(messages.title)}
|
||||
icon='warning'
|
||||
onClick={this.handleClick}
|
||||
small={small}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ class UploadButton extends ImmutablePureComponent {
|
|||
resetFileKey: PropTypes.number,
|
||||
acceptContentTypes: ImmutablePropTypes.listOf(PropTypes.string).isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
small: PropTypes.bool,
|
||||
}
|
||||
|
||||
handleChange = (e) => {
|
||||
|
@ -56,7 +57,7 @@ class UploadButton extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { intl, resetFileKey, unavailable, disabled, acceptContentTypes } = this.props
|
||||
const { intl, resetFileKey, unavailable, disabled, acceptContentTypes, small } = this.props
|
||||
|
||||
if (unavailable) return null
|
||||
|
||||
|
@ -66,6 +67,7 @@ class UploadButton extends ImmutablePureComponent {
|
|||
disabled={disabled}
|
||||
onClick={this.handleClick}
|
||||
icon='media'
|
||||
small={small}
|
||||
>
|
||||
<label>
|
||||
<span className={_s.displayNone}>{intl.formatMessage(messages.upload)}</span>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import ComposeForm from '../components/compose_form';
|
||||
import ComposeForm from '../components/compose_form'
|
||||
import {
|
||||
changeCompose,
|
||||
submitCompose,
|
||||
|
@ -9,66 +9,74 @@ import {
|
|||
insertEmojiCompose,
|
||||
uploadCompose,
|
||||
changeScheduledAt,
|
||||
} from '../../../actions/compose';
|
||||
} from '../../../actions/compose'
|
||||
import { me } from '../../../initial_state'
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
edit: state.getIn(['compose', 'id']) !== null,
|
||||
text: state.getIn(['compose', 'text']),
|
||||
suggestions: state.getIn(['compose', 'suggestions']),
|
||||
spoiler: state.getIn(['compose', 'spoiler']),
|
||||
spoilerText: state.getIn(['compose', 'spoiler_text']),
|
||||
privacy: state.getIn(['compose', 'privacy']),
|
||||
focusDate: state.getIn(['compose', 'focusDate']),
|
||||
caretPosition: state.getIn(['compose', 'caretPosition']),
|
||||
preselectDate: state.getIn(['compose', 'preselectDate']),
|
||||
isSubmitting: state.getIn(['compose', 'is_submitting']),
|
||||
isChangingUpload: state.getIn(['compose', 'is_changing_upload']),
|
||||
isUploading: state.getIn(['compose', 'is_uploading']),
|
||||
showSearch: state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']),
|
||||
anyMedia: state.getIn(['compose', 'media_attachments']).size > 0,
|
||||
isModalOpen: state.get('modal').modalType === 'COMPOSE',
|
||||
quoteOfId: state.getIn(['compose', 'quote_of_id']),
|
||||
scheduledAt: state.getIn(['compose', 'scheduled_at']),
|
||||
});
|
||||
const mapStateToProps = (state, { status }) => {
|
||||
|
||||
// : todo :
|
||||
//everything needs to be in relation to if there's a status or not
|
||||
|
||||
return {
|
||||
edit: state.getIn(['compose', 'id']) !== null,
|
||||
text: state.getIn(['compose', 'text']),
|
||||
suggestions: state.getIn(['compose', 'suggestions']),
|
||||
spoiler: state.getIn(['compose', 'spoiler']),
|
||||
spoilerText: state.getIn(['compose', 'spoiler_text']),
|
||||
privacy: state.getIn(['compose', 'privacy']),
|
||||
focusDate: state.getIn(['compose', 'focusDate']),
|
||||
caretPosition: state.getIn(['compose', 'caretPosition']),
|
||||
preselectDate: state.getIn(['compose', 'preselectDate']),
|
||||
isSubmitting: state.getIn(['compose', 'is_submitting']),
|
||||
isChangingUpload: state.getIn(['compose', 'is_changing_upload']),
|
||||
isUploading: state.getIn(['compose', 'is_uploading']),
|
||||
showSearch: state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']),
|
||||
anyMedia: state.getIn(['compose', 'media_attachments']).size > 0,
|
||||
isModalOpen: state.get('modal').modalType === 'COMPOSE',
|
||||
quoteOfId: state.getIn(['compose', 'quote_of_id']),
|
||||
scheduledAt: state.getIn(['compose', 'scheduled_at']),
|
||||
account: state.getIn(['accounts', me]),
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
|
||||
onChange (text) {
|
||||
dispatch(changeCompose(text));
|
||||
onChange(text) {
|
||||
dispatch(changeCompose(text))
|
||||
},
|
||||
|
||||
onSubmit (router, group) {
|
||||
dispatch(submitCompose(router, group));
|
||||
onSubmit(router, group) {
|
||||
dispatch(submitCompose(router, group))
|
||||
},
|
||||
|
||||
onClearSuggestions () {
|
||||
dispatch(clearComposeSuggestions());
|
||||
onClearSuggestions() {
|
||||
dispatch(clearComposeSuggestions())
|
||||
},
|
||||
|
||||
onFetchSuggestions (token) {
|
||||
dispatch(fetchComposeSuggestions(token));
|
||||
onFetchSuggestions(token) {
|
||||
dispatch(fetchComposeSuggestions(token))
|
||||
},
|
||||
|
||||
onSuggestionSelected (position, token, suggestion, path) {
|
||||
dispatch(selectComposeSuggestion(position, token, suggestion, path));
|
||||
onSuggestionSelected(position, token, suggestion, path) {
|
||||
dispatch(selectComposeSuggestion(position, token, suggestion, path))
|
||||
},
|
||||
|
||||
onChangeSpoilerText (checked) {
|
||||
dispatch(changeComposeSpoilerText(checked));
|
||||
onChangeSpoilerText(checked) {
|
||||
dispatch(changeComposeSpoilerText(checked))
|
||||
},
|
||||
|
||||
onPaste (files) {
|
||||
dispatch(uploadCompose(files));
|
||||
onPaste(files) {
|
||||
dispatch(uploadCompose(files))
|
||||
},
|
||||
|
||||
onPickEmoji (position, data, needsSpace) {
|
||||
dispatch(insertEmojiCompose(position, data, needsSpace));
|
||||
onPickEmoji(position, data, needsSpace) {
|
||||
dispatch(insertEmojiCompose(position, data, needsSpace))
|
||||
},
|
||||
|
||||
setScheduledAt (date) {
|
||||
dispatch(changeScheduledAt(date));
|
||||
setScheduledAt(date) {
|
||||
dispatch(changeScheduledAt(date))
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
function mergeProps(stateProps, dispatchProps, ownProps) {
|
||||
return Object.assign({}, ownProps, {
|
||||
|
@ -77,4 +85,4 @@ function mergeProps(stateProps, dispatchProps, ownProps) {
|
|||
})
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(ComposeForm);
|
||||
export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(ComposeForm)
|
||||
|
|
|
@ -2,7 +2,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
|||
import { FormattedMessage } from 'react-intl';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { debounce } from 'lodash';
|
||||
import { fetchFavouritedStatuses, expandFavouritedStatuses } from '../../actions/favourites';
|
||||
import { fetchFavoritedStatuses, expandFavoritedStatuses } from '../../actions/favourites';
|
||||
import { meUsername } from '../../initial_state';
|
||||
import StatusList from '../../components/status_list';
|
||||
import ColumnIndicator from '../../components/column_indicator';
|
||||
|
@ -29,11 +29,11 @@ class Favourites extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
componentWillMount () {
|
||||
this.props.dispatch(fetchFavouritedStatuses());
|
||||
this.props.dispatch(fetchFavoritedStatuses());
|
||||
}
|
||||
|
||||
handleLoadMore = debounce(() => {
|
||||
this.props.dispatch(expandFavouritedStatuses());
|
||||
this.props.dispatch(expandFavoritedStatuses());
|
||||
}, 300, { leading: true })
|
||||
|
||||
render () {
|
||||
|
|
|
@ -10,7 +10,6 @@ import {
|
|||
import { FormattedMessage } from 'react-intl';
|
||||
import AccountContainer from '../../../containers/account_container';
|
||||
import ScrollableList from '../../../components/scrollable_list';
|
||||
import Dropdown from '../../../components/dropdown_menu'
|
||||
|
||||
const mapStateToProps = (state, { params: { id } }) => ({
|
||||
group: state.getIn(['groups', id]),
|
||||
|
|
|
@ -2,7 +2,6 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
|||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import Dropdown from '../../../../components/dropdown_menu'
|
||||
import Button from '../../../../components/button';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
|
|
@ -1,24 +1,27 @@
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { FormattedMessage, injectIntl, defineMessages } from 'react-intl';
|
||||
import { Link } from 'react-router-dom';
|
||||
import classNames from 'classnames';
|
||||
import { connectGroupStream } from '../../../actions/streaming';
|
||||
import { expandGroupTimeline } from '../../../actions/timelines';
|
||||
import StatusListContainer from '../../../containers/status_list_container';
|
||||
import ColumnSettingsContainer from './containers/column_settings_container';
|
||||
import Icon from '../../../components/icon';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import { injectIntl, defineMessages } from 'react-intl'
|
||||
import { Link } from 'react-router-dom'
|
||||
import classNames from 'classnames'
|
||||
import { connectGroupStream } from '../../../actions/streaming'
|
||||
import { expandGroupTimeline } from '../../../actions/timelines'
|
||||
import StatusListContainer from '../../../containers/status_list_container'
|
||||
import ColumnSettingsContainer from './containers/column_settings_container'
|
||||
import Icon from '../../../components/icon'
|
||||
import ColumnIndicator from '../../../components/column_indicator'
|
||||
|
||||
const messages = defineMessages({
|
||||
tabLatest: { id: 'group.timeline.tab_latest', defaultMessage: 'Latest' },
|
||||
show: { id: 'group.timeline.show_settings', defaultMessage: 'Show settings' },
|
||||
hide: { id: 'group.timeline.hide_settings', defaultMessage: 'Hide settings' },
|
||||
});
|
||||
tabLatest: { id: 'group.timeline.tab_latest', defaultMessage: 'Latest' },
|
||||
show: { id: 'group.timeline.show_settings', defaultMessage: 'Show settings' },
|
||||
hide: { id: 'group.timeline.hide_settings', defaultMessage: 'Hide settings' },
|
||||
empty: { id: 'empty_column.group', defaultMessage: 'There is nothing in this group yet. When members of this group post new statuses, they will appear here.' },
|
||||
})
|
||||
|
||||
const mapStateToProps = (state, props) => ({
|
||||
group: state.getIn(['groups', props.params.id]),
|
||||
relationships: state.getIn(['group_relationships', props.params.id]),
|
||||
hasUnread: state.getIn(['timelines', `group:${props.params.id}`, 'unread']) > 0,
|
||||
});
|
||||
group: state.getIn(['groups', props.params.id]),
|
||||
relationships: state.getIn(['group_relationships', props.params.id]),
|
||||
hasUnread: state.getIn(['timelines', `group:${props.params.id}`, 'unread']) > 0,
|
||||
})
|
||||
|
||||
export default
|
||||
@connect(mapStateToProps)
|
||||
|
@ -26,105 +29,71 @@ export default
|
|||
class GroupTimeline extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
router: PropTypes.object,
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
params: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
columnId: PropTypes.string,
|
||||
hasUnread: PropTypes.bool,
|
||||
group: PropTypes.oneOfType([ImmutablePropTypes.map, PropTypes.bool]),
|
||||
relationships: ImmutablePropTypes.map,
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
params: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
columnId: PropTypes.string,
|
||||
hasUnread: PropTypes.bool,
|
||||
group: PropTypes.oneOfType([ImmutablePropTypes.map, PropTypes.bool]),
|
||||
relationships: ImmutablePropTypes.map,
|
||||
intl: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
state = {
|
||||
collapsed: true,
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
const { dispatch } = this.props;
|
||||
const { id } = this.props.params;
|
||||
componentDidMount() {
|
||||
const { dispatch } = this.props
|
||||
const { id } = this.props.params
|
||||
|
||||
dispatch(expandGroupTimeline(id));
|
||||
dispatch(expandGroupTimeline(id))
|
||||
|
||||
this.disconnect = dispatch(connectGroupStream(id));
|
||||
this.disconnect = dispatch(connectGroupStream(id))
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
if (this.disconnect) {
|
||||
this.disconnect();
|
||||
this.disconnect = null;
|
||||
}
|
||||
componentWillUnmount() {
|
||||
if (this.disconnect) {
|
||||
this.disconnect()
|
||||
this.disconnect = null
|
||||
}
|
||||
}
|
||||
|
||||
handleLoadMore = maxId => {
|
||||
const { id } = this.props.params;
|
||||
this.props.dispatch(expandGroupTimeline(id, { maxId }));
|
||||
const { id } = this.props.params
|
||||
this.props.dispatch(expandGroupTimeline(id, { maxId }))
|
||||
}
|
||||
|
||||
handleToggleClick = (e) => {
|
||||
e.stopPropagation();
|
||||
this.setState({ collapsed: !this.state.collapsed });
|
||||
}
|
||||
e.stopPropagation()
|
||||
this.setState({ collapsed: !this.state.collapsed })
|
||||
}
|
||||
|
||||
render () {
|
||||
const { columnId, group, relationships, account, intl } = this.props;
|
||||
const { collapsed } = this.state;
|
||||
const { id } = this.props.params;
|
||||
render() {
|
||||
const { columnId, group, relationships, account, intl } = this.props
|
||||
const { collapsed } = this.state
|
||||
const { id } = this.props.params
|
||||
|
||||
if (typeof group === 'undefined' || !relationships) {
|
||||
return (<ColumnIndicator type='loading' />);
|
||||
} else if (group === false) {
|
||||
return (<ColumnIndicator type='missing' />);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{
|
||||
relationships.get('member') &&
|
||||
<TimelineComposeBlock size={46} group={group} shouldCondense={true} autoFocus={false} />
|
||||
}
|
||||
|
||||
<div className='group__feed'>
|
||||
<div className="column-header__wrapper">
|
||||
<h1 className="column-header">
|
||||
<Link to={`/groups/${id}`} className={classNames('btn grouped active')}>
|
||||
{intl.formatMessage(messages.tabLatest)}
|
||||
</Link>
|
||||
|
||||
<div className='column-header__buttons'>
|
||||
<button
|
||||
className={classNames('column-header__button', { 'active': !collapsed })}
|
||||
title={intl.formatMessage(collapsed ? messages.show : messages.hide)}
|
||||
aria-label={intl.formatMessage(collapsed ? messages.show : messages.hide)}
|
||||
aria-pressed={collapsed ? 'false' : 'true'}
|
||||
onClick={this.handleToggleClick}
|
||||
><Icon id='sliders' /></button>
|
||||
</div>
|
||||
</h1>
|
||||
{!collapsed && <div className='column-header__collapsible'>
|
||||
<div className='column-header__collapsible-inner'>
|
||||
<div className='column-header__collapsible__extra'>
|
||||
<ColumnSettingsContainer />
|
||||
</div>
|
||||
</div>
|
||||
</div>}
|
||||
</div>
|
||||
|
||||
<StatusListContainer
|
||||
alwaysPrepend
|
||||
scrollKey={`group_timeline-${columnId}`}
|
||||
timelineId={`group:${id}`}
|
||||
onLoadMore={this.handleLoadMore}
|
||||
group={group}
|
||||
withGroupAdmin={relationships && relationships.get('admin')}
|
||||
emptyMessage={<FormattedMessage id='empty_column.group' defaultMessage='There is nothing in this group yet. When members of this group post new statuses, they will appear here.' />}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
if (typeof group === 'undefined' || !relationships) {
|
||||
return (<ColumnIndicator type='loading' />)
|
||||
} else if (group === false) {
|
||||
return (<ColumnIndicator type='missing' />)
|
||||
}
|
||||
|
||||
return (
|
||||
<StatusListContainer
|
||||
alwaysPrepend
|
||||
scrollKey={`group_timeline-${columnId}`}
|
||||
timelineId={`group:${id}`}
|
||||
onLoadMore={this.handleLoadMore}
|
||||
group={group}
|
||||
withGroupAdmin={relationships && relationships.get('admin')}
|
||||
emptyMessage={intl.formatMessage(messages.empty)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
export { default } from './pinned_statuses';
|
|
@ -1,49 +0,0 @@
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { fetchPinnedStatuses } from '../../actions/pin_statuses';
|
||||
import { meUsername } from '../../initial_state';
|
||||
import StatusList from '../../components/status_list/status_list';
|
||||
import ColumnIndicator from '../../components/column_indicator';
|
||||
|
||||
const mapStateToProps = (state, { params: { username } }) => {
|
||||
return {
|
||||
isMyAccount: (username.toLowerCase() === meUsername.toLowerCase()),
|
||||
statusIds: state.getIn(['status_lists', 'pins', 'items']),
|
||||
hasMore: !!state.getIn(['status_lists', 'pins', 'next']),
|
||||
};
|
||||
};;
|
||||
|
||||
export default
|
||||
@connect(mapStateToProps)
|
||||
class PinnedStatuses extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
statusIds: ImmutablePropTypes.list.isRequired,
|
||||
hasMore: PropTypes.bool.isRequired,
|
||||
isMyAccount: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
componentWillMount () {
|
||||
this.props.dispatch(fetchPinnedStatuses());
|
||||
}
|
||||
|
||||
render () {
|
||||
const { statusIds, hasMore, isMyAccount } = this.props;
|
||||
|
||||
if (!isMyAccount) {
|
||||
return <ColumnIndicator type='missing' />
|
||||
}
|
||||
|
||||
return (
|
||||
<StatusList
|
||||
statusIds={statusIds}
|
||||
scrollKey='pinned_statuses'
|
||||
hasMore={hasMore}
|
||||
emptyMessage={<FormattedMessage id='pinned_statuses.none' defaultMessage='No pins to show.' />}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import ComposeFormContainer from '../../compose/containers/compose_form_container';
|
||||
import NotificationsContainer from '../../../containers/notifications_container';
|
||||
import LoadingBarContainer from '../../../containers/loading_bar_container';
|
||||
import ModalContainer from '../../../containers/modal_container';
|
||||
import ModalRoot from '../../../components/modal/modal_root'
|
||||
|
||||
export default class Compose extends PureComponent {
|
||||
|
||||
|
@ -10,7 +10,7 @@ export default class Compose extends PureComponent {
|
|||
<div>
|
||||
<ComposeFormContainer />
|
||||
<NotificationsContainer />
|
||||
<ModalContainer />
|
||||
<ModalRoot />
|
||||
<LoadingBarContainer className='loading-bar' />
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -3,7 +3,6 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
|||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import { openModal } from '../../../../actions/modal';
|
||||
import { me, isStaff } from '../../../../initial_state';
|
||||
import Dropdown from '../../../../components/dropdown_menu';
|
||||
import IconButton from '../../../../components/icon_button';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
|
|
@ -15,7 +15,8 @@ import { clearHeight } from '../../actions/height_cache'
|
|||
import { openModal } from '../../actions/modal'
|
||||
import WrappedRoute from './util/wrapped_route'
|
||||
import NotificationsContainer from '../../containers/notifications_container'
|
||||
import ModalContainer from '../../containers/modal_container'
|
||||
import ModalRoot from '../../components/modal/modal_root'
|
||||
import PopoverRoot from '../../components/popover/popover_root'
|
||||
import UploadArea from '../../components/upload_area'
|
||||
// import TrendsPanel from './components/trends_panel'
|
||||
// import { WhoToFollowPanel } from '../../components/panel'
|
||||
|
@ -80,7 +81,6 @@ const mapStateToProps = state => ({
|
|||
isComposing: state.getIn(['compose', 'is_composing']),
|
||||
hasComposingText: state.getIn(['compose', 'text']).trim().length !== 0,
|
||||
hasMediaAttachments: state.getIn(['compose', 'media_attachments']).size > 0,
|
||||
dropdownMenuIsOpen: state.getIn(['dropdown_menu', 'openId']) !== null,
|
||||
})
|
||||
|
||||
const keyMap = {
|
||||
|
@ -243,7 +243,6 @@ class UI extends PureComponent {
|
|||
hasMediaAttachments: PropTypes.bool,
|
||||
location: PropTypes.object,
|
||||
intl: PropTypes.object.isRequired,
|
||||
dropdownMenuIsOpen: PropTypes.bool,
|
||||
}
|
||||
|
||||
state = {
|
||||
|
@ -504,7 +503,7 @@ class UI extends PureComponent {
|
|||
|
||||
render() {
|
||||
const { draggingOver } = this.state
|
||||
const { children, location, dropdownMenuIsOpen } = this.props
|
||||
const { children, location } = this.props
|
||||
|
||||
const handlers = me ? {
|
||||
help: this.handleHotkeyToggleHelp,
|
||||
|
@ -532,12 +531,7 @@ class UI extends PureComponent {
|
|||
attach={window}
|
||||
focused
|
||||
>
|
||||
<div
|
||||
ref={this.setRef}
|
||||
style={{
|
||||
pointerEvents: dropdownMenuIsOpen ? 'none' : null
|
||||
}}
|
||||
>
|
||||
<div ref={this.setRef}>
|
||||
<SwitchingArea
|
||||
location={location}
|
||||
onLayoutChange={this.handleLayoutChange}
|
||||
|
@ -546,7 +540,8 @@ class UI extends PureComponent {
|
|||
</SwitchingArea>
|
||||
|
||||
<NotificationsContainer />
|
||||
<ModalContainer />
|
||||
<ModalRoot />
|
||||
<PopoverRoot />
|
||||
<UploadArea active={draggingOver} onClose={this.closeUploadModal} />
|
||||
</div>
|
||||
</HotKeys>
|
||||
|
|
|
@ -58,10 +58,6 @@ export function Status() {
|
|||
return import(/* webpackChunkName: "features/status" */'../../status')
|
||||
}
|
||||
|
||||
export function PinnedStatuses() {
|
||||
return import(/* webpackChunkName: "features/pinned_statuses" */'../../pinned_statuses')
|
||||
}
|
||||
|
||||
export function AccountTimeline() {
|
||||
return import(/* webpackChunkName: "features/account_timeline" */'../../account_timeline')
|
||||
}
|
||||
|
@ -90,7 +86,7 @@ export function GenericNotFound() {
|
|||
return import(/* webpackChunkName: "features/generic_not_found" */'../../generic_not_found')
|
||||
}
|
||||
|
||||
export function FavouritedStatuses() {
|
||||
export function FavoritedStatuses() {
|
||||
return import(/* webpackChunkName: "features/favourited_statuses" */'../../favourited_statuses')
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import Sticky from 'react-stickynode'
|
||||
import Sidebar from '../components/sidebar'
|
||||
import Search from '../components/search'
|
||||
import Image from '../components/image'
|
||||
import Text from '../components/text'
|
||||
import Button from '../components/button'
|
||||
import DisplayName from '../components/display_name'
|
||||
import TabBar from '../components/tab_bar'
|
||||
import ColumnHeader from '../components/column_header'
|
||||
|
||||
export default class GroupLayout extends ImmutablePureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.array,
|
||||
tabs: PropTypes.array,
|
||||
group: ImmutablePropTypes.map,
|
||||
layout: PropTypes.object,
|
||||
title: PropTypes.string,
|
||||
showBackBtn: PropTypes.bool,
|
||||
}
|
||||
|
||||
render() {
|
||||
const { group, children, layout, title, showBackBtn, actions, tabs } = this.props
|
||||
|
||||
// const tabs = !account ? null : [
|
||||
// {
|
||||
// to: `/${account.get('acct')}`,
|
||||
// title: 'Timeline',
|
||||
// },
|
||||
// {
|
||||
// to: `/${account.get('acct')}/comments`,
|
||||
// title: 'Comments',
|
||||
// },
|
||||
// {
|
||||
// to: `/${account.get('acct')}/photos`,
|
||||
// title: 'Photos',
|
||||
// },
|
||||
// {
|
||||
// to: `/${account.get('acct')}/videos`,
|
||||
// title: 'Videos',
|
||||
// },
|
||||
// {
|
||||
// to: '',
|
||||
// title: 'More',
|
||||
// },
|
||||
// ]
|
||||
|
||||
return (
|
||||
<div className={[_s.default, _s.flexRow, _s.width100PC, _s.heightMin100VH, _s.backgroundcolorSecondary3].join(' ')}>
|
||||
|
||||
<Sidebar />
|
||||
|
||||
<main role='main' className={[_s.default, _s.flexShrink1, _s.flexGrow1, _s.borderColorSecondary2, _s.borderLeft1PX].join(' ')}>
|
||||
|
||||
<div className={[_s.default, _s.height53PX, _s.borderBottom1PX, _s.borderColorSecondary2, _s.backgroundcolorSecondary3, _s.z3, _s.top0, _s.positionFixed].join(' ')}>
|
||||
<div className={[_s.default, _s.height53PX, _s.paddingLeft15PX, _s.width1015PX, _s.flexRow, _s.justifyContentSpaceBetween].join(' ')}>
|
||||
<div className={[_s.default, _s.width645PX].join(' ')}>
|
||||
<ColumnHeader
|
||||
title={title}
|
||||
showBackBtn={showBackBtn}
|
||||
actions={actions}
|
||||
tabs={tabs}
|
||||
/>
|
||||
</div>
|
||||
<div className={[_s.default, _s.width340PX].join(' ')}>
|
||||
<Search />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={[_s.default, _s.height53PX].join(' ')}></div>
|
||||
|
||||
<div className={[_s.default, _s.width1015PX, _s.paddingLeft15PX, _s.paddingVertical15PX].join(' ')}>
|
||||
|
||||
<div className={[_s.default, _s.z1, _s.width100PC, _s.marginBottom15PX].join(' ')}>
|
||||
<div className={[_s.default, _s.height350PX, _s.width100PC, _s.radiusSmall, _s.overflowHidden].join(' ')}>
|
||||
<Image className={_s.height350PX} src='https://gab.com/media/user/bz-5cf53d08403d4.jpeg' />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={[_s.default, _s.flexRow, _s.width100PC, _s.justifyContentSpaceBetween].join(' ')}>
|
||||
<div className={[_s.default, _s.width645PX, _s.z1].join(' ')}>
|
||||
<div className={_s.default}>
|
||||
{children}``
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={[_s.default, _s.width340PX].join(' ')}>
|
||||
<Sticky top={73} enabled>
|
||||
<div className={[_s.default, _s.width340PX].join(' ')}>
|
||||
{layout}
|
||||
</div>
|
||||
</Sticky>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,16 +1,46 @@
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import Sticky from 'react-stickynode'
|
||||
import Sidebar from '../components/sidebar'
|
||||
import Image from '../components/image'
|
||||
import Text from '../components/text'
|
||||
import Button from '../components/button'
|
||||
import DisplayName from '../components/display_name'
|
||||
import TabBar from '../components/tab_bar'
|
||||
|
||||
export default class ProfileLayout extends PureComponent {
|
||||
export default class ProfileLayout extends ImmutablePureComponent {
|
||||
static propTypes = {
|
||||
account: ImmutablePropTypes.map,
|
||||
layout: PropTypes.object,
|
||||
title: PropTypes.string,
|
||||
showBackBtn: PropTypes.bool,
|
||||
}
|
||||
|
||||
render() {
|
||||
const { children, layout } = this.props
|
||||
const { account, children, layout } = this.props
|
||||
|
||||
const tabs = !account ? null : [
|
||||
{
|
||||
to: `/${account.get('acct')}`,
|
||||
title: 'Timeline',
|
||||
},
|
||||
{
|
||||
to: `/${account.get('acct')}/comments`,
|
||||
title: 'Comments',
|
||||
},
|
||||
{
|
||||
to: `/${account.get('acct')}/photos`,
|
||||
title: 'Photos',
|
||||
},
|
||||
{
|
||||
to: `/${account.get('acct')}/videos`,
|
||||
title: 'Videos',
|
||||
},
|
||||
{
|
||||
to: '',
|
||||
title: 'More',
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div className={[_s.default, _s.flexRow, _s.width100PC, _s.heightMin100VH, _s.backgroundcolorSecondary3].join(' ')}>
|
||||
|
@ -21,14 +51,67 @@ export default class ProfileLayout extends PureComponent {
|
|||
|
||||
<div className={[_s.default, _s.width1015PX, _s.flexRow, _s.justifyContentSpaceBetween, _s.paddingLeft15PX, _s.paddingVertical15PX].join(' ')}>
|
||||
<div className={[_s.default, _s.z1, _s.width100PC].join(' ')}>
|
||||
<div className={[_s.default, _s.height350PX, _s.width100PC].join(' ')}>
|
||||
<div className={[_s.default, _s.height350PX, _s.width100PC, _s.radiusSmall, _s.overflowHidden].join(' ')}>
|
||||
<Image className={_s.height350PX} src='https://gab.com/media/user/bz-5cf53d08403d4.jpeg' />
|
||||
</div>
|
||||
<div className={[_s.default, _s.backgroundColorPrimary].join(' ')}>
|
||||
|
||||
<div className={[_s.default, _s.borderBottom1PX, _s.borderColorSecondary, _s.width100PC].join(' ')}>
|
||||
<div className={[_s.default, _s.flexRow, _s.paddingHorizontal15PX].join(' ')}>
|
||||
<Image
|
||||
className={[_s.circle, _s.marginTopNeg75PX, _s.borderColorWhite, _s.border2PX].join(' ')}
|
||||
height='150px'
|
||||
width='150px'
|
||||
src='http://localhost:3000/system/accounts/avatars/000/000/001/original/260e8c96c97834da.jpeg?1562898139'
|
||||
/>
|
||||
<div className={[_s.default, _s.paddingHorizontal15PX, _s.paddingVertical10PX].join(' ')}>
|
||||
<DisplayName account={account} multiline large />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={[_s.default, _s.flexRow, _s.borderBottom1PX, _s.borderColorSecondary, _s.marginTop5PX, _s.height53PX].join(' ')}>
|
||||
<div className={[_s.default].join(' ')}>
|
||||
<TabBar tabs={tabs} large />
|
||||
</div>
|
||||
<div className={[_s.default, _s.flexRow, _s.marginLeftAuto, _s.paddingVertical5PX].join(' ')}>
|
||||
<Button
|
||||
outline
|
||||
icon='ellipsis'
|
||||
iconWidth='18px'
|
||||
iconHeight='18px'
|
||||
iconClassName={_s.fillColorBrand}
|
||||
color='brand'
|
||||
backgroundColor='none'
|
||||
className={[_s.justifyContentCenter, _s.alignItemsCenter, _s.marginRight10PX, _s.paddingHorizontal10PX].join(' ')}
|
||||
/>
|
||||
<Button
|
||||
outline
|
||||
icon='chat'
|
||||
iconWidth='18px'
|
||||
iconHeight='18px'
|
||||
iconClassName={_s.fillColorBrand}
|
||||
color='brand'
|
||||
backgroundColor='none'
|
||||
className={[_s.justifyContentCenter, _s.alignItemsCenter, _s.marginRight10PX, _s.paddingHorizontal10PX].join(' ')}
|
||||
/>
|
||||
<Button
|
||||
className={[_s.justifyContentCenter, _s.alignItemsCenter].join(' ')}
|
||||
>
|
||||
<span className={[_s.paddingHorizontal15PX].join(' ')}>
|
||||
<Text
|
||||
color='white'
|
||||
weight='bold'
|
||||
size='medium'
|
||||
className={[_s.paddingHorizontal15PX].join(' ')}
|
||||
>
|
||||
Follow
|
||||
</Text>
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={[_s.default, _s.width1015PX, _s.flexRow, _s.justifyContentSpaceBetween, _s.paddingLeft15PX, _s.paddingVertical15PX].join(' ')}>
|
||||
<div className={[_s.default, _s.width1015PX, _s.flexRow, _s.justifyContentSpaceBetween, _s.paddingRight15PX, _s.paddingVertical15PX].join(' ')}>
|
||||
<div className={[_s.default, _s.width645PX, _s.z1].join(' ')}>
|
||||
<div className={_s.default}>
|
||||
{children}
|
||||
|
@ -36,7 +119,7 @@ export default class ProfileLayout extends PureComponent {
|
|||
</div>
|
||||
|
||||
<div className={[_s.default, _s.width340PX].join(' ')}>
|
||||
<Sticky top={73} enabled>
|
||||
<Sticky top={15} enabled>
|
||||
<div className={[_s.default, _s.width340PX].join(' ')}>
|
||||
{layout}
|
||||
</div>
|
||||
|
|
|
@ -1,62 +1,90 @@
|
|||
import { Fragment } from 'react';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { fetchGroup } from '../actions/groups';
|
||||
import HeaderContainer from '../features/groups/timeline/containers/header_container';
|
||||
import GroupPanel from '../features/groups/timeline/components/panel';
|
||||
// import GroupSidebarPanel from '../features/groups/sidebar_panel';
|
||||
import DefaultLayout from '../layouts/default_layout';
|
||||
import { WhoToFollowPanel } from '../components/panel';
|
||||
import LinkFooter from '../components/link_footer';
|
||||
import { Fragment } from 'react'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import { fetchGroup } from '../actions/groups'
|
||||
import GroupInfoPanel from '../components/panel/group_info_panel'
|
||||
import GroupLayout from '../layouts/group_layout'
|
||||
import WhoToFollowPanel from '../components/panel/who_to_follow_panel'
|
||||
import LinkFooter from '../components/link_footer'
|
||||
import TimelineComposeBlock from '../components/timeline_compose_block'
|
||||
import Divider from '../components/divider'
|
||||
|
||||
const mapStateToProps = (state, { params: { id } }) => ({
|
||||
group: state.getIn(['groups', id]),
|
||||
relationships: state.getIn(['group_relationships', id]),
|
||||
});
|
||||
})
|
||||
|
||||
export default
|
||||
@connect(mapStateToProps)
|
||||
class GroupPage extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
group: ImmutablePropTypes.map,
|
||||
static propTypes = {
|
||||
group: ImmutablePropTypes.map,
|
||||
relationships: ImmutablePropTypes.map,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
componentWillMount() {
|
||||
const { params: { id }, dispatch } = this.props;
|
||||
|
||||
dispatch(fetchGroup(id));
|
||||
}
|
||||
|
||||
render () {
|
||||
const { children, group, relationships } = this.props;
|
||||
const top = group ? <HeaderContainer groupId={group.get('id')} /> : null;
|
||||
componentWillMount() {
|
||||
const { params: { id }, dispatch } = this.props
|
||||
|
||||
dispatch(fetchGroup(id))
|
||||
}
|
||||
|
||||
render() {
|
||||
const { children, group, relationships } = this.props
|
||||
|
||||
// <div className="column-header__wrapper">
|
||||
// <h1 className="column-header">
|
||||
// <Link to={`/groups/${id}`} className={classNames('btn grouped active')}>
|
||||
// {intl.formatMessage(messages.tabLatest)}
|
||||
// </Link>
|
||||
|
||||
// <div className='column-header__buttons'>
|
||||
// <button
|
||||
// className={classNames('column-header__button', { 'active': !collapsed })}
|
||||
// title={intl.formatMessage(collapsed ? messages.show : messages.hide)}
|
||||
// aria-label={intl.formatMessage(collapsed ? messages.show : messages.hide)}
|
||||
// aria-pressed={collapsed ? 'false' : 'true'}
|
||||
// onClick={this.handleToggleClick}
|
||||
// ><Icon id='sliders' /></button>
|
||||
// </div>
|
||||
// </h1>
|
||||
// {!collapsed && <div className='column-header__collapsible'>
|
||||
// <div className='column-header__collapsible-inner'>
|
||||
// <div className='column-header__collapsible__extra'>
|
||||
// <ColumnSettingsContainer />
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>}
|
||||
// </div>
|
||||
|
||||
return (
|
||||
<DefaultLayout
|
||||
layout={{
|
||||
TOP: top,
|
||||
RIGHT: (
|
||||
<Fragment>
|
||||
<WhoToFollowPanel />
|
||||
</Fragment>
|
||||
),
|
||||
LEFT: (
|
||||
<Fragment>
|
||||
{group && relationships &&
|
||||
<GroupPanel
|
||||
group={group}
|
||||
relationships={relationships}
|
||||
/>}
|
||||
<LinkFooter />
|
||||
</Fragment>
|
||||
)
|
||||
}}
|
||||
<GroupLayout
|
||||
title={'group name'}
|
||||
actions={[
|
||||
{
|
||||
icon: 'ellipsis',
|
||||
onClick: null,
|
||||
},
|
||||
]}
|
||||
layout={(
|
||||
<Fragment>
|
||||
<GroupInfoPanel />
|
||||
<WhoToFollowPanel />
|
||||
<LinkFooter />
|
||||
</Fragment>
|
||||
)}
|
||||
showBackBtn
|
||||
>
|
||||
{
|
||||
!!relationships && relationships.get('member') &&
|
||||
<Fragment>
|
||||
<TimelineComposeBlock size={46} group={group} autoFocus />
|
||||
<Divider />
|
||||
</Fragment>
|
||||
}
|
||||
{children}
|
||||
</DefaultLayout>
|
||||
</GroupLayout>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -40,7 +40,7 @@ export default class HomePage extends PureComponent {
|
|||
</Fragment>
|
||||
)}
|
||||
>
|
||||
<TimelineComposeBlock autoFocus={false} shouldCondense />
|
||||
<TimelineComposeBlock autoFocus={false} />
|
||||
<Divider />
|
||||
{children}
|
||||
</DefaultLayout>
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import { Fragment } from 'react'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import { fetchAccountByUsername } from '../actions/accounts'
|
||||
import { makeGetAccount } from '../selectors'
|
||||
import LinkFooter from '../components/link_footer'
|
||||
import ProfileStatsPanel from '../components/panel/profile_stats_panel'
|
||||
import ProfileInfoPanel from '../components/panel/profile_info_panel'
|
||||
import MediaGalleryPanel from '../components/panel/media_gallery_panel'
|
||||
import ProfileLayout from '../layouts/profile_layout'
|
||||
|
@ -10,24 +13,15 @@ const mapStateToProps = (state, { params: { username }, withReplies = false }) =
|
|||
const accounts = state.getIn(['accounts'])
|
||||
const accountFetchError = (state.getIn(['accounts', -1, 'username'], '').toLowerCase() == username.toLowerCase())
|
||||
|
||||
let accountId = -1
|
||||
let account = null
|
||||
let accountUsername = username
|
||||
if (accountFetchError) {
|
||||
accountId = null
|
||||
}
|
||||
else {
|
||||
if (!accountFetchError) {
|
||||
account = accounts.find(acct => username.toLowerCase() == acct.getIn(['acct'], '').toLowerCase())
|
||||
accountId = account ? account.getIn(['id'], null) : -1
|
||||
accountUsername = account ? account.getIn(['acct'], '') : ''
|
||||
}
|
||||
|
||||
//Children components fetch information
|
||||
const getAccount = makeGetAccount()
|
||||
|
||||
return {
|
||||
account,
|
||||
accountId,
|
||||
accountUsername,
|
||||
account: account ? getAccount(state, account.get('id')) : null,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,30 +29,33 @@ export default
|
|||
@connect(mapStateToProps)
|
||||
class ProfilePage extends ImmutablePureComponent {
|
||||
static propTypes = {
|
||||
params: PropTypes.object.isRequired,
|
||||
account: ImmutablePropTypes.map,
|
||||
accountUsername: PropTypes.string.isRequired,
|
||||
accountId: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.number,
|
||||
]).isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
children: PropTypes.node,
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
const { dispatch, params: { username } } = this.props
|
||||
dispatch(fetchAccountByUsername(username))
|
||||
}
|
||||
|
||||
render() {
|
||||
const { accountId, account, accountUsername } = this.props
|
||||
const { account } = this.props
|
||||
|
||||
return (
|
||||
<ProfileLayout
|
||||
account={account}
|
||||
layout={(
|
||||
<Fragment>
|
||||
<ProfileInfoPanel />
|
||||
<MediaGalleryPanel />
|
||||
<ProfileStatsPanel account={account} />
|
||||
<ProfileInfoPanel account={account} />
|
||||
<MediaGalleryPanel account={account} />
|
||||
<LinkFooter />
|
||||
</Fragment>
|
||||
)}
|
||||
>
|
||||
{ /* this.props.children */ }
|
||||
{ this.props.children }
|
||||
</ProfileLayout>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
import Immutable from 'immutable';
|
||||
import {
|
||||
DROPDOWN_MENU_OPEN,
|
||||
DROPDOWN_MENU_CLOSE,
|
||||
} from '../actions/dropdown_menu';
|
||||
|
||||
const initialState = Immutable.Map({ openId: null, placement: null, keyboard: false });
|
||||
|
||||
export default function dropdownMenu(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case DROPDOWN_MENU_OPEN:
|
||||
return state.merge({ openId: action.id, placement: action.placement, keyboard: action.keyboard });
|
||||
case DROPDOWN_MENU_CLOSE:
|
||||
return state.get('openId') === action.id ? state.set('openId', null) : state;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import { combineReducers } from 'redux-immutable';
|
||||
import dropdown_menu from './dropdown_menu';
|
||||
import popover from './popover';
|
||||
import timelines from './timelines';
|
||||
import meta from './meta';
|
||||
import alerts from './alerts';
|
||||
|
@ -40,7 +40,7 @@ import sidebar from './sidebar';
|
|||
import status_revision_list from './status_revision_list';
|
||||
|
||||
const reducers = {
|
||||
dropdown_menu,
|
||||
popover,
|
||||
timelines,
|
||||
meta,
|
||||
alerts,
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
import Immutable from 'immutable'
|
||||
import {
|
||||
POPOVER_OPEN,
|
||||
POPOVER_CLOSE,
|
||||
} from '../actions/popover'
|
||||
|
||||
const initialState = Immutable.Map({
|
||||
popoverType: null,
|
||||
placement: null,
|
||||
keyboard: false
|
||||
})
|
||||
|
||||
export default function popoverMenu(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case POPOVER_OPEN:
|
||||
console.log("POPOVER_OPEN:", action)
|
||||
return {
|
||||
popoverType: action.popoverType,
|
||||
placement: action.placement,
|
||||
keyboard: action.keyboard
|
||||
}
|
||||
case POPOVER_CLOSE:
|
||||
console.log("POPOVER_CLOSE:", action)
|
||||
return initialState;
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
|
@ -6,9 +6,6 @@ import {
|
|||
FAVOURITED_STATUSES_EXPAND_SUCCESS,
|
||||
FAVOURITED_STATUSES_EXPAND_FAIL,
|
||||
} from '../actions/favourites';
|
||||
import {
|
||||
PINNED_STATUSES_FETCH_SUCCESS,
|
||||
} from '../actions/pin_statuses';
|
||||
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
||||
import {
|
||||
FAVOURITE_SUCCESS,
|
||||
|
@ -75,8 +72,6 @@ export default function statusLists(state = initialState, action) {
|
|||
return prependOneToList(state, 'favourites', action.status);
|
||||
case UNFAVOURITE_SUCCESS:
|
||||
return removeOneFromList(state, 'favourites', action.status);
|
||||
case PINNED_STATUSES_FETCH_SUCCESS:
|
||||
return normalizeList(state, 'pins', action.statuses, action.next);
|
||||
case PIN_SUCCESS:
|
||||
return prependOneToList(state, 'pins', action.status);
|
||||
case UNPIN_SUCCESS:
|
||||
|
|
|
@ -28,6 +28,20 @@ export function breakpointExtraSmall(width) {
|
|||
return width < BREAKPOINT_EXTRA_SMALL
|
||||
}
|
||||
|
||||
export function getScreenBreakpoint(width) {
|
||||
if (width > BREAKPOINT_EXTRA_LARGE) {
|
||||
return 'BREAKPOINT_EXTRA_LARGE'
|
||||
} else if (width > BREAKPOINT_MEDIUM && width < BREAKPOINT_LARGE) {
|
||||
return 'BREAKPOINT_LARGE'
|
||||
} else if (width > BREAKPOINT_SMALL && width < BREAKPOINT_MEDIUM) {
|
||||
return 'BREAKPOINT_MEDIUM'
|
||||
} else if (width > BREAKPOINT_EXTRA_SMALL && width < BREAKPOINT_SMALL) {
|
||||
return 'BREAKPOINT_SMALL'
|
||||
} else {
|
||||
return 'BREAKPOINT_EXTRA_SMALL'
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const LAYOUT_BREAKPOINT = 630
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
html, body {
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
@ -17,6 +18,22 @@ body {
|
|||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", sans-serif;
|
||||
}
|
||||
|
||||
.statusContent a {
|
||||
color: #21cf7a;
|
||||
}
|
||||
|
||||
.dangerousContent * {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
font-size: 14px;
|
||||
overflow-wrap: break-word;
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", sans-serif;
|
||||
}
|
||||
|
||||
.dangerousContent a {
|
||||
color: #21cf7a;
|
||||
}
|
||||
|
||||
.statusCardVideo iframe {
|
||||
height: 100% !important;
|
||||
width: 100% !important;
|
||||
|
@ -68,17 +85,41 @@ body {
|
|||
flex-shrink: 1;
|
||||
}
|
||||
|
||||
.flexGrow1 { flex-grow: 1; }
|
||||
.flexShrink1 { flex-shrink: 1; }
|
||||
.flexRow { flex-direction: row; }
|
||||
.flexWrap { flex-wrap: wrap; }
|
||||
.flexGrow1 {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.alignItemsEnd { align-items: flex-end; }
|
||||
.alignItemsStart { align-items: flex-start; }
|
||||
.alignItemsCenter { align-items: center; }
|
||||
.flexShrink1 {
|
||||
flex-shrink: 1;
|
||||
}
|
||||
|
||||
.justifyContentSpaceBetween { justify-content: space-between; }
|
||||
.justifyContentCenter { justify-content: center; }
|
||||
.flexRow {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.flexWrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.alignItemsEnd {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.alignItemsStart {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.alignItemsCenter {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.justifyContentSpaceBetween {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.justifyContentCenter {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.overflowHidden {
|
||||
overflow-x: hidden;
|
||||
|
@ -93,207 +134,624 @@ body {
|
|||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.whiteSpaceNoWrap { white-space: nowrap; }
|
||||
.whiteSpaceNoWrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.outlineNone { outline: none; }
|
||||
.outlineFocusBrand:focus { outline: 2px solid #21cf7a; }
|
||||
.resizeNone { resize: none; }
|
||||
.outlineNone {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.circle { border-radius: 9999px; }
|
||||
.radiusSmall { border-radius: 8px; }
|
||||
.outlineFocusBrand:focus {
|
||||
outline: 2px solid #21cf7a;
|
||||
}
|
||||
|
||||
.borderColorSecondary2 { border-color: #e5e9ed; }
|
||||
.borderColorSecondary { border-color: #ECECED; }
|
||||
.borderColorWhite { border-color: #fff; }
|
||||
.borderColorBrand { border-color: #21cf7a; }
|
||||
.borderColorTransparent { border-color: transparent; }
|
||||
.borderRight1PX { border-right-width: 1px; }
|
||||
.borderBottom1PX { border-bottom-width: 1px; }
|
||||
.borderLeft1PX { border-left-width: 1px; }
|
||||
.borderTop1PX { border-top-width: 1px; }
|
||||
.border1PX { border-width: 1px; }
|
||||
.border2PX { border-width: 2px; }
|
||||
.borderBottom2PX { border-bottom-width: 2px; }
|
||||
.resizeNone {
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.borderDashed { border-style: dashed; }
|
||||
.circle {
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
.marginAuto { margin: auto; }
|
||||
.radiusSmall {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.displayNone { display: none; }
|
||||
.displayBlock { display: block; }
|
||||
.displayInline { display: inline; }
|
||||
.displayFlex { display: flex !important; }
|
||||
.displayInlineBlock { display: inline-block; }
|
||||
.borderColorSecondary2 {
|
||||
border-color: #e5e9ed;
|
||||
}
|
||||
|
||||
.cursorPointer { cursor: pointer }
|
||||
.borderColorSecondary {
|
||||
border-color: #ECECED;
|
||||
}
|
||||
|
||||
.pointerEventsAuto > * { pointer-events: auto;}
|
||||
.pointerEventsNone { pointer-events: none !important; }
|
||||
.borderColorWhite {
|
||||
border-color: #fff;
|
||||
}
|
||||
|
||||
.backgroundTransparent { background-color: transparent; }
|
||||
.backgroundPanel { background-color: #aaa; }
|
||||
.backgroundSubtle { background-color: #F5F8FA; }
|
||||
.backgroundSubtle_onHover:hover { background-color: #F5F8FA; }
|
||||
.backgroundSubtle2 { background-color: #e8ecef; }
|
||||
.backgroundSubtle2Dark_onHover:hover { background-color: #d9e0e5; }
|
||||
.backgroundcolorSecondary3 { background-color: #F6F6F9; }
|
||||
.backgroundColorPrimary { background-color: #fff; }
|
||||
.backgroundColorPrimaryOpaque { background-color: rgba(255,255,255,0.8); }
|
||||
.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; }
|
||||
.backgroundColorBrand_onHover:hover { background-color: #21cf7a; }
|
||||
.backgroundColorBrandDark { background-color: #38A16B; }
|
||||
.backgroundColorBrandDark_onHover:hover { background-color: #38A16B; }
|
||||
.colorPrimary { color: #000; }
|
||||
.colorWhite { color: #fff; }
|
||||
.colorWhite_onHover:hover { color: #fff; }
|
||||
.colorSecondary { color: #4B4F55; }
|
||||
.colorBrand { color: #21cf7a }
|
||||
.fillColorBlack { fill: #000; }
|
||||
.fillColorWhite { fill: #fff; }
|
||||
.fillColorBrand { fill: #21cf7a; }
|
||||
.fillcolorSecondary { fill: #666; }
|
||||
.borderColorBrand {
|
||||
border-color: #21cf7a;
|
||||
}
|
||||
|
||||
.bottom0 { bottom: 0; }
|
||||
.bottomAuto { bottom: auto; }
|
||||
.left0 { left: 0px; }
|
||||
.right0 { right: 0px; }
|
||||
.rightAuto { right: auto; }
|
||||
.top0 { top: 0px; }
|
||||
.borderColorTransparent {
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.lineHeight125 { line-height: 1.25em; }
|
||||
.lineHeight15 { line-height: 1.5em; }
|
||||
.lineHeight2 { line-height: 2em; }
|
||||
.borderRight1PX {
|
||||
border-right-width: 1px;
|
||||
}
|
||||
|
||||
.positionFixed { position: fixed; }
|
||||
.positionSticky { position: sticky; }
|
||||
.positionRelative { position: relative; }
|
||||
.positionAbsolute { position: absolute; }
|
||||
.borderBottom1PX {
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
|
||||
.noSelect { user-select: none; }
|
||||
.borderLeft1PX {
|
||||
border-left-width: 1px;
|
||||
}
|
||||
|
||||
.heightMin100VH { min-height: 100vh; }
|
||||
.height100VH { height: 100vh; }
|
||||
.height100PC { height: 100%; }
|
||||
.height22PX { height: 22px; }
|
||||
.height50PX { height: 50px; }
|
||||
.height53PX { height: 53px; }
|
||||
.height72PX { height: 72px; }
|
||||
.height122PX { height: 122px; }
|
||||
.height260PX { height: 260px; }
|
||||
.height350PX { height: 350px; }
|
||||
.borderTop1PX {
|
||||
border-top-width: 1px;
|
||||
}
|
||||
|
||||
.width1015PX { width: 1015px; }
|
||||
.width645PX { width: 645px; }
|
||||
.width400PX { width: 400px; }
|
||||
.width340PX { width: 340px; }
|
||||
.width240PX { width: 240px; }
|
||||
.width100PC { width: 100%; }
|
||||
.width72PX { width: 72px; }
|
||||
.width50PC { width: 50%; }
|
||||
.maxWidth100PC { max-width: 100%; }
|
||||
.border1PX {
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
.border2PX {
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
.borderBottom2PX {
|
||||
border-bottom-width: 2px;
|
||||
}
|
||||
|
||||
.borderDashed {
|
||||
border-style: dashed;
|
||||
}
|
||||
|
||||
.marginAuto {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.displayNone {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.displayBlock {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.displayInline {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.displayFlex {
|
||||
display: flex !important;
|
||||
}
|
||||
|
||||
.displayInlineBlock {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.cursorPointer {
|
||||
cursor: pointer
|
||||
}
|
||||
|
||||
.pointerEventsAuto>* {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.pointerEventsNone {
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
.backgroundTransparent {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.backgroundPanel {
|
||||
background-color: #aaa;
|
||||
}
|
||||
|
||||
.backgroundSubtle {
|
||||
background-color: #F5F8FA;
|
||||
}
|
||||
|
||||
.backgroundSubtle_onHover:hover {
|
||||
background-color: #F5F8FA;
|
||||
}
|
||||
|
||||
.backgroundSubtle2 {
|
||||
background-color: #e8ecef;
|
||||
}
|
||||
|
||||
.backgroundSubtle2Dark_onHover:hover {
|
||||
background-color: #d9e0e5;
|
||||
}
|
||||
|
||||
.backgroundcolorSecondary3 {
|
||||
background-color: #F6F6F9;
|
||||
}
|
||||
|
||||
.backgroundColorPrimary {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.backgroundColorPrimaryOpaque {
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.backgroundColorBrand_onHover:hover {
|
||||
background-color: #21cf7a;
|
||||
}
|
||||
|
||||
.backgroundColorBrandDark {
|
||||
background-color: #38A16B;
|
||||
}
|
||||
|
||||
.backgroundColorBrandDark_onHover:hover {
|
||||
background-color: #38A16B;
|
||||
}
|
||||
|
||||
.colorPrimary {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.colorWhite {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.colorWhite_onHover:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.colorSecondary {
|
||||
color: #4B4F55;
|
||||
}
|
||||
|
||||
.colorBrand {
|
||||
color: #21cf7a
|
||||
}
|
||||
|
||||
.fillColorBlack {
|
||||
fill: #000;
|
||||
}
|
||||
|
||||
.fillColorWhite {
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
.fillColorBrand {
|
||||
fill: #21cf7a;
|
||||
}
|
||||
|
||||
.fillcolorSecondary {
|
||||
fill: #666;
|
||||
}
|
||||
|
||||
.bottom0 {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.bottomAuto {
|
||||
bottom: auto;
|
||||
}
|
||||
|
||||
.left0 {
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
.right0 {
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
.rightAuto {
|
||||
right: auto;
|
||||
}
|
||||
|
||||
.top0 {
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
.lineHeight125 {
|
||||
line-height: 1.25em;
|
||||
}
|
||||
|
||||
.lineHeight15 {
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
.lineHeight2 {
|
||||
line-height: 2em;
|
||||
}
|
||||
|
||||
.positionFixed {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.positionSticky {
|
||||
position: sticky;
|
||||
}
|
||||
|
||||
.positionRelative {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.positionAbsolute {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.noSelect {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.heightMin100VH {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.height100VH {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.height100PC {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.height22PX {
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
.height50PX {
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.height53PX {
|
||||
height: 53px;
|
||||
}
|
||||
|
||||
.height72PX {
|
||||
height: 72px;
|
||||
}
|
||||
|
||||
.height122PX {
|
||||
height: 122px;
|
||||
}
|
||||
|
||||
.height260PX {
|
||||
height: 260px;
|
||||
}
|
||||
|
||||
.height350PX {
|
||||
height: 350px;
|
||||
}
|
||||
|
||||
.width1015PX {
|
||||
width: 1015px;
|
||||
}
|
||||
|
||||
.width645PX {
|
||||
width: 645px;
|
||||
}
|
||||
|
||||
.width400PX {
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.width340PX {
|
||||
width: 340px;
|
||||
}
|
||||
|
||||
.width240PX {
|
||||
width: 240px;
|
||||
}
|
||||
|
||||
.width100PC {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.width72PX {
|
||||
width: 72px;
|
||||
}
|
||||
|
||||
.width50PC {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.maxWidth100PC {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
@media (min-width: 1480px) {
|
||||
.width1015PX { width: 1080px; }
|
||||
.width645PX { width: 700px; }
|
||||
.width340PX { width: 350px; }
|
||||
.width240PX { width: 250px; }
|
||||
.width1015PX {
|
||||
width: 1080px;
|
||||
}
|
||||
|
||||
.width645PX {
|
||||
width: 700px;
|
||||
}
|
||||
|
||||
.width340PX {
|
||||
width: 350px;
|
||||
}
|
||||
|
||||
.width240PX {
|
||||
width: 250px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1160px) and (max-width: 1280px) {
|
||||
.width1015PX { width: 910px; }
|
||||
.width645PX { width: 580px; }
|
||||
.width340PX { width: 300px; }
|
||||
.width240PX { width: 230px; }
|
||||
.width1015PX {
|
||||
width: 910px;
|
||||
}
|
||||
|
||||
.width645PX {
|
||||
width: 580px;
|
||||
}
|
||||
|
||||
.width340PX {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.width240PX {
|
||||
width: 230px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1080px) and (max-width: 1160px) {
|
||||
.width1015PX { width: 850px; }
|
||||
.width645PX { width: 525px; }
|
||||
.width340PX { width: 300px; }
|
||||
.width240PX { width: 210px; }
|
||||
.width1015PX {
|
||||
width: 850px;
|
||||
}
|
||||
|
||||
.width645PX {
|
||||
width: 525px;
|
||||
}
|
||||
|
||||
.width340PX {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.width240PX {
|
||||
width: 210px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 992px) and (max-width: 1080px) {
|
||||
.width1015PX { width: 850px; }
|
||||
.width645PX { width: 525px; }
|
||||
.width340PX { width: 300px; }
|
||||
.width240PX { width: 100px; }
|
||||
.width1015PX {
|
||||
width: 850px;
|
||||
}
|
||||
|
||||
.width645PX {
|
||||
width: 525px;
|
||||
}
|
||||
|
||||
.width340PX {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.width240PX {
|
||||
width: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 0px) and (max-width: 992px) {
|
||||
.width1015PX { width: 600px; }
|
||||
.width645PX { width: 600px; }
|
||||
.width340PX { width: 0px; }
|
||||
.width240PX { width: 100px; }
|
||||
.width1015PX {
|
||||
max-width: 600px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.width645PX {
|
||||
max-width: 600px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.width340PX {
|
||||
width: 0px;
|
||||
}
|
||||
|
||||
.width240PX {
|
||||
width: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.top0 { top: 0; }
|
||||
.top60PC { top: 60%; }
|
||||
.top0 {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.textAlignLeft { text-align: left; }
|
||||
.textAlignCenter { text-align: center; }
|
||||
.top60PC {
|
||||
top: 60%;
|
||||
}
|
||||
|
||||
.fontSize24PX { font-size: 24px; }
|
||||
.fontSize19PX { font-size: 19px; }
|
||||
.fontSize16PX { font-size: 16px; }
|
||||
.fontSize15PX { font-size: 15px; }
|
||||
.fontSize14PX { font-size: 14px; }
|
||||
.fontSize13PX { font-size: 13px; }
|
||||
.fontSize12PX { font-size: 12px; }
|
||||
.textAlignLeft {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.fontWeightNormal { font-weight: 400; }
|
||||
.fontWeightMedium { font-weight: 500; }
|
||||
.fontWeightBold { font-weight: 600; }
|
||||
.fontWeightExtraBold { font-weight: 800; }
|
||||
.textAlignCenter {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.noUnderline { text-decoration: none; }
|
||||
.underline { text-decoration: underline; }
|
||||
.underline_onHover:hover { text-decoration: underline; }
|
||||
.fontSize24PX {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.objectFitCover { object-fit: cover; }
|
||||
.fontSize19PX {
|
||||
font-size: 19px;
|
||||
}
|
||||
|
||||
.z1 { z-index: 1; }
|
||||
.z2 { z-index: 2; }
|
||||
.z3 { z-index: 3; }
|
||||
.z4 { z-index: 4; }
|
||||
.fontSize16PX {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.fontSize15PX {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.fontSize14PX {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.fontSize13PX {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.fontSize12PX {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.fontWeightNormal {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.fontWeightMedium {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.fontWeightBold {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.fontWeightExtraBold {
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.noUnderline {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.underline {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.underline_onHover:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.objectFitCover {
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.shadow {
|
||||
/* todo */
|
||||
}
|
||||
|
||||
.z1 {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.z2 {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.z3 {
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.z4 {
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
.marginVertical5PX {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.marginLeft5PX { margin-left: 5px; }
|
||||
.marginRight2PX { margin-right: 2px; }
|
||||
.marginRight5PX { margin-right: 5px; }
|
||||
.marginLeft5PX {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.marginRight2PX {
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.marginRight5PX {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.marginVertical10PX {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.marginRight10PX { margin-right: 10px; }
|
||||
.marginLeft15PX { margin-left: 15px; }
|
||||
.marginLeftAuto { margin-left: auto; }
|
||||
.marginRightAuto { margin-right: auto; }
|
||||
.marginBottom15PX { margin-bottom: 15px; }
|
||||
.marginBottom10PX { margin-bottom: 10px; }
|
||||
.marginTop10PX { margin-top: 10px; }
|
||||
.marginTop5PX { margin-top: 5px; }
|
||||
.marginTopAuto { margin-top: auto; }
|
||||
.marginTopNeg30PX { margin-top: -30px; }
|
||||
.marginRight10PX {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.paddingTop5625PC { padding-top: 56.25%; }
|
||||
.marginLeft15PX {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.marginLeftAuto {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.marginRightAuto {
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.marginBottom15PX {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.marginBottom10PX {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.marginTop10PX {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.marginTop5PX {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.marginTopAuto {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.marginTopNeg30PX {
|
||||
margin-top: -30px;
|
||||
}
|
||||
|
||||
.marginTopNeg75PX {
|
||||
margin-top: -75px;
|
||||
}
|
||||
|
||||
.paddingTop5625PC {
|
||||
padding-top: 56.25%;
|
||||
}
|
||||
|
||||
.paddingTop10PX {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.paddingHorizontal15PX {
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
.paddingLeft15PX { padding-left: 15px; }
|
||||
.paddingRight15PX { padding-right: 15px; }
|
||||
.paddingLeft15PX {
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.paddingRight15PX {
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
.paddingVertical5PX {
|
||||
padding-top: 5px;
|
||||
|
@ -315,6 +773,10 @@ body {
|
|||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
.paddingBottom15PX {
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
.paddingHorizontal5PX {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
|
@ -328,4 +790,4 @@ body {
|
|||
.paddingHorizontal20PX {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@
|
|||
"build:production": "cross-env RAILS_ENV=production NODE_ENV=production ./bin/webpack",
|
||||
"manage:translations": "node ./config/webpack/translationRunner.js",
|
||||
"start": "node ./streaming/index.js",
|
||||
"storybook": "start-storybook",
|
||||
"test": "${npm_execpath} run test:lint && ${npm_execpath} run test:jest",
|
||||
"test:lint": "eslint --ext=js .",
|
||||
"test:jest": "cross-env NODE_ENV=test jest --coverage",
|
||||
|
@ -74,6 +75,7 @@
|
|||
"@babel/preset-react": "^7.0.0",
|
||||
"@babel/runtime": "^7.3.4",
|
||||
"@clusterws/cws": "^0.14.0",
|
||||
"@storybook/react": "^5.3.14",
|
||||
"array-includes": "^3.0.3",
|
||||
"autoprefixer": "^9.5.1",
|
||||
"axios": "^0.19.0",
|
||||
|
|
Loading…
Reference in New Issue