Progress, Deck done

This commit is contained in:
mgabdev
2020-12-16 17:29:06 -05:00
parent 8f94ffad9c
commit 04053c0e31
20 changed files with 473 additions and 93 deletions

View File

@@ -1,8 +1,82 @@
import React from 'react'
import PropTypes from 'prop-types'
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { connect } from 'react-redux'
import { fetchAccount } from '../actions/accounts'
import DeckColumnHeader from './deck_column_header'
import Avatar from './avatar'
import DisplayName from './display_name'
import {
FONT_SIZES,
DEFAULT_FONT_SIZE,
FONT_SIZES_EXTRA_SMALL,
FONT_SIZES_SMALL,
FONT_SIZES_NORMAL,
FONT_SIZES_MEDIUM,
FONT_SIZES_LARGE,
FONT_SIZES_EXTRA_LARGE
} from '../constants'
class DeckColumn extends React.PureComponent {
class DeckColumn extends ImmutablePureComponent {
state = {
width: 360, // default
refreshBool: false,
}
componentDidMount() {
this.setWidth()
const { accountId, account } = this.props
if (!!accountId || !account) {
this.props.onFetchAccount(accountId)
}
}
componentDidUpdate(prevProps) {
if (prevProps.fontSize !== this.props.fontSize) {
this.setWidth()
}
}
setWidth = () => {
const { fontSize } = this.props
let width = 360
switch (FONT_SIZES[fontSize]) {
case FONT_SIZES_EXTRA_SMALL:
width = 310
break;
case FONT_SIZES_SMALL:
width = 320
break;
case FONT_SIZES_NORMAL:
width = 335
break;
case FONT_SIZES_MEDIUM:
width = 350
break;
case FONT_SIZES_LARGE:
width = 365
break;
case FONT_SIZES_EXTRA_LARGE:
width = 385
break;
default:
break;
}
this.setState({ width })
}
handleOnRefresh = () => {
//hacky
this.setState({ refreshBool: true })
setTimeout(() => {
this.setState({ refreshBool: false })
}, 100);
}
render() {
const {
@@ -12,20 +86,38 @@ class DeckColumn extends React.PureComponent {
children,
index,
noButtons,
noRefresh,
account,
} = this.props
const { width, refreshBool } = this.state
let newTitle = title
if (!!account) {
newTitle = (
<div className={[_s.d, _s.flexRow, _s.aiCenter].join(' ')}>
<Avatar account={account} noHover size={30} />
<div className={[_s.d, _s.ml10].join(' ')}>
<DisplayName account={account} noUsername isInline noHover />
</div>
</div>
)
}
return (
<div className={[_s.d, _s.w360PX, _s.px2, _s.bgSecondary, _s.h100VH].join(' ')}>
<div className={[_s.d, _s.px2, _s.bgSecondary, _s.h100VH].join(' ')} style={{ width: `${width}px` }}>
<div className={[_s.d, _s.w100PC, _s.bgPrimary, _s.h100VH].join(' ')}>
<DeckColumnHeader
title={title}
title={newTitle}
subtitle={subtitle}
icon={icon}
index={index}
noButtons={noButtons}
noRefresh={noRefresh}
onRefresh={this.handleOnRefresh}
/>
<div className={[_s.d, _s.w100PC, _s.overflowYScroll, _s.boxShadowNone, _s.posAbs, _s.top60PX, _s.left0, _s.right0, _s.bottom0].join(' ')}>
{children}
{ !refreshBool && children}
</div>
</div>
</div>
@@ -34,12 +126,32 @@ class DeckColumn extends React.PureComponent {
}
const mapStateToProps = (state, { accountId }) => {
const account = !!accountId ? state.getIn(['accounts', accountId]) : null
return {
account,
fontSize: state.getIn(['settings', 'displayOptions', 'fontSize'], DEFAULT_FONT_SIZE),
}
}
const mapDispatchToProps = (dispatch) => ({
onFetchAccount(accountId) {
dispatch(fetchAccount(accountId))
}
})
DeckColumn.propTypes = {
title: PropTypes.string,
subtitle: PropTypes.string,
icon: PropTypes.string,
index: PropTypes.number,
noButtons: PropTypes.bool,
noRefresh: PropTypes.bool,
onRefresh: PropTypes.func,
accountId: PropTypes.string,
account: ImmutablePropTypes.map,
onFetchAccount: PropTypes.func.isRequired,
}
export default DeckColumn
export default connect(mapStateToProps, mapDispatchToProps)(DeckColumn)

View File

@@ -13,7 +13,8 @@ class DeckColumnHeader extends React.PureComponent {
}
handleClickRefresh = () => {
const { onRefresh } = this.props
if (!!onRefresh) onRefresh()
}
render() {
@@ -23,34 +24,36 @@ class DeckColumnHeader extends React.PureComponent {
icon,
children,
noButtons,
noRefresh,
} = this.props
return (
<div data-sort-header className={[_s.d, _s.w100PC, _s.flexRow, _s.aiCenter, _s.h60PX, _s.px15, _s.py10, _s.borderBottom1PX, _s.borderColorSecondary, _s.bgPrimary].join(' ')}>
{
!!icon &&
<div data-sort-header className={[_s.d, _s.flexRow, _s.mr15, _s.cursorEWResize].join(' ')}>
<span className={[_s.d, _s.w1PX, _s.h24PX, _s.mr2, _s.bgSecondary].join(' ')} />
<span className={[_s.d, _s.w1PX, _s.h24PX, _s.mr2, _s.bgSecondary].join(' ')} />
<span className={[_s.d, _s.w1PX, _s.h24PX, _s.bgSecondary].join(' ')} />
</div>
}
{ !!icon && <Icon id={icon} className={_s.cPrimary} size='18px' /> }
<div className={[_s.d, _s.flexRow, _s.aiEnd, _s.ml15].join(' ')}>
<div data-sort-header className={[_s.d, _s.flexRow, _s.mr15, _s.cursorEWResize].join(' ')}>
<span className={[_s.d, _s.w1PX, _s.h24PX, _s.mr2, _s.bgSecondary].join(' ')} />
<span className={[_s.d, _s.w1PX, _s.h24PX, _s.mr2, _s.bgSecondary].join(' ')} />
<span className={[_s.d, _s.w1PX, _s.h24PX, _s.bgSecondary].join(' ')} />
</div>
{ !!icon && <Icon id={icon} className={[_s.cPrimary, _s.mr15].join(' ')} size='18px' /> }
<div className={[_s.d, _s.flexRow, _s.aiEnd].join(' ')}>
{ !!title && <Text size='extraLarge' weight='medium'>{title}</Text> }
{ !!subtitle && <Text className={_s.ml5} color='secondary'>{subtitle}</Text> }
</div>
{
!!title && !noButtons &&
<div className={[_s.d, _s.flexRow, _s.aiCenter, _s.mlAuto, _s.jcCenter].join(' ')}>
<Button
isNarrow
noClasses
onClick={this.handleClickRefresh}
className={[_s.d, _s.mr5, _s.cursorPointer, _s.outlineNone, _s.bgTransparent, _s.px5, _s.py5].join(' ')}
iconClassName={_s.cSecondary}
icon='repost'
/>
{
!noRefresh &&
<Button
isNarrow
noClasses
onClick={this.handleClickRefresh}
className={[_s.d, _s.mr5, _s.cursorPointer, _s.outlineNone, _s.bgTransparent, _s.px5, _s.py5].join(' ')}
iconClassName={_s.cSecondary}
icon='repost'
/>
}
<Button
isNarrow
noClasses
@@ -73,6 +76,8 @@ DeckColumnHeader.propTypes = {
icon: PropTypes.string,
index: PropTypes.number,
noButtons: PropTypes.bool,
noRefresh: PropTypes.bool,
onRefresh: PropTypes.func,
}
export default connect()(DeckColumnHeader)

View File

@@ -7,13 +7,19 @@ import { openModal } from '../../actions/modal'
import { setDeckColumnAtIndex } from '../../actions/deck'
import { getOrderedLists, getListOfGroups } from '../../selectors'
import { fetchLists } from '../../actions/lists'
import {
fetchDeckAccountSuggestions,
clearDeckAccountSuggestions,
} from '../../actions/deck'
import { fetchGroupsByTab } from '../../actions/groups'
import { MODAL_DECK_COLUMN_ADD } from '../../constants'
import Account from '../account'
import Heading from '../heading'
import Button from '../button'
import Block from '../block'
import Input from '../input'
import List from '../list'
import Text from '../text'
class DeckColumnAddOptionsModal extends ImmutablePureComponent {
@@ -50,17 +56,24 @@ class DeckColumnAddOptionsModal extends ImmutablePureComponent {
this.setState({ hashtagValue: '' })
}
handleAddUser = (userId) => {
this.setState({ usernameValue: '' })
this.props.onClearDeckAccountSuggestions()
if (!!userId) this.handleAdd(`user.${userId}`)
}
onChangeHashtagValue = (hashtagValue) => {
this.setState({ hashtagValue })
}
onChangeUsernameValue = (usernameValue) => {
this.setState({ usernameValue })
this.props.onFetchUserSuggestions(usernameValue)
}
getContentForColumn = () => {
const { column, lists, groups, accounts } = this.props
const { hashtagValue } = this.state
const { column, lists, groups, suggestionsIds } = this.props
const { hashtagValue, usernameValue } = this.state
if (column === 'hashtag') {
return (
@@ -107,17 +120,39 @@ class DeckColumnAddOptionsModal extends ImmutablePureComponent {
/>
</div>
)
} else if (column === 'group') {
} else if (column === 'user') {
return (
<div className={[_s.d, _s.px15, _s.py10].join(' ')}>
<Input
type='text'
value={usernameValue}
placeholder=''
id='user-deck'
title='Enter username'
onChange={this.onChangeUsernameValue}
/>
<div className={[_s.d, _s.width100PC].join(' ')}>
<div className={[_s.d, _s.px15, _s.py10].join(' ')}>
<Input
type='text'
value={usernameValue}
placeholder=''
id='user-deck'
title='Enter username'
onChange={this.onChangeUsernameValue}
/>
</div>
<div className={[_s.d, _s.pt10].join(' ')}>
<div className={[_s.d].join(' ')}>
<Text weight='bold' size='small' color='secondary' className={[_s.d, _s.px15, _s.ml15, _s.mt5, _s.mb15].join(' ')}>
Search results ({suggestionsIds.size})
</Text>
{
suggestionsIds &&
suggestionsIds.map((accountId) => (
<Account
compact
key={`create-deck-user-${accountId}`}
id={accountId}
onActionClick={() => this.handleAddUser(accountId)}
actionIcon='add'
/>
))
}
</div>
</div>
</div>
)
}
@@ -175,7 +210,7 @@ class DeckColumnAddOptionsModal extends ImmutablePureComponent {
const mapStateToProps = (state) => ({
lists: getOrderedLists(state),
groups: getListOfGroups(state, { type: 'member' }),
accounts: [],
suggestionsIds: state.getIn(['deck', 'accountSuggestions']),
})
const mapDispatchToProps = (dispatch) => ({
@@ -191,6 +226,12 @@ const mapDispatchToProps = (dispatch) => ({
onOpenDeckColumnAddModal() {
dispatch(openModal(MODAL_DECK_COLUMN_ADD))
},
onFetchUserSuggestions(query) {
dispatch(fetchDeckAccountSuggestions(query))
},
onClearDeckAccountSuggestions() {
dispatch(clearDeckAccountSuggestions())
}
})
DeckColumnAddOptionsModal.propTypes = {

View File

@@ -1,74 +1,152 @@
import React from 'react'
import PropTypes from 'prop-types'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import { connect } from 'react-redux'
import { closePopover } from '../../actions/popover'
import { getListOfGroups } from '../../selectors'
import { fetchGroupsByTab } from '../../actions/groups'
import { changeComposeGroupId } from '../../actions/compose'
import PopoverLayout from './popover_layout'
import List from '../list'
import Button from '../button'
import Text from '../text'
class ComposePostDesinationPopover extends React.PureComponent {
class ComposePostDesinationPopover extends ImmutablePureComponent {
state = {
isGroupsSelected: false,
}
componentDidMount() {
if (this.props.composeGroupId) {
this.setState({ isGroupsSelected: true })
}
}
componentDidUpdate (prevProps) {
if (prevProps.composeGroupId !== this.props.composeGroupId) {
this.setState({ isGroupsSelected: !!this.props.composeGroupId })
}
}
handleOnClosePopover = () => {
this.props.onClosePopover()
}
render() {
const {
isXS,
} = this.props
selectDestination = (destination) => {
const isGroupsSelected = destination === 'group'
this.setState({ isGroupsSelected })
if (isGroupsSelected) {
this.props.onFetchMemberGroups()
} else {
this.handleSelectGroup(null)
}
}
// TIMELINE
// GROUP - MY GROUPS
const items = [
handleSelectGroup = (groupId) => {
this.props.onChangeComposeGroupId(groupId)
this.handleOnClosePopover()
}
render() {
const { isXS, groups, composeGroupId } = this.props
const { isGroupsSelected } = this.state
const mainItems = [
{
hideArrow: true,
title: 'Timeline',
onClick: () => this.handleOnDelete(),
isActive: !isGroupsSelected,
onClick: () => this.selectDestination('home'),
},
{
title: 'Group',
onClick: () => this.handleOnReport(),
isActive: isGroupsSelected,
onClick: () => this.selectDestination('group'),
},
]
const groupItems = !!groups ? groups.map((group) => ({
hideArrow: true,
onClick: () => this.handleSelectGroup(group.get('id')),
title: group.get('title'),
isActive: group.get('id') === composeGroupId,
})) : []
return (
<PopoverLayout
width={180}
width={isGroupsSelected ? 320 : 180}
isXS={isXS}
onClose={this.handleOnClosePopover}
>
<div className={[_s.d]}>
<Text className={[_s.d, _s.px15, _s.py10, _s.bgSecondary].join(' ')}>Post to:</Text>
<List items={items} />
</div>
<div>
<Text className={[_s.d, _s.px15, _s.py10, _s.bgSecondary].join(' ')}>
<Button
isText
icon='back'
/>
Select group:
</Text>
<List items={items} />
</div>
{
!isGroupsSelected &&
<div className={[_s.d, _s.w100PC].join(' ')}>
<Text className={[_s.d, _s.px15, _s.py10, _s.bgSecondary].join(' ')}>Post to:</Text>
<List items={mainItems} />
</div>
}
{
isGroupsSelected &&
<div className={[_s.d, _s.w100PC].join(' ')}>
<div className={[_s.d, _s.flexRow, _s.bgSecondary].join(' ')}>
<Button
isText
icon='back'
color='primary'
backgroundColor='none'
className={[_s.aiCenter, _s.jcCenter, _s.pl15, _s.pr5].join(' ')}
onClick={() => this.selectDestination('home')}
/>
<Text className={[_s.d, _s.pl5, _s.py10].join(' ')}>
Select group:
</Text>
</div>
<div className={[_s.d, _s.w100PC, _s.overflowYScroll, _s.maxH340PX].join(' ')}>
<List
scrollKey='groups-post-destination-add'
showLoading={groups.size === 0}
emptyMessage="You are not a member of any groups yet."
items={groupItems}
/>
</div>
</div>
}
</PopoverLayout>
)
}
}
const mapStateToProps = (state) => ({
//
})
const mapStateToProps = (state) => {
const composeGroupId = state.getIn(['compose', 'group_id'])
return {
composeGroupId,
composeGroup: state.getIn(['groups', composeGroupId]),
groups: getListOfGroups(state, { type: 'member' }),
}
}
const mapDispatchToProps = (dispatch) => ({
onClosePopover: () => dispatch(closePopover()),
onClosePopover() {
dispatch(closePopover())
},
onFetchMemberGroups() {
dispatch(fetchGroupsByTab('member'))
},
onChangeComposeGroupId(groupId) {
dispatch(changeComposeGroupId(groupId))
}
})
ComposePostDesinationPopover.propTypes = {
isXS: PropTypes.bool,
onClosePopover: PropTypes.func.isRequired,
onFetchMemberGroups: PropTypes.func.isRequired,
onChangeComposeGroupId: PropTypes.func.isRequired,
groups: ImmutablePropTypes.list,
composeGroup: ImmutablePropTypes.map,
}
export default connect(mapStateToProps, mapDispatchToProps)(ComposePostDesinationPopover)

View File

@@ -3,7 +3,9 @@ import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { defineMessages, injectIntl } from 'react-intl'
import { closePopover } from '../../actions/popover'
import { openModal } from '../../actions/modal'
import { meUsername } from '../../initial_state'
import { MODAL_DISPLAY_OPTIONS } from '../../constants'
import PopoverLayout from './popover_layout'
import List from '../list'
@@ -13,6 +15,11 @@ class NavSettingsPopover extends React.PureComponent {
this.props.onClosePopover()
}
handleOpenDisplayOptions = () => {
this.props.onOpenDisplayOptions()
this.handleOnClosePopover()
}
render() {
const { intl, isXS } = this.props
@@ -29,6 +36,10 @@ class NavSettingsPopover extends React.PureComponent {
to: `/${meUsername}`,
onClick: this.handleOnClosePopover,
},
{
title: 'Display options',
onClick: this.handleOpenDisplayOptions,
},
{
title: intl.formatMessage(messages.help),
href: 'https://help.gab.com',
@@ -60,6 +71,9 @@ const mapDispatchToProps = (dispatch) => ({
onClosePopover() {
dispatch(closePopover())
},
onOpenDisplayOptions() {
dispatch(openModal(MODAL_DISPLAY_OPTIONS))
}
})
NavSettingsPopover.propTypes = {