gab-social/app/javascript/gabsocial/features/list_edit.js

318 lines
9.8 KiB
JavaScript

import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { injectIntl, defineMessages } from 'react-intl'
import isObject from 'lodash.isobject'
import {
setupListEditor,
resetListEditor,
removeFromListEditor,
addToListEditor,
fetchListSuggestions,
clearListSuggestions,
changeListSuggestions,
} from '../actions/lists'
import { openModal } from '../actions/modal'
import {
MODAL_LIST_EDITOR,
MODAL_LIST_DELETE,
} from '../constants'
import Account from '../components/account'
import Button from '../components/button'
import Divider from '../components/divider'
import Input from '../components/input'
import TabBar from '../components/tab_bar'
import Text from '../components/text'
class ListEdit extends ImmutablePureComponent {
state = {
activeTab: this.props.tab || 'members'
}
componentDidMount() {
const { onInitialize, listId } = this.props
if (listId) {
onInitialize(listId)
}
}
componentDidUpdate(prevProps) {
if (this.props.listId !== prevProps.listId) {
this.props.onInitialize(this.props.listId)
}
}
componentWillUnmount() {
this.props.onReset()
}
handleChangeTab = (tab) => {
this.setState({ activeTab: tab })
}
onClickClose = () => {
this.props.onClose(MODAL_LIST_EDITOR)
}
handleOnDeleteList = () => {
this.props.onDeleteList(this.props.list)
}
handleAddOrRemoveFromList = (accountId) => {
if (this.props.accountIds.includes(accountId)) {
this.props.onRemoveAccountFromList(accountId)
} else {
this.props.onAddAccountToList(accountId)
}
}
handleSearchSuggestionsChange = (value) => {
this.props.onChangeSuggestions(value)
}
handleSearchSuggestionsKeyUp = (e) => {
if (e.keyCode === 13) {
this.props.onSubmitSearchSuggestions(this.props.searchSuggestionsValue)
}
}
handleSearchSuggestionsSubmit = () => {
this.props.onSubmitSearchSuggestions(this.props.searchSuggestionsValue)
}
render() {
const {
title,
accountIds,
searchAccountIds,
intl,
searchSuggestionsValue,
} = this.props
const { activeTab } = this.state
// : todo : save new list title
return (
<div>
<div className={[_s.d, _s.borderTop1PX, _s.borderColorSecondary].join(' ')}>
<div className={[_s.d, _s.z4, _s.bgPrimary, _s.px15, _s.top0, _s.posSticky, _s.borderBottom1PX, _s.borderColorSecondary,].join(' ')}>
<TabBar
tabs={[
{
title: 'Members list',
onClick: () => this.handleChangeTab('members'),
active: activeTab === 'members',
},
{
title: 'Add new',
onClick: () => this.handleChangeTab('add-new'),
active: activeTab === 'add-new',
},
{
title: 'Settings',
onClick: () => this.handleChangeTab('settings'),
active: activeTab === 'settings',
},
]}
/>
</div>
{
activeTab === 'members' &&
<div className={[_s.d, _s.mb10, _s.py10].join(' ')}>
<div className={[_s.d, _s.pb10, _s.pt5].join(' ')}>
<div className={[_s.d].join(' ')}>
<Text weight='bold' size='small' color='secondary' className={[_s.d, _s.px15, _s.mt5, _s.mb15].join(' ')}>
Total members ({accountIds.size})
</Text>
{
accountIds &&
accountIds.map((accountId) => (
<Account
compact
key={`remove-from-list-${accountId}`}
id={accountId}
onActionClick={() => this.handleAddOrRemoveFromList(accountId)}
actionIcon='subtract'
/>
))
}
</div>
</div>
</div>
}
{
activeTab === 'settings' &&
<div className={[_s.d, _s.mb10, _s.pb10, _s.px15].join(' ')}>
<div className={[_s.d, _s.py15].join(' ')}>
<Input
title={intl.formatMessage(messages.editListTitle)}
placeholder='My new list title...'
value={title}
// onChange={onChange}
// onSubmit={onSubmit}
// disabled={disabled}
/>
</div>
<Divider />
<div className={_s.mb10}>
<Button
onClick={this.handleOnDeleteList}
backgroundColor='danger'
>
Delete List
</Button>
</div>
<Text size='extraSmall' color='secondary'>
Once you delete a list you cannot retrieve it.
</Text>
</div>
}
{
activeTab === 'add-new' &&
<div className={[_s.d, _s.mb10, _s.py10].join(' ')}>
<div className={[_s.d, _s.px15].join(' ')}>
<Input
placeholder={intl.formatMessage(messages.search)}
value={searchSuggestionsValue}
onChange={this.handleSearchSuggestionsChange}
onKeyUp={this.handleSearchSuggestionsKeyUp}
handleSubmit={this.handleSearchSuggestionsSubmit}
title={intl.formatMessage(messages.searchTitle)}
prependIcon='search'
hideLabel
/>
</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.mt5, _s.mb15].join(' ')}>
Search results ({searchAccountIds.size})
</Text>
{
searchAccountIds &&
searchAccountIds.map((accountId) => {
if (accountIds.includes(accountId)) return null
return (
<Account
key={`add-to-list-${accountId}`}
id={accountId}
compact
onActionClick={() => this.handleAddOrRemoveFromList(accountId)}
actionIcon='add'
/>
)
})
}
</div>
</div>
</div>
}
</div>
</div>
)
}
}
const messages = defineMessages({
close: { id: 'lightbox.close', defaultMessage: 'Close' },
save: { id: 'lists.new.save_title', defaultMessage: 'Save Title' },
changeTitle: { id: 'lists.edit.submit', defaultMessage: 'Change title' },
addToList: { id: 'lists.account.add', defaultMessage: 'Add to list' },
removeFromList: { id: 'lists.account.remove', defaultMessage: 'Remove from list' },
editList: { id: 'lists.edit', defaultMessage: 'Edit list' },
editListTitle: { id: 'lists.new.edit_title_placeholder', defaultMessage: 'Edit list title' },
remove: { id: 'lists.account.remove', defaultMessage: 'Remove from list' },
add: { id: 'lists.account.add', defaultMessage: 'Add to list' },
search: { id: 'lists.search', defaultMessage: 'Search people...' },
searchMembers: { id: 'lists.search_members', defaultMessage: 'Search members...' },
searchTitle: { id: 'tabs_bar.search', defaultMessage: 'Search' },
})
const mapStateToProps = (state, { params, id }) => {
const listId = isObject(params) ? params['id'] : id
return {
listId,
list: state.getIn(['lists', listId]),
title: state.getIn(['listEditor', 'title']),
disabled: !state.getIn(['listEditor', 'isChanged']),
accountIds: state.getIn(['listEditor', 'accounts', 'items']),
searchAccountIds: state.getIn(['listEditor', 'suggestions', 'items']),
searchSuggestionsValue: state.getIn(['listEditor', 'suggestions', 'value']),
}
}
const mapDispatchToProps = (dispatch) => ({
onDeleteList(list) {
dispatch(openModal(MODAL_LIST_DELETE, { list }))
},
onChangeTitle(value) {
dispatch(changeListEditorTitle(value))
},
onUpdateList() {
dispatch(submitListEditor(false))
},
onInitialize(listId) {
dispatch(setupListEditor(listId))
},
onReset() {
dispatch(resetListEditor())
},
onRemoveAccountFromList(accountId) {
dispatch(removeFromListEditor(accountId))
},
onAddAccountToList(accountId) {
dispatch(addToListEditor(accountId))
},
onSubmitSearchSuggestions(value) {
dispatch(fetchListSuggestions(value))
},
onClearSearchSuggestions() {
dispatch(clearListSuggestions())
},
onChangeSuggestions(value) {
dispatch(changeListSuggestions(value))
},
})
ListEdit.propTypes = {
list: ImmutablePropTypes.map,
title: PropTypes.string,
listId: PropTypes.string.isRequired,
onClose: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
onInitialize: PropTypes.func.isRequired,
onReset: PropTypes.func.isRequired,
searchSuggestionsValue: PropTypes.string.isRequired,
accountIds: ImmutablePropTypes.list.isRequired,
searchAccountIds: ImmutablePropTypes.list.isRequired,
onRemoveAccountFromList: PropTypes.func.isRequired,
onAddAccountToList: PropTypes.func.isRequired,
onChangeSuggestions: PropTypes.func.isRequired,
onClearSearchSuggestions: PropTypes.func.isRequired,
onSubmitSearchSuggestions: PropTypes.func.isRequired,
onDeleteList: PropTypes.func.isRequired,
tab: PropTypes.string,
}
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(ListEdit))