This commit is contained in:
mgabdev
2020-04-28 01:33:58 -04:00
parent 763694b5ab
commit c3d0d8bde2
87 changed files with 1392 additions and 826 deletions

View File

@@ -165,7 +165,7 @@ class Account extends ImmutablePureComponent {
<NavLink
title={account.get('acct')}
to={`/${account.get('acct')}`}
className={[_s.default, _s.alignItemsStart, _s.noUnderline, _s.px10, _s.flexGrow1].join(' ')}
className={[_s.default, _s.alignItemsStart, _s.noUnderline, _s.px10, _s.overflowHidden, _s.flexNormal].join(' ')}
>
<DisplayName account={account} isMultiline={compact} />
{!compact && actionButton}

View File

@@ -164,13 +164,13 @@ export default class Button extends PureComponent {
underline_onHover: underlineOnHover,
backgroundColorSubtle2Dark_onHover: backgroundColor === COLORS.tertiary || backgroundColor === COLORS.secondary,
backgroundColorBlackOpaque_onHover: backgroundColor === COLORS.black,
backgroundColorBrandDark_onHover: backgroundColor === COLORS.brand,
backgroundColorDangerDark_onHover: backgroundColor === COLORS.danger,
backgroundColorSubtle2Dark_onHover: backgroundColor === COLORS.tertiary || backgroundColor === COLORS.secondary && !isDisabled,
backgroundColorBlackOpaque_onHover: backgroundColor === COLORS.black && !isDisabled,
backgroundColorBrandDark_onHover: backgroundColor === COLORS.brand && !isDisabled,
backgroundColorDangerDark_onHover: backgroundColor === COLORS.danger && !isDisabled,
backgroundColorBrand_onHover: color === COLORS.brand && isOutline,
colorWhite_onHover: !!children && color === COLORS.brand && isOutline,
backgroundColorBrand_onHover: color === COLORS.brand && isOutline && !isDisabled,
colorWhite_onHover: !!children && color === COLORS.brand && isOutline && !isDisabled,
fillColorSecondary: !!icon && color === COLORS.secondary,
fillColorWhite: !!icon && color === COLORS.white,

View File

@@ -27,7 +27,7 @@ class ColumnIndicator extends PureComponent {
render() {
const { type, message, intl } = this.props
const title = type !== 'error' ? intl.formatMessage(messages[type]) : message
const title = type !== 'error' && !message ? intl.formatMessage(messages[type]) : message
return (
<div className={[_s.default, _s.width100PC, _s.justifyContentCenter, _s.alignItemsCenter, _s.py15].join(' ')}>

View File

@@ -60,7 +60,7 @@ class Comment extends ImmutablePureComponent {
<Avatar account={status.get('account')} size={32} />
</NavLink>
<div className={[_s.default, _s.flexNormal].join(' ')}>
<div className={_s.default}>
<div className={[_s.default, _s.px10, _s.pt5, _s.pb10, _s.radiusSmall, _s.backgroundColorSubtle].join(' ')}>
<CommentHeader status={status} />
<StatusContent

View File

@@ -161,7 +161,7 @@ class DisplayName extends ImmutablePureComponent {
/>
{
!noRelationship && account.get('locked') &&
<Icon id='lock-filled' size={iconSize} className={_s.ml5} />
<Icon id='lock-filled' size={iconSize} className={[_s.fillColorPrimary, _s.ml5].join(' ')} />
}
</bdi>
{

View File

@@ -31,8 +31,6 @@ class FloatingActionButton extends PureComponent {
return (
<Button
onClick={onOpenCompose}
color='white'
backgroundColor='brand'
className={[_s.posFixed, _s.z4, _s.py15, _s.mb15, _s.mr15, _s.bottom0, _s.right0].join(' ')}
title={message}
aria-label={message}

View File

@@ -0,0 +1,37 @@
import Text from './text'
export default class Form extends PureComponent {
static propTypes = {
children: PropTypes.any,
errorMessage: PropTypes.string,
onSubmit: PropTypes.func.isRequired,
}
componentDidUpdate (prevProps) {
}
render() {
const {
children,
errorMessage,
onSubmit,
} = this.props
return (
<form onSubmit={onSubmit} className={_s.default}>
{
!!errorMessage &&
<Text color='danger' className={_s.my10}>
{errorMessage}
</Text>
}
<div className={_s.default}>
{children}
</div>
</form>
)
}
}

View File

@@ -4,6 +4,7 @@ import { Fragment } from 'react'
import { NavLink } from 'react-router-dom'
import { defineMessages, injectIntl } from 'react-intl'
import classNames from 'classnames/bind'
import { PLACEHOLDER_MISSING_HEADER_SRC } from '../constants'
import { shortNumberFormat } from '../utils/numbers'
import Button from './button'
import DotTextSeperator from './dot_text_seperator'
@@ -34,10 +35,16 @@ class GroupCollectionItem extends ImmutablePureComponent {
static propTypes = {
group: ImmutablePropTypes.map,
relationships: ImmutablePropTypes.map,
isHidden: PropTypes.bool,
}
render() {
const { intl, group, relationships } = this.props
const {
intl,
group,
relationships,
isHidden,
} = this.props
if (!relationships) return null
@@ -53,7 +60,20 @@ class GroupCollectionItem extends ImmutablePureComponent {
const isMember = relationships.get('member')
const isAdmin = relationships.get('admin')
const coverSrc = group.get('cover')
const coverSrc = group.get('cover_image_url') || ''
const coverMissing = coverSrc.indexOf(PLACEHOLDER_MISSING_HEADER_SRC) > -1 || !coverSrc
if (isHidden) {
return (
<Fragment>
{group.get('title')}
{subtitle}
{isMember && intl.formatMessage(messages.member)}
{isAdmin && intl.formatMessage(messages.admin)}
</Fragment>
)
}
const navLinkClasses = cx({
default: 1,
@@ -77,7 +97,7 @@ class GroupCollectionItem extends ImmutablePureComponent {
className={navLinkClasses}
>
{
!!coverSrc &&
!!coverSrc && !coverMissing &&
<Image
src={coverSrc}
alt={group.get('title')}
@@ -86,7 +106,7 @@ class GroupCollectionItem extends ImmutablePureComponent {
}
{
!coverSrc && (isMember || isAdmin) &&
(!coverSrc || coverMissing) && (isMember || isAdmin) &&
<div className={[_s.default, _s.height40PX, _s.backgroundColorSubtle, _s.borderColorSecondary, _s.borderBottom1PX].join(' ')} />
}

View File

@@ -65,7 +65,7 @@ class GroupHeader extends ImmutablePureComponent {
},
]
const coverSrc = !!group ? group.get('cover') : undefined
const coverSrc = !!group ? group.get('cover_image_url') : undefined
const title = !!group ? group.get('title') : undefined
return (

View File

@@ -4,6 +4,7 @@ import { Fragment } from 'react'
import { NavLink } from 'react-router-dom'
import { defineMessages, injectIntl } from 'react-intl'
import classNames from 'classnames/bind'
import { PLACEHOLDER_MISSING_HEADER_SRC } from '../constants'
import { shortNumberFormat } from '../utils/numbers'
import Image from './image'
import Text from './text'
@@ -31,6 +32,7 @@ class GroupListItem extends ImmutablePureComponent {
relationships: ImmutablePropTypes.map,
slim: PropTypes.bool,
isLast: PropTypes.bool,
isHidden: PropTypes.bool,
}
static defaultProps = {
@@ -39,9 +41,16 @@ class GroupListItem extends ImmutablePureComponent {
}
render() {
const { intl, group, relationships, slim, isLast } = this.props
const {
intl,
group,
relationships,
slim,
isLast,
isHidden,
} = this.props
if (!relationships) return null
if (!relationships || !group) return null
const unreadCount = relationships.get('unread_count')
@@ -53,6 +62,15 @@ class GroupListItem extends ImmutablePureComponent {
</Fragment>
) : intl.formatMessage(messages.no_recent_activity)
if (isHidden) {
return (
<Fragment>
{group.get('title')}
{subtitle}
</Fragment>
)
}
const containerClasses = cx({
default: 1,
noUnderline: 1,
@@ -80,10 +98,12 @@ class GroupListItem extends ImmutablePureComponent {
default: 1,
px10: 1,
mt5: 1,
flexShrink1: slim,
mb10: !slim,
})
const coverSrc = group.get('cover')
const coverSrc = group.get('cover_image_url') || ''
const coverMissing = coverSrc.indexOf(PLACEHOLDER_MISSING_HEADER_SRC) > -1 || !coverSrc
return (
<NavLink
@@ -92,7 +112,7 @@ class GroupListItem extends ImmutablePureComponent {
>
{
(!!coverSrc || slim) &&
(!!coverSrc || slim) && !coverMissing &&
<Image
src={coverSrc}
alt={group.get('title')}

View File

@@ -11,6 +11,7 @@ import ChatIcon from '../assets/chat_icon'
import CircleIcon from '../assets/circle_icon'
import CloseIcon from '../assets/close_icon'
import CodeIcon from '../assets/code_icon'
import CogIcon from '../assets/cog_icon'
import CommentIcon from '../assets/comment_icon'
import CopyIcon from '../assets/copy_icon'
import DissenterIcon from '../assets/dissenter_icon'
@@ -80,6 +81,7 @@ const ICONS = {
'chat': ChatIcon,
'close': CloseIcon,
'code': CodeIcon,
'cog': CogIcon,
'comment': CommentIcon,
'copy': CopyIcon,
'dissenter': DissenterIcon,

View File

@@ -7,6 +7,7 @@ import Text from './text'
const cx = classNames.bind(_s)
export default class Input extends PureComponent {
static propTypes = {
placeholder: PropTypes.string,
prependIcon: PropTypes.string,
@@ -25,6 +26,10 @@ export default class Input extends PureComponent {
hideLabel: PropTypes.bool,
}
handleOnChange = (e) => {
this.props.onChange(e.target.value)
}
render() {
const {
placeholder,
@@ -55,6 +60,7 @@ export default class Input extends PureComponent {
py5: small,
backgroundTransparent: !readOnly,
backgroundColorSubtle2: readOnly,
colorPrimary: !readOnly,
colorSecondary: readOnly,
fontSize15PX: !small,
fontSize13PX: small,
@@ -101,7 +107,7 @@ export default class Input extends PureComponent {
placeholder={placeholder}
ref={inputRef}
value={value}
onChange={onChange}
onChange={this.handleOnChange}
onKeyUp={onKeyUp}
onFocus={onFocus}
onBlur={onBlur}

View File

@@ -1,3 +1,4 @@
import { Fragment } from 'react'
import classNames from 'classnames/bind'
import Button from './button'
import Icon from './icon'
@@ -9,6 +10,7 @@ export default class ListItem extends PureComponent {
static propTypes = {
icon: PropTypes.string,
isLast: PropTypes.bool,
isHidden: PropTypes.bool,
to: PropTypes.string,
href: PropTypes.string,
title: PropTypes.string,
@@ -29,8 +31,17 @@ export default class ListItem extends PureComponent {
size,
icon,
hideArrow,
isHidden,
} = this.props
if (isHidden) {
return (
<Fragment>
{title}
</Fragment>
)
}
const small = size === 'small'
const large = size === 'large'
@@ -77,7 +88,7 @@ export default class ListItem extends PureComponent {
/>
}
<Text color='primary' size={textSize}>
<Text color='primary' size={textSize} className={[_s.overflowHidden, _s.flexNormal, _s.pr5, _s.textOverflowEllipsis].join(' ')}>
{title}
</Text>
@@ -86,7 +97,7 @@ export default class ListItem extends PureComponent {
<Icon
id='angle-right'
size='10px'
className={[_s.mlAuto, _s.fillColorSecondary].join(' ')}
className={[_s.mlAuto, _s.fillColorSecondary, _s.flexShrink1].join(' ')}
/>
}
</Button>

View File

@@ -1,6 +1,5 @@
import { injectIntl, defineMessages } from 'react-intl'
import Button from './button'
import Icon from './icon'
import Text from './text'
const messages = defineMessages({
@@ -15,8 +14,6 @@ class LoadMore extends PureComponent {
onClick: PropTypes.func,
disabled: PropTypes.bool,
visible: PropTypes.bool,
maxId: PropTypes.string,
gap: PropTypes.bool,
intl: PropTypes.object.isRequired,
}
@@ -24,13 +21,16 @@ class LoadMore extends PureComponent {
visible: true,
}
handleClick = () => {
const { gap, maxId } = this.props
this.props.onClick(gap ? maxId : undefined)
handleClick = (e) => {
this.props.onClick()
}
render() {
const { disabled, visible, gap, intl } = this.props
const {
disabled,
visible,
intl,
} = this.props
return (
<div className={[_s.default, _s.py10, _s.px10].join(' ')}>
@@ -40,22 +40,15 @@ class LoadMore extends PureComponent {
backgroundColor='tertiary'
color='primary'
disabled={disabled || !visible}
style={{ visibility: visible ? 'visible' : 'hidden' }}
style={{
visibility: visible ? 'visible' : 'hidden',
}}
onClick={this.handleClick}
aria-label={intl.formatMessage(messages.load_more)}
>
{
!gap &&
<Text color='inherit' align='center'>
{intl.formatMessage(messages.load_more)}
</Text>
}
{
gap &&
<Text align='center'>
<Icon id='ellipsis' size='14px' />
</Text>
}
<Text color='inherit' align='center'>
{intl.formatMessage(messages.load_more)}
</Text>
</Button>
</div>
)

View File

@@ -13,13 +13,14 @@ const cx = classNames.bind(_s)
export default class MediaItem extends ImmutablePureComponent {
static propTypes = {
account: ImmutablePropTypes.map.isRequired,
attachment: ImmutablePropTypes.map.isRequired,
small: PropTypes.bool
isSmall: PropTypes.bool,
}
state = {
visible: displayMedia !== 'hide_all' && !this.props.attachment.getIn(['status', 'sensitive']) || displayMedia === 'show_all',
loaded: false,
visible: displayMedia !== 'hide_all' && !this.props.attachment.getIn(['status', 'sensitive']) || displayMedia === 'show_all',
}
componentDidMount() {
@@ -59,7 +60,11 @@ export default class MediaItem extends ImmutablePureComponent {
}
render() {
const { attachment, small } = this.props
const {
account,
attachment,
isSmall,
} = this.props
const { visible, loaded } = this.state
const status = attachment.get('status')
@@ -81,8 +86,8 @@ export default class MediaItem extends ImmutablePureComponent {
top0: 1,
height100PC: 1,
width100PC: 1,
py5: !small,
px5: !small,
py2: !isSmall,
px2: !isSmall,
})
const linkClasses = cx({
@@ -91,15 +96,16 @@ export default class MediaItem extends ImmutablePureComponent {
height100PC: 1,
overflowHidden: 1,
border1PX: 1,
borderColorPrimary: !small,
borderColorPrimary: small,
borderColorPrimary: 1,
})
const statusUrl = `/${account.getIn(['acct'])}/posts/${status.get('id')}`;
return (
<div className={[_s.default, _s.width25PC, _s.pt25PC].join(' ')}>
<div className={containerClasses}>
<NavLink
to={status.get('url')} /* : todo : */
to={statusUrl}
title={title}
className={linkClasses}
>

View File

@@ -51,7 +51,7 @@ class CommunityTimelineSettingsModal extends ImmutablePureComponent {
return (
<ModalLayout
width='320'
width={320}
title={intl.formatMessage(messages.title)}
>

View File

@@ -0,0 +1,58 @@
import { injectIntl, defineMessages } from 'react-intl'
import { muteAccount } from '../../actions/accounts'
const messages = defineMessages({
muteMessage: { id: 'confirmations.mute.message', defaultMessage: 'Are you sure you want to mute {name}?' },
cancel: { id: 'confirmation_modal.cancel', defaultMessage: 'Cancel' },
title: { id: 'display_options', defaultMessage: 'Display Options' },
})
const mapStateToProps = (state) => ({
})
const mapDispatchToProps = (dispatch) => ({
})
export default
@connect(mapStateToProps, mapDispatchToProps)
@injectIntl
class DisplayOptionsModal extends PureComponent {
static propTypes = {
isSubmitting: PropTypes.bool.isRequired,
account: PropTypes.object.isRequired,
onConfirm: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
}
componentDidMount() {
this.button.focus()
}
handleClick = () => {
this.props.onClose()
this.props.onConfirm(this.props.account, this.props.notifications)
}
handleCancel = () => {
this.props.onClose()
}
// document.documentElement.style.setProperty("--color-surface", "black");
render() {
const { account, intl } = this.props
return (
<ModalLayout
width={320}
title={intl.formatMessage(messages.title)}
>
</ModalLayout>
)
}
}

View File

@@ -28,10 +28,11 @@ class EditProfileModal extends ImmutablePureComponent {
static propTypes = {
account: ImmutablePropTypes.map,
intl: PropTypes.object.isRequired,
onClose: PropTypes.func.isRequired,
}
render() {
const { account, intl } = this.props
const { account, intl, onClose } = this.props
const headerSrc = !!account ? account.get('header') : ''
@@ -40,6 +41,7 @@ class EditProfileModal extends ImmutablePureComponent {
title={intl.formatMessage(messages.edit_profile)}
noPadding
width={460}
onClose={onClose}
>
<div className={[_s.default, _s.py5, _s.px5, _s.width100PC, _s.overflowHidden].join(' ')}>
<Image

View File

@@ -4,7 +4,7 @@ import ModalLayout from './modal_layout'
import GroupCreate from '../../features/group_create'
const messages = defineMessages({
title: { id: 'create_group', defaultMessage: 'Create Group' },
title: { id: 'create_group', defaultMessage: 'Create group' },
})
export default
@@ -22,7 +22,7 @@ class GroupCreateModal extends ImmutablePureComponent {
return (
<ModalLayout
title={intl.formatMessage(messages.title)}
width='440'
width={440}
onClose={onClose}
>
<GroupCreate onCloseModal={onClose} />

View File

@@ -54,7 +54,7 @@ class HashtagTimelineSettingsModal extends ImmutablePureComponent {
return (
<ModalLayout
width='320'
width={320}
title={intl.formatMessage(messages.title)}
onClose={onClose}
>

View File

@@ -55,7 +55,7 @@ class HomeTimelineSettingsModal extends ImmutablePureComponent {
return (
<ModalLayout
width='320'
width={320}
title={intl.formatMessage(messages.title)}
onClose={onClose}
>

View File

@@ -22,10 +22,10 @@ class ListCreateModal extends ImmutablePureComponent {
return (
<ModalLayout
title={intl.formatMessage(messages.title)}
width='500'
width={500}
onClose={onClose}
>
<ListCreate />
<ListCreate isModal />
</ModalLayout>
)
}

View File

@@ -1,6 +1,5 @@
import { injectIntl, defineMessages } from 'react-intl'
import { makeGetAccount } from '../../selectors'
import { blockAccount } from '../../actions/accounts'
import { deleteList } from '../../actions/lists'
import ConfirmationModal from './confirmation_modal'
const messages = defineMessages({
@@ -10,14 +9,14 @@ const messages = defineMessages({
})
const mapDispatchToProps = (dispatch) => ({
onConfirm(account) {
// dispatch(blockAccount(account.get('id')))
onConfirm(listId) {
dispatch(deleteList(listId))
},
})
export default
@connect(null, mapDispatchToProps)
@injectIntl
@connect(null, mapDispatchToProps)
class ListDeleteModal extends PureComponent {
static propTypes = {
@@ -27,17 +26,17 @@ class ListDeleteModal extends PureComponent {
}
handleClick = () => {
this.props.onConfirm(this.props.account)
this.props.onConfirm(this.props.list.get('id'))
}
render() {
const { list, intl, onClose } = this.props
const title = intl.formatMessage(messages.title, {
list: !!list ? account.get('title') : '',
list: !!list ? list.get('title') : '',
})
const message = intl.formatMessage(messages.listMessage, {
list: !!list ? account.get('title') : '',
list: !!list ? list.get('title') : '',
})
return (

View File

@@ -1,68 +1,33 @@
import { injectIntl, defineMessages } from 'react-intl'
import { muteAccount } from '../../actions/accounts'
import { defineMessages, injectIntl } from 'react-intl'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ModalLayout from './modal_layout'
import ListEdit from '../../features/list_edit'
const messages = defineMessages({
muteMessage: { id: 'confirmations.mute.message', defaultMessage: 'Are you sure you want to mute {name}?' },
cancel: { id: 'confirmation_modal.cancel', defaultMessage: 'Cancel' },
confirm: { id: 'confirmations.mute.confirm', defaultMessage: 'Mute' },
})
const mapStateToProps = (state) => ({
isSubmitting: state.getIn(['reports', 'new', 'isSubmitting']),
account: state.getIn(['mutes', 'new', 'account']),
})
const mapDispatchToProps = (dispatch) => ({
onConfirm(account, notifications) {
dispatch(muteAccount(account.get('id'), notifications))
},
title: { id: 'lists.edit', defaultMessage: 'Edit list' },
})
export default
@connect(mapStateToProps, mapDispatchToProps)
@injectIntl
class UnfollowModal extends PureComponent {
class ListEditorModal extends ImmutablePureComponent {
static propTypes = {
isSubmitting: PropTypes.bool.isRequired,
account: PropTypes.object.isRequired,
onConfirm: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
}
componentDidMount() {
this.button.focus()
}
handleClick = () => {
this.props.onClose()
this.props.onConfirm(this.props.account, this.props.notifications)
}
handleCancel = () => {
this.props.onClose()
onClose: PropTypes.func.isRequired,
listId: PropTypes.string.isRequired,
}
render() {
const { account, intl } = this.props
// , {
// message: <FormattedMessage id='confirmations.unfollow.message' defaultMessage='Are you sure you want to unfollow {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
// confirm: intl.formatMessage(messages.unfollowConfirm),
// onConfirm: () => dispatch(unfollowAccount(account.get('id'))),
// }));
const { intl, onClose, listId } = this.props
return (
<ConfirmationModal
title={`Mute @${account.get('acct')}`}
message={<FormattedMessage id='confirmations.mute.message' defaultMessage='Are you sure you want to mute @{name}?' values={{ name: account.get('acct') }} />}
confirm={<FormattedMessage id='mute' defaultMessage='Mute' />}
onConfirm={() => {
// dispatch(blockDomain(domain))
// dispatch(blockDomain(domain))
}}
/>
<ModalLayout
title={intl.formatMessage(messages.title)}
width={500}
onClose={onClose}
>
<ListEdit listId={listId} />
</ModalLayout>
)
}
}

View File

@@ -55,7 +55,7 @@ class ListTimelineSettingsModal extends ImmutablePureComponent {
return (
<ModalLayout
width='320'
width={320}
title={intl.formatMessage(messages.title)}
onClose={onClose}
>

View File

@@ -17,7 +17,6 @@ class ModalLayout extends PureComponent {
title: PropTypes.string,
children: PropTypes.node,
onClose: PropTypes.func.isRequired,
handleCloseModal: PropTypes.func.isRequired,
width: PropTypes.number,
hideClose: PropTypes.bool,
noPadding: PropTypes.bool,

View File

@@ -2,73 +2,101 @@ import { closeModal } from '../../actions/modal'
import { cancelReplyCompose } from '../../actions/compose'
import Bundle from '../../features/ui/util/bundle'
import {
MuteModal,
ReportModal,
MODAL_ACTIONS,
MODAL_BLOCK_ACCOUNT,
MODAL_BLOCK_DOMAIN,
MODAL_BOOST,
MODAL_COMMUNITY_TIMELINE_SETTINGS,
MODAL_COMPOSE,
MODAL_CONFIRM,
MODAL_DISPLAY_OPTIONS,
MODAL_EDIT_PROFILE,
MODAL_EMBED,
MODAL_GIF_PICKER,
MODAL_GROUP_CREATE,
MODAL_GROUP_DELETE,
MODAL_GROUP_EDITOR,
MODAL_HASHTAG_TIMELINE_SETTINGS,
MODAL_HOME_TIMELINE_SETTINGS,
MODAL_HOTKEYS,
MODAL_LIST_CREATE,
MODAL_LIST_DELETE,
MODAL_LIST_EDITOR,
MODAL_LIST_TIMELINE_SETTINGS,
MODAL_MEDIA,
MODAL_MUTE,
MODAL_PRO_UPGRADE,
MODAL_REPORT,
MODAL_STATUS_REVISIONS,
MODAL_UNAUTHORIZED,
MODAL_UNFOLLOW,
MODAL_VIDEO,
} from '../../constants'
import {
ActionsModal,
BlockAccountModal,
BlockDomainModal,
BoostModal,
CommunityTimelineSettingsModal,
ComposeModal,
ConfirmationModal,
DisplayOptionsModal,
EditProfileModal,
EmbedModal,
// ListEditor,
// ListAdder,
GifPickerModal,
GroupCreateModal,
GroupDeleteModal,
GroupEditorModal,
HashtagTimelineSettingsModal,
HomeTimelineSettingsModal,
HotkeysModal,
ListCreateModal,
ListDeleteModal,
ListEditorModal,
ListTimelineSettingsModal,
MediaModal,
MuteModal,
ProUpgradeModal,
ReportModal,
StatusRevisionsModal,
UnauthorizedModal,
UnfollowModal,
VideoModal,
} from '../../features/ui/util/async_components'
import ModalBase from './modal_base'
import BundleModalError from '../bundle_modal_error'
import ActionsModal from './actions_modal'
import BlockAccountModal from './block_account_modal'
import BlockDomainModal from './block_domain_modal'
import BoostModal from './boost_modal'
import CommunityTimelineSettingsModal from './community_timeline_settings_modal'
import ComposeModal from './compose_modal'
import ConfirmationModal from './confirmation_modal'
import EditProfileModal from './edit_profile_modal'
import GifPickerModal from './gif_picker_modal'
import GroupCreateModal from './group_create_modal'
import GroupDeleteModal from './group_delete_modal'
import GroupEditorModal from './group_editor_modal'
import HashtagTimelineSettingsModal from './hashtag_timeline_settings_modal'
import HomeTimelineSettingsModal from './home_timeline_settings_modal'
import HotkeysModal from './hotkeys_modal'
import ListCreateModal from './list_create_modal'
import ListDeleteModal from './list_delete_modal'
import ListEditorModal from './list_editor_modal'
import ListTimelineSettingsModal from './list_timeline_settings_modal'
import MediaModal from './media_modal'
import ModalLoading from './modal_loading'
import ProUpgradeModal from './pro_upgrade_modal'
import VideoModal from './video_modal'
import UnauthorizedModal from './unauthorized_modal'
import UnfollowModal from './unfollow_modal'
const MODAL_COMPONENTS = {
ACTIONS: () => Promise.resolve({ default: ActionsModal }),
BLOCK_ACCOUNT: () => Promise.resolve({ default: BlockAccountModal }),
BLOCK_DOMAIN: () => Promise.resolve({ default: BlockDomainModal }),
BOOST: () => Promise.resolve({ default: BoostModal }),
COMMUNITY_TIMELINE_SETTINGS: () => Promise.resolve({ default: CommunityTimelineSettingsModal }),
COMPOSE: () => Promise.resolve({ default: ComposeModal }),
CONFIRM: () => Promise.resolve({ default: ConfirmationModal }),
EDIT_PROFILE: () => Promise.resolve({ default: EditProfileModal }),
EMBED: () => Promise.resolve({ default: EmbedModal }),
GIF_PICKER: () => Promise.resolve({ default: GifPickerModal }),
GROUP_CREATE: () => Promise.resolve({ default: GroupCreateModal }),
GROUP_DELETE: () => Promise.resolve({ default: GroupDeleteModal }),
GROUP_EDITOR: () => Promise.resolve({ default: GroupEditorModal }),
HASHTAG_TIMELINE_SETTINGS: () => Promise.resolve({ default: HashtagTimelineSettingsModal }),
HOME_TIMELINE_SETTINGS: () => Promise.resolve({ default: HomeTimelineSettingsModal }),
HOTKEYS: () => Promise.resolve({ default: HotkeysModal }),
LIST_CREATE: () => Promise.resolve({ default: ListCreateModal }),
LIST_DELETE: () => Promise.resolve({ default: ListDeleteModal }),
LIST_EDITOR: () => Promise.resolve({ default: ListEditorModal }),
LIST_TIMELINE_SETTINGS: () => Promise.resolve({ default: ListTimelineSettingsModal }),
MEDIA: () => Promise.resolve({ default: MediaModal }),
MUTE: MuteModal,
PRO_UPGRADE: () => Promise.resolve({ default: ProUpgradeModal }),
REPORT: ReportModal,
STATUS_REVISIONS: StatusRevisionsModal,
UNAUTHORIZED: () => Promise.resolve({ default: UnauthorizedModal }),
UNFOLLOW: () => Promise.resolve({ default: UnfollowModal }),
VIDEO: () => Promise.resolve({ default: VideoModal }),
}
const MODAL_COMPONENTS = {}
MODAL_COMPONENTS[MODAL_ACTIONS] = ActionsModal
MODAL_COMPONENTS[MODAL_BLOCK_ACCOUNT] = BlockAccountModal
MODAL_COMPONENTS[MODAL_BLOCK_DOMAIN] = BlockDomainModal
MODAL_COMPONENTS[MODAL_BOOST] = BoostModal
MODAL_COMPONENTS[MODAL_COMMUNITY_TIMELINE_SETTINGS] = CommunityTimelineSettingsModal
MODAL_COMPONENTS[MODAL_COMPOSE] = ComposeModal
MODAL_COMPONENTS[MODAL_CONFIRM] = ConfirmationModal
MODAL_COMPONENTS[MODAL_DISPLAY_OPTIONS] = DisplayOptionsModal
MODAL_COMPONENTS[MODAL_EDIT_PROFILE] = EditProfileModal
MODAL_COMPONENTS[MODAL_EMBED] = EmbedModal
MODAL_COMPONENTS[MODAL_GIF_PICKER] = GifPickerModal
MODAL_COMPONENTS[MODAL_GROUP_CREATE] = GroupCreateModal
MODAL_COMPONENTS[MODAL_GROUP_DELETE] = GroupDeleteModal
MODAL_COMPONENTS[MODAL_GROUP_EDITOR] = GroupEditorModal
MODAL_COMPONENTS[MODAL_HASHTAG_TIMELINE_SETTINGS] = HashtagTimelineSettingsModal
MODAL_COMPONENTS[MODAL_HOME_TIMELINE_SETTINGS] = HomeTimelineSettingsModal
MODAL_COMPONENTS[MODAL_HOTKEYS] = HotkeysModal
MODAL_COMPONENTS[MODAL_LIST_CREATE] = ListCreateModal
MODAL_COMPONENTS[MODAL_LIST_DELETE] = ListDeleteModal
MODAL_COMPONENTS[MODAL_LIST_EDITOR] = ListEditorModal
MODAL_COMPONENTS[MODAL_LIST_TIMELINE_SETTINGS] = ListTimelineSettingsModal
MODAL_COMPONENTS[MODAL_MEDIA] = MediaModal
MODAL_COMPONENTS[MODAL_MUTE] = MuteModal
MODAL_COMPONENTS[MODAL_PRO_UPGRADE] = ProUpgradeModal
MODAL_COMPONENTS[MODAL_REPORT] = ReportModal
MODAL_COMPONENTS[MODAL_STATUS_REVISIONS] = StatusRevisionsModal
MODAL_COMPONENTS[MODAL_UNAUTHORIZED] = UnauthorizedModal
MODAL_COMPONENTS[MODAL_UNFOLLOW] = UnfollowModal
MODAL_COMPONENTS[MODAL_VIDEO] = VideoModal
const mapStateToProps = (state) => ({
type: state.getIn(['modal', 'modalType']),

View File

@@ -25,7 +25,7 @@ class ProUpgradeModal extends ImmutablePureComponent {
return (
<ModalLayout
title={intl.formatMessage(messages.title)}
width='460'
width={460}
onClose={onClose}
>
<Text>

View File

@@ -5,7 +5,6 @@ import classNames from 'classnames/bind'
import { loadStatusRevisions } from '../../actions/status_revisions'
import ModalLayout from './modal_layout'
import RelativeTimestamp from '../relative_timestamp'
import ScrollableList from '../scrollable_list'
import Text from '../text'
const cx = classNames.bind(_s)
@@ -56,42 +55,40 @@ class StatusRevisionsModal extends ImmutablePureComponent {
return (
<ModalLayout
title={intl.formatMessage(messages.title)}
width='480'
width={480}
onClose={onClose}
>
<div className={[_s.default]}>
<ScrollableList>
{
revisions.map((revision, i) => {
const isFirst = i === 0
const isLast = i === revisions.size - 1
{
revisions.map((revision, i) => {
const isFirst = i === 0
const isLast = i === revisions.size - 1
const containerClasses = cx({
default: 1,
pt5: 1,
pb10: 1,
mt5: !isFirst,
borderColorSecondary: !isLast,
borderBottom1PX: !isLast,
})
return (
<div key={`status-revision-${i}`} className={containerClasses}>
<div className={[_s.default, _s.pb5].join(' ')}>
<Text size='medium'>
{revision.get('text')}
</Text>
</div>
<div className={[_s.default]}>
<Text size='small' color='secondary'>
Edited on <RelativeTimestamp timestamp={revision.get('created_at')} />
</Text>
</div>
</div>
)
const containerClasses = cx({
default: 1,
pt5: 1,
pb10: 1,
mt5: !isFirst,
borderColorSecondary: !isLast,
borderBottom1PX: !isLast,
})
}
</ScrollableList>
return (
<div key={`status-revision-${i}`} className={containerClasses}>
<div className={[_s.default, _s.pb5].join(' ')}>
<Text size='medium'>
{revision.get('text')}
</Text>
</div>
<div className={[_s.default]}>
<Text size='small' color='secondary'>
Edited on <RelativeTimestamp timestamp={revision.get('created_at')} />
</Text>
</div>
</div>
)
})
}
</div>
</ModalLayout>
)

View File

@@ -43,10 +43,18 @@ class Notification extends ImmutablePureComponent {
createdAt: PropTypes.string,
statusId: PropTypes.string,
type: PropTypes.string.isRequired,
isHidden: PropTypes.bool,
}
render() {
const { intl, accounts, createdAt, type, statusId } = this.props
const {
intl,
accounts,
createdAt,
type,
statusId,
isHidden,
} = this.props
const count = !!accounts ? accounts.size : 0
@@ -83,6 +91,10 @@ class Notification extends ImmutablePureComponent {
return null
}
if (isHidden) {
// : todo :
}
return (
<div className={[_s.default, _s.px10, _s.cursorPointer, _s.backgroundColorSubtle_onHover].join(' ')}>
<div className={[_s.default, _s.borderBottom1PX, _s.borderColorSecondary].join(' ')}>

View File

@@ -1,5 +1,5 @@
import { Fragment } from 'react'
import { defineMessages, injectIntl } from 'react-intl'
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import { shortNumberFormat } from '../../utils/numbers'
@@ -9,6 +9,7 @@ import Divider from '../divider'
import Heading from '../heading'
import Icon from '../icon'
import Text from '../text'
import RelativeTimestamp from '../relative_timestamp'
const messages = defineMessages({
title: { id: 'about', defaultMessage: 'About' },
@@ -27,15 +28,19 @@ class GroupInfoPanel extends ImmutablePureComponent {
render() {
const { intl, group } = this.props
console.log("group:", group)
return (
<PanelLayout title={intl.formatMessage(messages.title)}>
{
!!group &&
<Fragment>
<Heading size='h2'>
{group.get('title')}
</Heading>
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}>
<Text weight='medium'>
{group.get('title')}
</Text>
</div>
<Divider isSmall />
@@ -63,6 +68,23 @@ class GroupInfoPanel extends ImmutablePureComponent {
<Divider isSmall />
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}>
<Icon id='calendar' size='12px' className={_s.fillColorSecondary} />
<Text
size='small'
color='secondary'
className={_s.ml5}
>
{
<FormattedMessage id='lists.panel_created' defaultMessage='Created: {date}' values={{
date: <RelativeTimestamp timestamp={group.get('created_at')} />,
}} />
}
</Text>
</div>
<Divider isSmall />
<Text>
{group.get('description')}
</Text>

View File

@@ -1,9 +1,10 @@
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { defineMessages, injectIntl } from 'react-intl'
import { fetchGroups } from '../../actions/groups'
import PanelLayout from './panel_layout'
import GroupListItem from '../group_list_item'
import Button from '../button'
import ScrollableList from '../scrollable_list'
const messages = defineMessages({
title: { id: 'groups.sidebar-panel.title', defaultMessage: 'Groups you\'re in' },
@@ -15,13 +16,41 @@ const mapStateToProps = (state) => ({
groupIds: state.getIn(['group_lists', 'member']),
})
const mapDispatchToProps = (dispatch) => ({
onFetchGroups: (type) => {
dispatch(fetchGroups(type))
}
})
export default
@connect(mapStateToProps)
@connect(mapStateToProps, mapDispatchToProps)
@injectIntl
class GroupSidebarPanel extends ImmutablePureComponent {
static propTypes = {
groupIds: ImmutablePropTypes.list,
slim: PropTypes.bool,
isLazy: PropTypes.bool,
isSlim: PropTypes.bool,
onFetchGroups: PropTypes.func.isRequired,
}
state = {
fetched: false,
}
static getDerivedStateFromProps(nextProps, prevState) {
if (!nextProps.isHidden && nextProps.isIntersecting && !prevState.fetched) {
return {
fetched: true
}
}
return null
}
componentDidUpdate(prevProps, prevState, snapshot) {
if (!prevState.fetched && this.state.fetched && this.props.isLazy) {
this.props.onFetchGroups('member')
}
}
render() {
@@ -42,16 +71,20 @@ class GroupSidebarPanel extends ImmutablePureComponent {
noPadding={slim}
>
<div className={_s.default}>
{
groupIds.slice(0, maxCount).map((groupId, i) => (
<GroupListItem
key={`group-panel-item-${groupId}`}
id={groupId}
slim={slim}
isLast={groupIds.length - 1 === i}
/>
))
}
<ScrollableList
scrollKey='groups-panel'
>
{
groupIds.slice(0, maxCount).map((groupId, i) => (
<GroupListItem
key={`group-panel-item-${groupId}`}
id={groupId}
slim={slim}
isLast={groupIds.length - 1 === i}
/>
))
}
</ScrollableList>
</div>
</PanelLayout>
)

View File

@@ -1,57 +1,48 @@
import { defineMessages, injectIntl } from 'react-intl'
import { fetchSuggestions, dismissSuggestion } from '../../actions/suggestions'
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import PanelLayout from './panel_layout'
import Avatar from '../avatar'
import Divider from '../divider'
import Icon from '../icon'
import Heading from '../heading'
import RelativeTimestamp from '../relative_timestamp'
import Text from '../text'
const messages = defineMessages({
dismissSuggestion: { id: 'suggestions.dismiss', defaultMessage: 'Dismiss suggestion' },
memberCount: { id: 'lists.panel_members', defaultMessage: 'Members: {count}' },
createdAt: { id: 'lists.panel_created', defaultMessage: 'Created: {date}' },
title: { id: 'lists_information', defaultMessage: 'List Information' },
edit: { id: 'edit', defaultMessage: 'Edit' },
})
const mapStateToProps = (state) => ({
// accountIds: state.getIn(['listEditor', 'accounts', 'items']),
})
const mapDispatchToProps = (dispatch) => ({
})
export default
@connect(mapStateToProps, mapDispatchToProps)
@injectIntl
class ListDetailsPanel extends ImmutablePureComponent {
static propTypes = {
intl: PropTypes.object.isRequired,
onEdit: PropTypes.func.isRequired,
list: ImmutablePropTypes.map,
}
handleShowAllLists() {
handleOnEdit = () => {
this.props.onEdit()
}
render() {
const { intl } = this.props
const { intl, list } = this.props
const title = !!list ? list.get('title') : ''
const createdAt = !!list ? list.get('created_at') : ''
return (
<PanelLayout
title={intl.formatMessage(messages.title)}
headerButtonTitle={intl.formatMessage(messages.edit)}
headerButtonAction={this.handleShowAllLists}
headerButtonAction={this.handleOnEdit}
>
<div className={_s.default}>
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter,].join(' ')}>
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}>
<Text weight='medium'>
Some List Title
{title}
</Text>
</div>
@@ -65,42 +56,13 @@ class ListDetailsPanel extends ImmutablePureComponent {
className={_s.ml5}
>
{
intl.formatMessage(messages.createdAt, {
date: '12-25-2019'
})
<FormattedMessage id='lists.panel_created' defaultMessage='Created: {date}' values={{
date: <RelativeTimestamp timestamp={createdAt} />,
}} />
}
</Text>
</div>
<Divider isSmall />
<div className={[_s.default].join(' ')}>
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}>
<Icon id='group' size='12px' className={_s.fillColorSecondary} />
<Text
size='small'
color='secondary'
className={_s.ml5}
>
{
intl.formatMessage(messages.memberCount, {
count: 10
})
}
</Text>
</div>
<div className={[_s.default, _s.flexRow, _s.flexWrap, _s.pt10].join(' ')}>
{
[1, 2, 3, 4, 5, 6, 7, 8, 9].map(item => (
<div className={[_s.default, _s.mr5].join(' ')}>
<Avatar size={26} />
</div>
))
}
</div>
</div>
</div>
</PanelLayout>
)

View File

@@ -32,8 +32,28 @@ class ListsPanel extends ImmutablePureComponent {
intl: PropTypes.object.isRequired,
}
componentWillMount() {
this.props.onFetchLists()
state = {
fetched: false,
}
updateOnProps = [
'lists',
]
static getDerivedStateFromProps(nextProps, prevState) {
if (!nextProps.isHidden && nextProps.isIntersecting && !prevState.fetched) {
return {
fetched: true,
}
}
return null
}
componentDidUpdate(prevProps, prevState, snapshot) {
if (!prevState.fetched && this.state.fetched) {
this.props.onFetchLists()
}
}
render() {

View File

@@ -66,11 +66,12 @@ class MediaGalleryPanel extends ImmutablePureComponent {
>
<div className={[_s.default, _s.flexRow, _s.flexWrap, _s.px10, _s.py10].join(' ')}>
{
attachments.slice(0, 16).map((attachment) => (
attachments.slice(0, 16).map((attachment, i) => (
<MediaItem
small
isSmall
key={attachment.get('id')}
attachment={attachment}
account={account}
/>
))
}

View File

@@ -9,7 +9,7 @@ import SettingSwitch from '../setting_switch'
const messages = defineMessages({
title: { id: 'notification_filters', defaultMessage: 'Notification Filters' },
onlyVerified: { id: 'notification_only_verified', defaultMessage: 'Only Verified Users' },
onlyFollowing: { id: 'notification_only_following', defaultMessage: 'Only People I Follow' },
// onlyFollowing: { id: 'notification_only_following', defaultMessage: 'Only People I Follow' },
})
const mapStateToProps = (state) => ({
@@ -55,13 +55,14 @@ class NotificationFilterPanel extends ImmutablePureComponent {
label={intl.formatMessage(messages.onlyVerified)}
/>
<SettingSwitch
{ /* : todo :
<SettingSwitch
prefix='notification'
settings={settings}
settingPath={'onlyFollowing'}
onChange={onChange}
label={intl.formatMessage(messages.onlyFollowing)}
/>
/> */ }
</PanelLayout>
)
}

View File

@@ -4,6 +4,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'
import { fetchGabTrends } from '../../actions/gab_trends'
import PanelLayout from './panel_layout'
import ColumnIndicator from '../column_indicator'
import ScrollableList from '../scrollable_list'
import TrendingItem from '../trends_item'
const messages = defineMessages({
@@ -29,7 +30,7 @@ class TrendsPanel extends ImmutablePureComponent {
onFetchGabTrends: PropTypes.func.isRequired,
}
componentWillMount() {
componentDidMount() {
this.props.onFetchGabTrends()
}
@@ -47,19 +48,26 @@ class TrendsPanel extends ImmutablePureComponent {
<ColumnIndicator type='loading' />
}
{
gabtrends && gabtrends.slice(0, 8).map((trend, i) => (
<TrendingItem
key={`gab-trend-${i}`}
index={i + 1}
isLast={i === 7}
url={trend.get('url')}
title={trend.get('title')}
description={trend.get('description')}
imageUrl={trend.get('image')}
publishDate={trend.get('date_published')}
author={trend.getIn(['author', 'name'], '')}
/>
))
!gabtrends.isEmpty() &&
<ScrollableList
scrollKey='trending-items'
>
{
gabtrends.slice(0, 8).map((trend, i) => (
<TrendingItem
key={`gab-trend-${i}`}
index={i + 1}
isLast={i === 7}
url={trend.get('url')}
title={trend.get('title')}
description={trend.get('description')}
imageUrl={trend.get('image')}
publishDate={trend.get('date_published')}
author={trend.getIn(['author', 'name'], '')}
/>
))
}
</ScrollableList>
}
</div>
</PanelLayout>

View File

@@ -36,19 +36,34 @@ class WhoToFollowPanel extends ImmutablePureComponent {
'suggestions',
]
componentDidMount () {
this.props.fetchSuggestions()
state = {
fetched: false,
}
static getDerivedStateFromProps(nextProps, prevState) {
if (!nextProps.isHidden && nextProps.isIntersecting && !prevState.fetched) {
return {
fetched: true
}
}
return null
}
componentDidUpdate(prevProps, prevState, snapshot) {
if (!prevState.fetched && this.state.fetched) {
this.props.fetchSuggestions()
}
}
render() {
const { intl, suggestions, dismissSuggestion } = this.props
if (suggestions.isEmpty()) {
return null
}
if (suggestions.isEmpty()) return null
return (
<PanelLayout
noPadding
title={intl.formatMessage(messages.title)}
footerButtonTitle={intl.formatMessage(messages.show_more)}
footerButtonTo='/explore'
@@ -57,6 +72,7 @@ class WhoToFollowPanel extends ImmutablePureComponent {
{
suggestions.map(accountId => (
<Account
compact
showDismiss
key={accountId}
id={accountId}

View File

@@ -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>
)
}
}

View File

@@ -2,8 +2,6 @@ import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { Manager, Reference, Popper } from 'react-popper'
import classnames from 'classnames/bind'
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'
@@ -117,7 +115,6 @@ class PopoverBase extends ImmutablePureComponent {
targetRef,
innerRef,
} = this.props
const open = this.state.id === openPopoverType
const containerClasses = cx({
default: 1,
@@ -125,8 +122,6 @@ class PopoverBase extends ImmutablePureComponent {
displayNone: !visible,
})
console.log('targetRef:', targetRef)
return (
<Manager>
<Popper

View File

@@ -1,37 +1,51 @@
import detectPassiveEvents from 'detect-passive-events'
import { closePopover } from '../../actions/popover'
import {
POPOVER_CONTENT_WARNING,
POPOVER_DATE_PICKER,
POPOVER_EMOJI_PICKER,
POPOVER_GROUP_INFO,
POPOVER_PROFILE_OPTIONS,
POPOVER_REPOST_OPTIONS,
POPOVER_SEARCH,
POPOVER_SIDEBAR_MORE,
POPOVER_STATUS_OPTIONS,
POPOVER_STATUS_SHARE,
POPOVER_STATUS_VISIBILITY,
POPOVER_USER_INFO,
} from '../../constants'
import {
ContentWarningPopover,
DatePickerPopover,
EmojiPickerPopover,
GroupInfoPopover,
ProfileOptionsPopover,
RepostOptionsPopover,
SearchPopover,
SidebarMorePopover,
StatusOptionsPopover,
StatusSharePopover,
StatusVisibilityPopover,
UserInfoPopover,
} from '../../features/ui/util/async_components'
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 EmojiPickerPopover from './emoji_picker_popover'
import GroupInfoPopover from './group_info_popover'
import ProfileOptionsPopover from './profile_options_popover'
import RepostOptionsPopover from './repost_options_popover'
import SearchPopover from './search_popover'
import SidebarMorePopover from './sidebar_more_popover'
import StatusOptionsPopover from './status_options_popover'
import StatusSharePopover from './status_share_popover'
import StatusVisibilityPopover from './status_visibility_popover'
import UserInfoPopover from './user_info_popover'
const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false
const POPOVER_COMPONENTS = {
CONTENT_WARNING: () => Promise.resolve({ default: ContentWarningPopover }),
DATE_PICKER: () => Promise.resolve({ default: DatePickerPopover }),
EMOJI_PICKER: () => Promise.resolve({ default: EmojiPickerPopover }),
GROUP_INFO: () => GroupInfoPopover,
PROFILE_OPTIONS: () => Promise.resolve({ default: ProfileOptionsPopover }),
REPOST_OPTIONS: () => Promise.resolve({ default: RepostOptionsPopover }),
SEARCH: () => Promise.resolve({ default: SearchPopover }),
SIDEBAR_MORE: () => Promise.resolve({ default: SidebarMorePopover }),
STATUS_OPTIONS: () => Promise.resolve({ default: StatusOptionsPopover }),
STATUS_SHARE: () => Promise.resolve({ default: StatusSharePopover }),
STATUS_VISIBILITY: () => Promise.resolve({ default: StatusVisibilityPopover }),
USER_INFO: () => Promise.resolve({ default: UserInfoPopover }),
}
const POPOVER_COMPONENTS = {}
POPOVER_COMPONENTS[POPOVER_CONTENT_WARNING] = ContentWarningPopover
POPOVER_COMPONENTS[POPOVER_DATE_PICKER] = DatePickerPopover
POPOVER_COMPONENTS[POPOVER_EMOJI_PICKER] = EmojiPickerPopover
POPOVER_COMPONENTS[POPOVER_GROUP_INFO] = GroupInfoPopover
POPOVER_COMPONENTS[POPOVER_PROFILE_OPTIONS] = ProfileOptionsPopover
POPOVER_COMPONENTS[POPOVER_REPOST_OPTIONS] = RepostOptionsPopover
POPOVER_COMPONENTS[POPOVER_SEARCH] = SearchPopover
POPOVER_COMPONENTS[POPOVER_SIDEBAR_MORE] = SidebarMorePopover
POPOVER_COMPONENTS[POPOVER_STATUS_OPTIONS] = StatusOptionsPopover
POPOVER_COMPONENTS[POPOVER_STATUS_SHARE] = StatusSharePopover
POPOVER_COMPONENTS[POPOVER_STATUS_VISIBILITY] = StatusVisibilityPopover
POPOVER_COMPONENTS[POPOVER_USER_INFO] = UserInfoPopover
const mapStateToProps = (state) => ({
type: state.getIn(['popover', 'popoverType']),
@@ -84,14 +98,10 @@ class PopoverRoot extends PureComponent {
document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions)
}
setRef = c => {
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)
@@ -150,8 +160,6 @@ class PopoverRoot extends PureComponent {
const { type, props } = this.props
const visible = !!type
console.log("POPOVER_COMPONENTS[type]:", type, POPOVER_COMPONENTS[type]);
return (
<PopoverBase
visible={visible}
@@ -162,12 +170,12 @@ class PopoverRoot extends PureComponent {
visible &&
<Bundle
fetchComponent={POPOVER_COMPONENTS[type]}
loading={this.renderLoading(type)}
loading={this.renderLoading()}
error={this.renderError}
renderDelay={200}
>
{
(SpecificComponent) => <SpecificComponent {...props} />
(Component) => <Component {...props} />
}
</Bundle>
}

View File

@@ -1,8 +1,39 @@
import { defineMessages, injectIntl } from 'react-intl'
import { MODAL_DISPLAY_OPTIONS } from '../../constants'
import { openModal } from '../../actions/modal'
import PopoverLayout from './popover_layout'
import List from '../list'
export default class SidebarMorePopover extends PureComponent {
const messages = defineMessages({
display: { id: 'display_options', defaultMessage: 'Display Options' },
help: { id: 'getting_started.help', defaultMessage: 'Help' },
settings: { id: 'settings', defaultMessage: 'Settings' },
logout: { 'id': 'confirmations.logout.confirm', 'defaultMessage': 'Log out' },
})
const mapDispatchToProps = (dispatch) => ({
onOpenDisplayModal: () => {
dispatch(openModal(MODAL_DISPLAY_OPTIONS))
},
})
export default
@injectIntl
@connect(null, mapDispatchToProps)
class SidebarMorePopover extends PureComponent {
static propTypes = {
intl: PropTypes.object.isRequired,
onOpenDisplayModal: PropTypes.func.isRequired,
}
handleOnOpenDisplayModal = () => {
this.props.onOpenDisplayModal()
}
render() {
const { intl } = this.props
return (
<PopoverLayout className={_s.width240PX}>
<List
@@ -10,15 +41,19 @@ export default class SidebarMorePopover extends PureComponent {
scrollKey='profile_options'
items={[
{
title: 'Help',
title: intl.formatMessage(messages.help),
href: 'https://help.gab.com',
},
{
title: 'Settings',
title: intl.formatMessage(messages.display),
onClick: this.handleOnOpenDisplayModal,
},
{
title: intl.formatMessage(messages.settings),
href: '/settings',
},
{
title: 'Log Out',
title: intl.formatMessage(messages.logout),
href: '/auth/log_out',
},
]}

View File

@@ -47,7 +47,7 @@ class StatusVisibilityDropdown extends PureComponent {
this.props.onChange(value)
}
componentWillMount () {
componentDidMount () {
const { intl } = this.props
this.options = [

View File

@@ -6,7 +6,9 @@ import {
CX,
POPOVER_PROFILE_OPTIONS,
PLACEHOLDER_MISSING_HEADER_SRC,
MODAL_EDIT_PROFILE,
} from '../constants'
import { openModal } from '../actions/modal'
import { openPopover } from '../actions/popover'
import { me } from '../initial_state'
import AccountActionButton from './account_action_button'
@@ -27,6 +29,7 @@ const messages = defineMessages({
comments: { id: 'comments', defaultMessage: 'Comments' },
media: { id: 'media', defaultMessage: 'Media' },
accountFollowsYou: { id: 'account.follows_you', defaultMessage: 'Follows you' },
editProfile: { id: "account.edit_profile", defaultMessage: "Edit profile" },
})
const mapDispatchToProps = (dispatch) => ({
@@ -35,6 +38,10 @@ const mapDispatchToProps = (dispatch) => ({
dispatch(openPopover(POPOVER_PROFILE_OPTIONS, props))
},
onEditProfile() {
dispatch(openModal(MODAL_EDIT_PROFILE))
},
});
export default
@@ -45,6 +52,7 @@ class ProfileHeader extends ImmutablePureComponent {
static propTypes = {
account: ImmutablePropTypes.map,
intl: PropTypes.object.isRequired,
onEditProfile: PropTypes.func.isRequired,
openProfileOptionsPopover: PropTypes.func.isRequired,
}
@@ -52,6 +60,10 @@ class ProfileHeader extends ImmutablePureComponent {
stickied: false,
}
handleOnEditProfile = () => {
this.props.onEditProfile()
}
handleOpenMore = () => {
const { openProfileOptionsPopover, account } = this.props
@@ -164,15 +176,10 @@ class ProfileHeader extends ImmutablePureComponent {
backgroundColor='none'
color='brand'
className={[_s.justifyContentCenter, _s.alignItemsCenter].join(' ')}
href=''
onClick={this.handleOnEditProfile}
>
<Text
color='inherit'
weight='bold'
size='medium'
className={[_s.px15].join(' ')}
>
Edit Profile
<Text color='inherit' weight='bold' size='medium' className={_s.px15}>
{intl.formatMessage(messages.editProfile)}
</Text>
</Button>
</div>

View File

@@ -196,7 +196,7 @@ export default class ScrollableList extends PureComponent {
return firstChild && firstChild.key;
}
handleLoadMore = e => {
handleLoadMore = (e) => {
e.preventDefault();
this.props.onLoadMore();
}
@@ -213,9 +213,7 @@ export default class ScrollableList extends PureComponent {
} = this.props
const childrenCount = React.Children.count(children);
const trackScroll = true; //placeholder
const loadMore = (hasMore && onLoadMore) ? <LoadMore visible={!isLoading} onClick={this.handleLoadMore} /> : null;
const loadMore = (hasMore && onLoadMore) ? <LoadMore visible={!isLoading} onClick={this.handleLoadMore} /> : null
if (showLoading) {
return <ColumnIndicator type='loading' />
@@ -232,7 +230,7 @@ export default class ScrollableList extends PureComponent {
index={index}
listLength={childrenCount}
intersectionObserverWrapper={this.intersectionObserverWrapper}
saveHeightKey={trackScroll ? `${this.context.router.route.location.key}:${scrollKey}` : null}
saveHeightKey={`${this.context.router.route.location.key}:${scrollKey}`}
>
{
React.cloneElement(child, {

View File

@@ -57,8 +57,8 @@ class Search extends PureComponent {
}
}
handleChange = (e) => {
this.props.onChange(e.target.value)
handleChange = (value) => {
this.props.onChange(value)
}
handleFocus = () => {

View File

@@ -43,8 +43,13 @@ const mapStateToProps = (state, { timelineId }) => {
const getStatusIds = makeGetStatusIds();
const promotion = promotions.length > 0 && sample(promotions.filter(p => p.timeline_id === timelineId));
const statusIds = getStatusIds(state, {
type: timelineId.substring(0,5) === 'group' ? 'group' : timelineId,
id: timelineId
})
return {
statusIds: getStatusIds(state, { type: timelineId.substring(0,5) === 'group' ? 'group' : timelineId, id: timelineId }),
statusIds,
isLoading: state.getIn(['timelines', timelineId, 'isLoading'], true),
isPartial: state.getIn(['timelines', timelineId, 'isPartial'], false),
hasMore: state.getIn(['timelines', timelineId, 'hasMore']),

View File

@@ -28,7 +28,7 @@ export default class TabBar extends PureComponent {
onClick={tab.onClick}
icon={tab.icon}
to={tab.to}
active={tab.active}
isActive={tab.active}
isLarge={isLarge}
/>
))

View File

@@ -13,22 +13,23 @@ export default class Textarea extends PureComponent {
onKeyUp: PropTypes.func,
onFocus: PropTypes.func,
onBlur: PropTypes.func,
onClear: PropTypes.func,
title: PropTypes.string,
}
handleOnChange = (e) => {
this.props.onChange(e.target.value)
}
render() {
const {
placeholder,
prependIcon,
value,
hasClear,
onChange,
onKeyUp,
onFocus,
onBlur,
onClear,
title
title,
} = this.props
const inputClasses = cx({
@@ -64,7 +65,7 @@ export default class Textarea extends PureComponent {
type='text'
placeholder={placeholder}
value={value}
onChange={onChange}
onChange={this.handleOnChange}
onKeyUp={onKeyUp}
onFocus={onFocus}
onBlur={onBlur}

View File

@@ -1,3 +1,4 @@
import { Fragment } from 'react'
import classNames from 'classnames/bind'
import { urlRegex } from '../features/compose/util/url_regex'
import Button from './button'
@@ -19,6 +20,7 @@ export default class TrendingItem extends PureComponent {
author: PropTypes.string,
publishDate: PropTypes.string,
isLast: PropTypes.bool,
isHidden: PropTypes.bool,
wide: PropTypes.bool,
}
@@ -51,9 +53,24 @@ export default class TrendingItem extends PureComponent {
publishDate,
isLast,
wide,
isHidden,
} = this.props
const { hovering } = this.state
const correctedAuthor = author.replace('www.', '')
const correctedDescription = description.length >= 120 ? `${description.substring(0, 120).trim()}...` : description
const descriptionHasLink = correctedDescription.match(urlRegex)
if (isHidden) {
return (
<Fragment>
{title}
{!descriptionHasLink && correctedDescription}
{correctedAuthor}
</Fragment>
)
}
const containerClasses = cx({
default: 1,
noUnderline: 1,
@@ -70,10 +87,6 @@ export default class TrendingItem extends PureComponent {
underline: hovering,
})
const correctedAuthor = author.replace('www.', '')
const correctedDescription = description.length >= 120 ? `${description.substring(0, 120).trim()}...` : description
const descriptionHasLink = correctedDescription.match(urlRegex)
const image = (
<Image
nullable