This commit is contained in:
mgabdev 2020-05-07 01:55:24 -04:00
parent a026d86b86
commit a390662e4f
29 changed files with 347 additions and 257 deletions

@ -8,7 +8,7 @@ export default class CardView extends PureComponent {
const { children } = this.props
return (
<div className={[_s.default, _s.boxShadowBlock, _s.bgPrimary, _s.overflowHidden, _s.radiusSmall].join(' ')}>
<div title='cardview' className={[_s.default].join(' ')}>
{children}
</div>
)

@ -29,12 +29,12 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
}
},
onOpenGroupOptions() {
},
openProfileOptionsPopover(props) {
dispatch(openPopover('GROUP_OPTIONS', props))
onOpenGroupOptions(targetRef, group) {
dispatch(openPopover('GROUP_OPTIONS', {
targetRef,
group,
position: 'top',
}))
},
});
@ -57,11 +57,18 @@ class GroupHeader extends ImmutablePureComponent {
this.props.onToggleMembership(group, relationships);
}
handleOnOpenGroupOptions = () => {
this.props.onOpenGroupOptions(this.infoBtn, this.props.group)
}
setInfoBtn = (c) => {
this.infoBtn = c;
}
render() {
const {
group,
intl,
onOpenGroupOptions,
relationships,
} = this.props
@ -131,7 +138,8 @@ class GroupHeader extends ImmutablePureComponent {
backgroundColor='tertiary'
className={_s.mr5}
icon='ellipsis'
onClick={onOpenGroupOptions}
onClick={this.handleOnOpenGroupOptions}
buttonRef={this.setInfoBtn}
/>
</div>
</div>

@ -1,68 +0,0 @@
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' },
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))
},
})
export default
@connect(mapStateToProps, mapDispatchToProps)
@injectIntl
class UnfollowModal extends PureComponent {
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()
}
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'))),
// }));
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))
}}
/>
)
}
}

@ -1,31 +1,36 @@
import { defineMessages, injectIntl } from 'react-intl'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ModalLayout from './modal_layout'
import GroupCreate from '../../features/group_create'
import GroupMembers from '../../features/group_members'
const messages = defineMessages({
title: { id: 'create_group', defaultMessage: 'Create group' },
title: { id: 'group_members', defaultMessage: 'Group members' },
})
export default
@injectIntl
class GroupMembersModal extends ImmutablePureComponent {
class GroupMembersModal extends PureComponent {
static propTypes = {
groupId: PropTypes.string.isRequired,
intl: PropTypes.object.isRequired,
onClose: PropTypes.func.isRequired,
}
render() {
const { intl, onClose } = this.props
const {
intl,
onClose,
groupId,
} = this.props
return (
<ModalLayout
title={intl.formatMessage(messages.title)}
width={440}
onClose={onClose}
noPadding
>
<GroupCreate onCloseModal={onClose} />
<GroupMembers groupId={groupId} onCloseModal={onClose} />
</ModalLayout>
)
}

@ -0,0 +1,37 @@
import { defineMessages, injectIntl } from 'react-intl'
import ModalLayout from './modal_layout'
import GroupRemovedAccounts from '../../features/group_removed_accounts'
const messages = defineMessages({
title: { id: 'group_removed', defaultMessage: 'Removed accounts' },
})
export default
@injectIntl
class GroupRemovedAccountsModal extends PureComponent {
static propTypes = {
groupId: PropTypes.string.isRequired,
intl: PropTypes.object.isRequired,
onClose: PropTypes.func.isRequired,
}
render() {
const {
intl,
onClose,
groupId,
} = this.props
return (
<ModalLayout
title={intl.formatMessage(messages.title)}
width={440}
onClose={onClose}
noPadding
>
<GroupRemovedAccounts groupId={groupId} onCloseModal={onClose} />
</ModalLayout>
)
}
}

@ -3,6 +3,9 @@ import { injectIntl, FormattedMessage, defineMessages } from 'react-intl'
import classNames from 'classnames/bind'
import { openModal } from '../../actions/modal'
import { cancelReplyCompose } from '../../actions/compose'
import { BREAKPOINT_EXTRA_SMALL } from '../../constants'
import Responsive from '../../features/ui/util/responsive_component'
import CardView from '../card_view'
const messages = defineMessages({
confirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
@ -145,7 +148,14 @@ class ModalBase extends PureComponent {
onClick={this.handleOnClose}
className={[_s.default, _s.posFixed, _s.alignItemsCenter, _s.justifyContentCenter, _s.z4, _s.width100PC, _s.height100PC, _s.top0, _s.rightAuto, _s.bottomAuto, _s.left0].join(' ')}
>
{children}
<Responsive min={BREAKPOINT_EXTRA_SMALL}>
{children}
</Responsive>
<Responsive max={BREAKPOINT_EXTRA_SMALL}>
<CardView>
{children}
</CardView>
</Responsive>
</div>
</Fragment>
}

@ -1,3 +1,4 @@
import { Fragment } from 'react'
import { closeModal } from '../../actions/modal'
import { cancelReplyCompose } from '../../actions/compose'
import Bundle from '../../features/ui/util/bundle'
@ -17,8 +18,8 @@ import {
MODAL_GIF_PICKER,
MODAL_GROUP_CREATE,
MODAL_GROUP_DELETE,
MODAL_GROUP_EDITOR,
MODAL_GROUP_MEMBERS,
MODAL_GROUP_REMOVED_ACCOUNTS,
MODAL_HASHTAG_TIMELINE_SETTINGS,
MODAL_HOME_TIMELINE_SETTINGS,
MODAL_HOTKEYS,
@ -51,8 +52,8 @@ import {
GifPickerModal,
GroupCreateModal,
GroupDeleteModal,
GroupEditorModal,
GroupMembersModal,
GroupRemovedAccountsModal,
HashtagTimelineSettingsModal,
HomeTimelineSettingsModal,
HotkeysModal,
@ -86,8 +87,8 @@ 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_GROUP_MEMBERS] = GroupMembersModal
MODAL_COMPONENTS[MODAL_GROUP_REMOVED_ACCOUNTS] = GroupRemovedAccountsModal
MODAL_COMPONENTS[MODAL_HASHTAG_TIMELINE_SETTINGS] = HashtagTimelineSettingsModal
MODAL_COMPONENTS[MODAL_HOME_TIMELINE_SETTINGS] = HomeTimelineSettingsModal
MODAL_COMPONENTS[MODAL_HOTKEYS] = HotkeysModal
@ -159,8 +160,6 @@ class ModalRoot extends PureComponent {
const { type, props } = this.props
const visible = !!type
// : todo : init card view if mobile
return (
<ModalBase onClose={this.onClickClose} type={type}>
{

@ -61,7 +61,7 @@ class NavigationBar extends ImmutablePureComponent {
return (
<div className={[_s.default, _s.z4, _s.height53PX, _s.width100PC].join(' ')}>
<div className={[_s.default, _s.height53PX, _s.bgBrand, _s.alignItemsCenter, _s.z3, _s.top0, _s.right0, _s.left0, _s.posFixed].join(' ')} >
<div className={[_s.default, _s.height53PX, _s.bgNavigation, _s.alignItemsCenter, _s.z3, _s.top0, _s.right0, _s.left0, _s.posFixed].join(' ')} >
<div className={[_s.default, _s.flexRow, _s.width1255PX].join(' ')}>
@ -71,8 +71,16 @@ class NavigationBar extends ImmutablePureComponent {
<div className={[_s.default, _s.flexRow].join(' ')}>
<h1 className={[_s.default, _s.mr15].join(' ')}>
<Button to='/' isText title='Gab' aria-label='Gab' className={[_s.default, _s.justifyContentCenter, _s.noSelect, _s.noUnderline, _s.height53PX, _s.cursorPointer, _s.px10, _s.mr15].join(' ')}>
<Icon id='gab-logo' className={_s.fillWhite} />
<Button
to='/'
isText
title='Gab'
aria-label='Gab'
color='none'
backgroundColor='none'
className={[_s.default, _s.justifyContentCenter, _s.noSelect, _s.noUnderline, _s.height53PX, _s.cursorPointer, _s.px10, _s.mr15].join(' ')}
>
<Icon id='gab-logo' className={_s.fillNavigationBrand} />
</Button>
</h1>
@ -131,7 +139,7 @@ class NavigationBar extends ImmutablePureComponent {
className={[_s.height53PX, _s.bgTransparent, _s.mr5, _s.cursorPointer, _s.outlineNone, _s.default, _s.justifyContentCenter].join(' ')}
icon='arrow-left'
iconSize='32px'
iconClassName={[_s.mr5, _s.fillPrimary].join(' ')}
iconClassName={[_s.mr5, _s.fillNavigation].join(' ')}
onClick={this.handleBackClick}
/>
}
@ -139,7 +147,9 @@ class NavigationBar extends ImmutablePureComponent {
<div className={[_s.default, _s.height53PX, _s.justifyContentCenter, _s.mlAuto, _s.mrAuto].join(' ')}>
<Heading size='h1'>
{title}
<span className={_s.colorNavigation}>
{title}
</span>
</Heading>
</div>
@ -157,8 +167,8 @@ class NavigationBar extends ImmutablePureComponent {
key={`action-btn-${i}`}
className={[_s.ml5, _s.height53PX, _s.justifyContentCenter, _s.px5].join(' ')}
icon={action.icon}
iconClassName={_s.fillPrimary}
iconSize='14px'
iconClassName={_s.fillNavigation}
iconSize='18px'
/>
))
}
@ -181,7 +191,7 @@ class NavigationBarButtonDivider extends PureComponent {
render() {
return (
<div className={[_s.default, _s.height20PX, _s.width1PX, _s.mr10, _s.ml10, _s.bgBrandDark].join(' ')} />
<div className={[_s.default, _s.height20PX, _s.width1PX, _s.mr10, _s.ml10, _s.bgNavigationBlend].join(' ')} />
)
}
@ -218,18 +228,18 @@ class NavigationBarButton extends PureComponent {
cursorPointer: 1,
bgTransparent: 1,
noUnderline: 1,
colorNavigation: 1,
px10: !!title,
px5: !title,
colorWhite: !!title,
fs13PX: !!title,
fontWeightNormal: !!title,
textUppercase: !!title,
bgBrandDark_onHover: !!title,
})
const iconClasses = CX({
fillWhite: !!title || active,
fillBrandDark: !title,
fillNavigation: !!title || active,
fillNavigationBlend: !title,
mr10: !!title,
})
@ -240,7 +250,6 @@ class NavigationBarButton extends PureComponent {
to={to}
href={href}
attrTitle={attrTitle}
color='white'
className={classes}
noClasses
>

@ -2,11 +2,12 @@ import { Fragment } from 'react'
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import { openModal } from '../../actions/modal'
import { MODAL_GROUP_MEMBERS } from '../../constants'
import { shortNumberFormat } from '../../utils/numbers'
import PanelLayout from './panel_layout'
import Button from '../button'
import Divider from '../divider'
import Heading from '../heading'
import Icon from '../icon'
import Text from '../text'
import RelativeTimestamp from '../relative_timestamp'
@ -16,20 +17,31 @@ const messages = defineMessages({
members: { id: 'members', defaultMessage: 'Members' },
})
const mapDispatchToProps = (dispatch) => ({
onOpenGroupMembersModal(groupId) {
console.log("onOpenGroupMembersModal:", groupId)
dispatch(openModal(MODAL_GROUP_MEMBERS, { groupId }))
},
})
export default
@injectIntl
@connect(null, mapDispatchToProps)
class GroupInfoPanel extends ImmutablePureComponent {
static propTypes = {
group: ImmutablePropTypes.list.isRequired,
group: ImmutablePropTypes.map.isRequired,
intl: PropTypes.object.isRequired,
onOpenGroupMembersModal: PropTypes.func.isRequired,
}
handleOnOpenGroupMembersModal = () => {
this.props.onOpenGroupMembersModal(this.props.group.get('id'))
}
render() {
const { intl, group } = this.props
console.log("group:", group)
return (
<PanelLayout title={intl.formatMessage(messages.title)}>
{
@ -53,10 +65,10 @@ class GroupInfoPanel extends ImmutablePureComponent {
</div>
<Button
isText
to={`/groups/${group.get('id')}/members`}
color='brand'
backgroundColor='none'
className={_s.mlAuto}
onClick={this.handleOnOpenGroupMembersModal}
>
<Text color='inherit' weight='medium' size='normal' className={_s.underline_onHover}>
{shortNumberFormat(group.get('member_count'))}

@ -65,8 +65,8 @@ class WhoToFollowPanel extends ImmutablePureComponent {
<PanelLayout
noPadding
title={intl.formatMessage(messages.title)}
footerButtonTitle={intl.formatMessage(messages.show_more)}
footerButtonTo='/explore'
// footerButtonTitle={intl.formatMessage(messages.show_more)}
// footerButtonTo='/explore'
>
<div className={_s.default}>
{

@ -1,48 +1,42 @@
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import { defineMessages, injectIntl } from 'react-intl'
import { quoteCompose } from '../../actions/compose'
import { repost, unrepost } from '../../actions/interactions'
import {
MODAL_GROUP_CREATE,
MODAL_GROUP_MEMBERS,
MODAL_GROUP_REMOVED_ACCOUNTS,
} from '../../constants'
import { openModal } from '../../actions/modal'
import { boostModal, me } from '../../initial_state'
import { closePopover } from '../../actions/popover'
import { me } from '../../initial_state'
import PopoverLayout from './popover_layout'
import List from '../list'
const messages = defineMessages({
repost: { id: 'repost', defaultMessage: 'Repost' },
repostWithComment: { id: 'repost_with_comment', defaultMessage: 'Repost with comment' },
groupMembers: { id: 'group_members', defaultMessage: 'Group members' },
removedMembers: { id: 'group_removed_members', defaultMessage: 'Removed accounts' },
editGroup: { id: 'edit_group', defaultMessage: 'Edit group' },
});
const mapDispatchToProps = (dispatch, { intl }) => ({
onQuote (status, router) {
if (!me) return dispatch(openModal('UNAUTHORIZED'))
const mapDispatchToProps = (dispatch) => ({
dispatch((_, getState) => {
const state = getState();
if (state.getIn(['compose', 'text']).trim().length !== 0) {
dispatch(openModal('CONFIRM', {
message: intl.formatMessage(messages.quoteMessage),
confirm: intl.formatMessage(messages.quoteConfirm),
onConfirm: () => dispatch(quoteCompose(status, router)),
}));
} else {
dispatch(quoteCompose(status, router));
}
});
onOpenEditGroup(group) {
dispatch(closePopover())
dispatch(openModal(MODAL_GROUP_CREATE, { group }))
},
onRepost (status) {
if (!me) return dispatch(openModal('UNAUTHORIZED'))
if (status.get('reblogged')) {
dispatch(unrepost(status));
} else {
dispatch(repost(status));
}
onOpenRemovedMembers(groupId) {
dispatch(closePopover())
dispatch(openModal(MODAL_GROUP_REMOVED_ACCOUNTS, { groupId }))
},
onOpenGroupMembers(groupId) {
dispatch(closePopover())
dispatch(openModal(MODAL_GROUP_MEMBERS, { groupId }))
},
});
export default
@injectIntl
@connect(null, mapDispatchToProps)
@ -50,19 +44,24 @@ class GroupOptionsPopover extends ImmutablePureComponent {
static defaultProps = {
intl: PropTypes.object.isRequired,
status: ImmutablePropTypes.map.isRequired,
onQuote: PropTypes.func.isRequired,
onRepost: PropTypes.func.isRequired,
group: ImmutablePropTypes.map.isRequired,
onOpenEditGroup: PropTypes.func.isRequired,
onOpenRemovedMembers: PropTypes.func.isRequired,
onOpenGroupMembers: PropTypes.func.isRequired,
}
updateOnProps = ['status']
updateOnProps = ['group']
handleOnRepost = () => {
handleEditGroup = () => {
this.props.onOpenEditGroup(this.props.group)
}
handleOnQuote = () => {
handleOnOpenRemovedMembers = () => {
this.props.onOpenRemovedMembers(this.props.group.get('id'))
}
handleOnOpenGroupMembers = () => {
this.props.onOpenGroupMembers(this.props.group.get('id'))
}
render() {
@ -71,22 +70,28 @@ class GroupOptionsPopover extends ImmutablePureComponent {
const listItems = [
{
hideArrow: true,
icon: 'repost',
title: intl.formatMessage(messages.repost),
onClick: this.handleOnRepost,
icon: 'group',
title: intl.formatMessage(messages.groupMembers),
onClick: this.handleOnOpenGroupMembers,
},
{
hideArrow: true,
icon: 'block',
title: intl.formatMessage(messages.removedMembers),
onClick: this.handleOnOpenRemovedMembers,
},
{
hideArrow: true,
icon: 'pencil',
title: intl.formatMessage(messages.repostWithComment),
onClick: this.handleBlockDomain,
title: intl.formatMessage(messages.editGroup),
onClick: this.handleEditGroup,
}
]
return (
<PopoverLayout>
<PopoverLayout width={210}>
<List
scrollKey='repost_options'
scrollKey='group_options'
items={listItems}
size='large'
/>

@ -3,6 +3,7 @@ import {
POPOVER_DATE_PICKER,
POPOVER_EMOJI_PICKER,
POPOVER_GROUP_INFO,
POPOVER_GROUP_OPTIONS,
POPOVER_PROFILE_OPTIONS,
POPOVER_REPOST_OPTIONS,
POPOVER_SEARCH,
@ -17,6 +18,7 @@ import {
DatePickerPopover,
EmojiPickerPopover,
GroupInfoPopover,
GroupOptionsPopover,
ProfileOptionsPopover,
RepostOptionsPopover,
SearchPopover,
@ -34,6 +36,7 @@ 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_GROUP_OPTIONS] = GroupOptionsPopover
POPOVER_COMPONENTS[POPOVER_PROFILE_OPTIONS] = ProfileOptionsPopover
POPOVER_COMPONENTS[POPOVER_REPOST_OPTIONS] = RepostOptionsPopover
POPOVER_COMPONENTS[POPOVER_SEARCH] = SearchPopover

@ -4,17 +4,11 @@ import Icon from './icon'
export default class PullToRefresher extends PureComponent {
static propTypes = {
children: PropTypes.any,
}
render() {
const { children } = this.props
return (
<Responsive max={BREAKPOINT_EXTRA_SMALL}>
<div className={[_s.default, _s.posAbs, _s.left0, _s.right0, _s.topNeg60PX].join(' ')}>
<Icon id='loading' size='20px' />
<div className={[_s.default, _s.alignItemsCenter, _s.posAbs, _s.left0, _s.right0, _s.topNeg80PX].join(' ')}>
<Icon id='loading' size='24px' />
</div>
</Responsive>
)

@ -128,6 +128,10 @@ export default class ScrollableList extends PureComponent {
const { innerHeight } = this.window;
const offset = scrollHeight - scrollTop - innerHeight;
if (scrollTop < -60 && this.props.onReload) {
// reload
}
if (600 > offset && this.props.onLoadMore && this.props.hasMore && !this.props.isLoading) {
this.props.onLoadMore();
}

@ -141,21 +141,22 @@ export default class StatusCard extends ImmutablePureComponent {
const horizontal = (card.get('width') > card.get('height') && (card.get('width') + 100 >= width)) || card.get('type') !== 'link' || embedded
const interactive = card.get('type') !== 'link'
const cardTitle = `${card.get('title')}`.trim()
const title = interactive ?
(
<a
className={[_s.default, _s.displayFlex, _s.text, _s.noUnderline, _s.overflowWrapBreakWord, _s.colorPrimary, _s.fs15PX, _s.fontWeightMedium].join(' ')}
href={card.get('url')}
title={card.get('title')}
title={cardTitle}
rel={DEFAULT_REL}
target='_blank'
>
{card.get('title')}
{cardTitle}
</a>
)
: (
<span className={[_s.default, _s.displayFlex, _s.text, _s.overflowWrapBreakWord, _s.colorPrimary, _s.fs15PX, _s.fontWeightMedium].join(' ')}>
{card.get('title')}
{cardTitle}
</span>
)

@ -22,6 +22,7 @@ export const POPOVER_CONTENT_WARNING = 'CONTENT_WARNING'
export const POPOVER_DATE_PICKER = 'DATE_PICKER'
export const POPOVER_EMOJI_PICKER = 'EMOJI_PICKER'
export const POPOVER_GROUP_INFO = 'GROUP_INFO'
export const POPOVER_GROUP_OPTIONS = 'GROUP_OPTIONS'
export const POPOVER_PROFILE_OPTIONS = 'PROFILE_OPTIONS'
export const POPOVER_REPOST_OPTIONS = 'REPOST_OPTIONS'
export const POPOVER_SEARCH = 'SEARCH'
@ -44,8 +45,8 @@ export const MODAL_EMBED = 'EMBED'
export const MODAL_GIF_PICKER = 'GIF_PICKER'
export const MODAL_GROUP_CREATE = 'GROUP_CREATE'
export const MODAL_GROUP_DELETE = 'GROUP_DELETE'
export const MODAL_GROUP_EDITOR = 'GROUP_EDITOR'
export const MODAL_GROUP_MEMBERS = 'GROUP_MEMBERS'
export const MODAL_GROUP_REMOVED_ACCOUNTS = 'GROUP_REMOVED_ACCOUNTS'
export const MODAL_HASHTAG_TIMELINE_SETTINGS = 'HASHTAG_TIMELINE_SETTINGS'
export const MODAL_HOME_TIMELINE_SETTINGS = 'HOME_TIMELINE_SETTINGS'
export const MODAL_HOTKEYS = 'HOTKEYS'

@ -17,8 +17,6 @@ const mapStateToProps = (state, { account, commentsOnly = false }) => {
const path = commentsOnly ? `${accountId}:comments_only` : accountId
console.log("commentsOnly, path:", commentsOnly, path)
return {
accountId,
statusIds: state.getIn(['timelines', `account:${path}`, 'items'], emptyList),

@ -1,86 +1,101 @@
import ImmutablePureComponent from 'react-immutable-pure-component';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import debounce from 'lodash.debounce'
import {
fetchMembers,
expandMembers,
updateRole,
createRemovedAccount,
} from '../actions/groups';
import { FormattedMessage } from 'react-intl';
import Account from '../components/account';
import ScrollableList from '../components/scrollable_list';
} from '../actions/groups'
import { FormattedMessage } from 'react-intl'
import Account from '../components/account'
import ScrollableList from '../components/scrollable_list'
const mapStateToProps = (state, { params: { id } }) => ({
group: state.getIn(['groups', id]),
relationships: state.getIn(['group_relationships', id]),
accountIds: state.getIn(['user_lists', 'groups', id, 'items']),
hasMore: !!state.getIn(['user_lists', 'groups', id, 'next']),
});
const mapStateToProps = (state, { groupId }) => ({
group: state.getIn(['groups', groupId]),
relationships: state.getIn(['group_relationships', groupId]),
accountIds: state.getIn(['user_lists', 'groups', groupId, 'items']),
hasMore: !!state.getIn(['user_lists', 'groups', groupId, 'next']),
})
const mapDispatchToProps = (dispatch) => ({
onFetchMembers(groupId) {
dispatch(fetchMembers(groupId))
},
onExpandMembers(groupId) {
dispatch(expandMembers(groupId))
},
})
export default
@connect(mapStateToProps)
@connect(mapStateToProps, mapDispatchToProps)
class GroupMembers extends ImmutablePureComponent {
static propTypes = {
params: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired,
groupId: PropTypes.string.isRequired,
accountIds: ImmutablePropTypes.list,
hasMore: PropTypes.bool,
};
onExpandMembers: PropTypes.func.isRequired,
onFetchMembers: PropTypes.func.isRequired,
}
componentWillMount() {
const { params: { id } } = this.props;
const { groupId } = this.props
this.props.dispatch(fetchMembers(id));
this.props.onFetchMembers(groupId)
}
componentWillReceiveProps(nextProps) {
if (nextProps.params.id !== this.props.params.id) {
this.props.dispatch(fetchMembers(nextProps.params.id));
if (nextProps.groupId !== this.props.groupId) {
this.props.onFetchMembers(nextProps.groupId)
}
}
handleLoadMore = debounce(() => {
this.props.dispatch(expandMembers(this.props.params.id));
}, 300, { leading: true });
this.props.onExpandMembers(this.props.groupId)
}, 300, { leading: true })
render() {
const { accountIds, hasMore, group, relationships, dispatch } = this.props;
if (!group || !accountIds || !relationships) {
return <LoadingIndicator />
}
const {
accountIds,
hasMore,
group,
relationships,
dispatch,
} = this.props
return (
<ScrollableList
scrollKey='members'
scrollKey='group-members'
hasMore={hasMore}
showLoading={(!group || !accountIds || !relationships)}
onLoadMore={this.handleLoadMore}
emptyMessage={<FormattedMessage id='group.members.empty' defaultMessage='This group does not has any members.' />}
>
{accountIds.map(id => {
let menu = [];
{
accountIds && accountIds.map((id) => {
let menu = []
if (relationships.get('admin')) {
menu = [
{ text: 'Remove from group', action: () => dispatch(createRemovedAccount(group.get('id'), id)) },
{ text: 'Make administrator', action: () => dispatch(updateRole(group.get('id'), id, 'admin')) },
]
}
if (relationships.get('admin')) {
menu = [
{ text: 'Remove from group', action: () => dispatch(createRemovedAccount(group.get('id'), id)) },
{ text: 'Make administrator', action: () => dispatch(updateRole(group.get('id'), id, 'admin')) },
]
}
return (
<div className="group-account-wrapper" key={id}>
<Account id={id} actionIcon="none" onActionClick={() => true} />
{ /*
menu.length > 0 && <DropdownMenuContainer items={menu} icon='ellipsis-h' size={18} direction='right' />
*/
}
</div>
);
})}
return (
<Account
compact
key={id}
id={id}
actionIcon='ellipsis'
onActionClick={() => true}
/>
)
})
}
</ScrollableList>
);
)
}
}

@ -1,26 +1,25 @@
import ImmutablePureComponent from 'react-immutable-pure-component';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import debounce from 'lodash.debounce'
import ColumnIndicator from '../components/column_indicator';
import {
fetchRemovedAccounts,
expandRemovedAccounts,
removeRemovedAccount,
} from '../actions/groups';
import { FormattedMessage } from 'react-intl';
import Account from '../components/account';
import ScrollableList from '../components/scrollable_list';
import { defineMessages, injectIntl } from 'react-intl';
} from '../actions/groups'
import { FormattedMessage } from 'react-intl'
import Account from '../components/account'
import ScrollableList from '../components/scrollable_list'
import { defineMessages, injectIntl } from 'react-intl'
const messages = defineMessages({
remove: { id: 'groups.removed_accounts', defaultMessage: 'Allow joining' },
});
})
const mapStateToProps = (state, { params: { id } }) => ({
group: state.getIn(['groups', id]),
accountIds: state.getIn(['user_lists', 'groups_removed_accounts', id, 'items']),
hasMore: !!state.getIn(['user_lists', 'groups_removed_accounts', id, 'next']),
});
const mapStateToProps = (state, { groupId }) => ({
group: state.getIn(['groups', groupId]),
accountIds: state.getIn(['user_lists', 'groups_removed_accounts', groupId, 'items']),
hasMore: !!state.getIn(['user_lists', 'groups_removed_accounts', groupId, 'next']),
})
export default
@connect(mapStateToProps)
@ -28,49 +27,50 @@ export default
class GroupRemovedAccounts extends ImmutablePureComponent {
static propTypes = {
params: PropTypes.object.isRequired,
groupId: PropTypes.string.isRequired,
dispatch: PropTypes.func.isRequired,
accountIds: ImmutablePropTypes.list,
hasMore: PropTypes.bool,
};
}
componentWillMount() {
const { params: { id } } = this.props;
const { groupId } = this.props
this.props.dispatch(fetchRemovedAccounts(id));
this.props.dispatch(fetchRemovedAccounts(groupId))
}
componentWillReceiveProps(nextProps) {
if (nextProps.params.id !== this.props.params.id) {
this.props.dispatch(fetchRemovedAccounts(nextProps.params.id));
if (nextProps.groupId !== this.props.groupId) {
this.props.dispatch(fetchRemovedAccounts(nextProps.groupId))
}
}
handleLoadMore = debounce(() => {
this.props.dispatch(expandRemovedAccounts(this.props.params.id));
}, 300, { leading: true });
this.props.dispatch(expandRemovedAccounts(this.props.groupId))
}, 300, { leading: true })
render() {
const { accountIds, hasMore, group, intl } = this.props;
if (!group || !accountIds) {
return <ColumnIndicator type='loading' />
}
const { accountIds, hasMore, group, intl } = this.props
return (
<ScrollableList
scrollKey='removed_accounts'
hasMore={hasMore}
showLoading={(!group || !accountIds)}
onLoadMore={this.handleLoadMore}
emptyMessage={<FormattedMessage id='group.removed_accounts.empty' defaultMessage='This group does not has any removed accounts.' />}
>
{accountIds.map(id => (<Account
key={id}
id={id}
actionIcon='remove'
onActionClick={() => this.props.dispatch(removeRemovedAccount(group.get('id'), id))}
actionTitle={intl.formatMessage(messages.remove)}
/>))}
{
accountIds && accountIds.map((id) => (
<Account
key={id}
id={id}
actionIcon='subtract'
onActionClick={() => this.props.dispatch(removeRemovedAccount(group.get('id'), id))}
actionTitle={intl.formatMessage(messages.remove)}
/>
))
}
</ScrollableList>
)
}

@ -229,7 +229,7 @@ class ListEdit extends ImmutablePureComponent {
key={`remove-from-list-${accountId}`}
id={accountId}
onActionClick={() => this.handleAddOrRemoveFromList(accountId)}
actionIcon={'subtract'}
actionIcon='subtract'
/>
))
}

@ -153,8 +153,8 @@ class SwitchingArea extends PureComponent {
<WrappedRoute path='/groups/browse/admin' exact page={GroupsPage} component={GroupsCollection} content={children} componentParams={{ activeTab: 'admin' }} />
<WrappedRoute path='/groups/create' page={ModalPage} component={GroupCreate} content={children} componentParams={{ title: 'Create Group' }} />
<WrappedRoute path='/groups/:id/members' page={GroupPage} component={GroupMembers} content={children} />
<WrappedRoute path='/groups/:id/removed-accounts' page={GroupPage} component={GroupRemovedAccounts} content={children} />
{ /* <WrappedRoute path='/groups/:id/members' page={GroupPage} component={GroupMembers} content={children} />
<WrappedRoute path='/groups/:id/removed-accounts' page={GroupPage} component={GroupRemovedAccounts} content={children} /> */}
<WrappedRoute path='/groups/:id/edit' page={ModalPage} component={GroupCreate} content={children} componentParams={{ title: 'Edit Group' }} />
<WrappedRoute path='/groups/:id' page={GroupPage} component={GroupTimeline} content={children} />
@ -208,11 +208,12 @@ class SwitchingArea extends PureComponent {
<Redirect from='/@:username/posts/:statusId' to='/:username/posts/:statusId' exact />
<WrappedRoute path='/:username/posts/:statusId' publicRoute exact page={BasicPage} component={StatusFeature} content={children} componentParams={{ title: 'Status' }} />
{ /*
<Redirect from='/@:username/posts/:statusId/reposts' to='/:username/posts/:statusId/reposts' />
<WrappedRoute path='/:username/posts/:statusId/reposts' page={BasicPage} component={StatusReposts} content={children} componentParams={{ title: 'Reposts' }} />
<Redirect from='/@:username/posts/:statusId/likes' to='/:username/posts/:statusId/likes' />
<WrappedRoute path='/:username/posts/:statusId/likes' page={BasicPage} component={StatusLikes} content={children} componentParams={{ title: 'Likes' }} />
<WrappedRoute path='/:username/posts/:statusId/likes' page={BasicPage} component={StatusLikes} content={children} componentParams={{ title: 'Likes' }} /> */ }
<WrappedRoute page={ErrorPage} component={GenericNotFound} content={children} />
</Switch>

@ -29,9 +29,10 @@ export function GroupsCollection() { return import(/* webpackChunkName: "feature
export function GroupCreate() { return import(/* webpackChunkName: "features/group_create" */'../../group_create') }
export function GroupCreateModal() { return import(/* webpackChunkName: "components/group_create_modal" */'../../../components/modal/group_create_modal') }
export function GroupDeleteModal() { return import(/* webpackChunkName: "components/group_delete_modal" */'../../../components/modal/group_delete_modal') }
export function GroupEditorModal() { return import(/* webpackChunkName: "components/group_editor_modal" */'../../../components/modal/group_editor_modal') }
export function GroupRemovedAccountsModal() { return import(/* webpackChunkName: "components/group_removed_accounts_modal" */'../../../components/modal/group_removed_accounts_modal') }
export function GroupMembersModal() { return import(/* webpackChunkName: "components/group_members_modal" */'../../../components/modal/group_members_modal') }
export function GroupInfoPopover() { return import(/* webpackChunkName: "components/group_info_popover" */'../../../components/popover/group_info_popover') }
export function GroupOptionsPopover() { return import(/* webpackChunkName: "components/group_options_popover" */'../../../components/popover/group_options_popover') }
export function GroupMembers() { return import(/* webpackChunkName: "features/group_members" */'../../group_members') }
export function GroupRemovedAccounts() { return import(/* webpackChunkName: "features/group_removed_accounts" */'../../group_removed_accounts') }
export function GroupTimeline() { return import(/* webpackChunkName: "features/group_timeline" */'../../group_timeline') }

@ -7,6 +7,7 @@ export default class DefaultLayout extends PureComponent {
children: PropTypes.node.isRequired,
layout: PropTypes.object,
showBackBtn: PropTypes.bool,
noComposeButton: PropTypes.bool,
tabs: PropTypes.array,
title: PropTypes.string.isRequired,
}
@ -19,6 +20,7 @@ export default class DefaultLayout extends PureComponent {
showBackBtn,
tabs,
title,
noComposeButton,
} = this.props
return (
@ -28,6 +30,7 @@ export default class DefaultLayout extends PureComponent {
showBackBtn={showBackBtn}
tabs={tabs}
title={title}
noComposeButton={noComposeButton}
>
{children}
</Layout>

@ -17,6 +17,7 @@ export default class BasicPage extends PureComponent {
return (
<DefaultLayout
noComposeButton
showBackBtn
title={title}
layout={(

@ -33,7 +33,7 @@ class GroupPage extends ImmutablePureComponent {
static propTypes = {
intl: PropTypes.object.isRequired,
group: ImmutablePropTypes.map,
chilren: PropTypes.node.isRequired,
children: PropTypes.node.isRequired,
relationships: ImmutablePropTypes.map,
fetchGroup: PropTypes.func.isRequired,
}

@ -77,6 +77,10 @@ class HomePage extends PureComponent {
icon: 'ellipsis',
onClick: onOpenHomePageSettingsModal,
},
{
icon: 'search',
onClick: onOpenHomePageSettingsModal,
},
]}
layout={(
<Fragment>

@ -1,8 +1,11 @@
import { Fragment } from 'react'
import { defineMessages, injectIntl } from 'react-intl'
import { BREAKPOINT_EXTRA_SMALL } from '../constants'
import Responsive from '../features/ui/util/responsive_component'
import PageTitle from '../features/ui/util/page_title'
import LinkFooter from '../components/link_footer'
import SearchFilterPanel from '../components/panel/search_filter_panel'
import Search from '../components/search'
import Layout from '../layouts/layout'
const messages = defineMessages({
@ -59,6 +62,11 @@ class SearchPage extends PureComponent {
)}
>
<PageTitle path={title} />
<Responsive max={BREAKPOINT_EXTRA_SMALL}>
<Search />
</Responsive>
{children}
</Layout>
)

@ -19,11 +19,8 @@ const initialState = ImmutableMap({
home: ImmutableMap({
shows: ImmutableMap({
photos: true,
polls: true,
reply: true,
repost: true,
videos: true,
}),
}),

@ -36,6 +36,12 @@
--text_color_tertiary: #777;
--border_color_secondary: #ececed;
/* Navigation bar. Only themes. Non-editable */
--navigation_background: var(--color_brand);
--navigation_blend: var(--color_brand-dark);
--navigation_primary: var(--color_white);
--navigation_brand: var(--color_white);
}
:root[no-circle] {
@ -59,6 +65,12 @@
--text_color_tertiary: #656565 !important;
--border_color_secondary: #424141 !important;
/* Navigation bar. Only themes. Non-editable */
--navigation_background: #000 !important;
--navigation_blend: var(--text_color_secondary) !important;
--navigation_primary: var(--text_color_primary) !important;
--navigation_brand: var(--color_brand) !important;
}
:root[theme='black'] {
@ -74,6 +86,12 @@
--text_color_tertiary: #656565 !important;
--border_color_secondary: #212020 !important;
/* Navigation bar. Only themes. Non-editable */
--navigation_background: #000 !important;
--navigation_blend: var(--text_color_secondary) !important;
--navigation_primary: var(--text_color_primary) !important;
--navigation_brand: var(--color_brand) !important;
}
html,
@ -386,7 +404,7 @@ body {
/* */
.topNeg60PX { top: -60px; }
.topNeg80PX { top: -80px; }
.top0 { top: 0; }
.top80PX { top: 80px; }
.top60PC { top: 60%; }
@ -850,6 +868,30 @@ body {
padding-bottom: env(safe-area-inset-bottom, 0);
}
.bgNavigation {
background-color: var(--navigation_background);
}
.bgNavigationBlend {
background-color: var(--navigation_blend);
}
.fillNavigation {
fill: var(--navigation_primary);
}
.fillNavigationBlend {
fill: var(--navigation_blend);
}
.colorNavigation {
color: var(--navigation_primary);
}
.fillNavigationBrand {
fill: var(--navigation_brand);
}
/**
* Rich Text Editor
*/