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

444 lines
13 KiB
JavaScript
Raw Normal View History

import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
2020-03-06 15:38:22 +00:00
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
2020-02-29 15:42:47 +00:00
import { defineMessages, injectIntl } from 'react-intl'
2020-03-06 15:38:22 +00:00
import isObject from 'lodash.isobject'
2020-04-28 06:33:58 +01:00
import {
changeGroupTitle,
changeGroupPassword,
2020-04-28 06:33:58 +01:00
changeGroupDescription,
changeGroupCoverImage,
2020-08-06 05:59:14 +01:00
changeGroupId,
changeGroupTags,
changeGroupCategory,
changeGroupIsPrivate,
changeGroupIsVisible,
2020-04-28 06:33:58 +01:00
submit,
setGroup,
resetEditor,
} from '../actions/group_editor'
import {
openModal,
closeModal,
} from '../actions/modal'
2020-08-06 05:59:14 +01:00
import { fetchGroup } from '../actions/groups'
import { fetchGroupCategories } from '../actions/group_categories'
import { me } from '../initial_state'
import { MODAL_PRO_UPGRADE } from '../constants'
2020-04-28 06:33:58 +01:00
import ColumnIndicator from '../components/column_indicator'
2020-03-05 15:44:17 +00:00
import Button from '../components/button'
import Divider from '../components/divider'
import Input from '../components/input'
import Text from '../components/text'
2020-04-28 06:33:58 +01:00
import Form from '../components/form'
2020-08-06 05:59:14 +01:00
import Switch from '../components/switch'
import Select from '../components/select'
2020-03-05 15:44:17 +00:00
import Textarea from '../components/textarea'
import FileInput from '../components/file_input'
2019-07-02 08:10:25 +01:00
2020-04-28 06:33:58 +01:00
class GroupCreate extends ImmutablePureComponent {
2019-07-02 08:10:25 +01:00
2019-07-17 19:22:19 +01:00
static contextTypes = {
router: PropTypes.object
}
2020-04-28 06:33:58 +01:00
componentDidMount() {
2020-08-06 05:59:14 +01:00
const { groupId, group } = this.props
this.props.onFetchGroupCategories()
if (!group) {
if (groupId) {
this.props.onFetchGroup(groupId)
} else {
this.props.onResetEditor()
}
2020-03-06 15:38:22 +00:00
} else {
2020-08-06 05:59:14 +01:00
this.props.onSetGroup(group)
2020-03-06 15:38:22 +00:00
}
}
componentWillReceiveProps(nextProps) {
if (this.props.group !== nextProps.group && !!nextProps.group) {
2020-04-28 06:33:58 +01:00
this.props.onSetGroup(nextProps.group)
2020-03-06 15:38:22 +00:00
}
2019-07-17 19:22:19 +01:00
}
2020-04-28 06:33:58 +01:00
handleCoverImageChange = (e) => {
try {
this.props.onCoverImageChange(e.target.files[0])
} catch (error) {
//
}
2019-07-17 19:22:19 +01:00
}
2020-04-28 06:33:58 +01:00
handleSubmit = (e) => {
2020-02-29 15:42:47 +00:00
e.preventDefault()
// if not pro and not admin of group show pro upgrade modal
if (!this.props.isPro && !this.props.isAdmin) {
this.props.onOpenProUpgradeModal()
return
}
2020-05-14 21:45:39 +01:00
if (this.props.onClose) this.props.onClose()
2020-02-29 15:42:47 +00:00
this.props.onSubmit(this.context.router.history)
2019-07-17 19:22:19 +01:00
}
2020-02-29 15:42:47 +00:00
render() {
2020-03-05 15:44:17 +00:00
const {
2020-03-06 15:38:22 +00:00
group,
error,
2020-04-28 06:33:58 +01:00
titleValue,
descriptionValue,
passwordValue,
2020-03-05 15:44:17 +00:00
coverImage,
2020-04-28 06:33:58 +01:00
intl,
onTitleChange,
onChangeGroupPassword,
2020-04-28 06:33:58 +01:00
onDescriptionChange,
2020-08-06 05:59:14 +01:00
onChangeGroupId,
onChangeGroupTags,
onChangeGroupCategory,
onChangeGroupIsPrivate,
onChangeGroupIsVisible,
2020-04-28 06:33:58 +01:00
isSubmitting,
onSubmit,
2020-08-06 05:59:14 +01:00
idValue,
tags,
category,
isPrivate,
isVisible,
groupId,
categories,
isAdmin,
isPro,
2020-03-05 15:44:17 +00:00
} = this.props
2019-07-17 19:22:19 +01:00
2020-08-06 05:59:14 +01:00
if (!group && groupId) {
return <ColumnIndicator type='loading' />
} else if ((!group && error) || (groupId && !isAdmin)) {
2020-03-06 15:38:22 +00:00
return <ColumnIndicator type='missing' />
} else if (!isPro && !groupId) {
return <ColumnIndicator type='error' message={(
<React.Fragment>
<Text>You must be a GabPRO member to create a group.</Text>
<Button
onClick={this.props.onOpenProUpgradeModal}
className={[_s.mt15, _s.mlAuto, _s.mrAuto].join(' ')}
>
Learn more
</Button>
</React.Fragment>
)} />
2020-03-06 15:38:22 +00:00
}
2020-08-06 05:59:14 +01:00
const memberCount = group ? group.get('member_count') : 0
const hasGroupSlug = group ? !!group.get('slug') : false
let categoriesOptions = [{'title':'',value:''}]
2020-08-06 05:59:14 +01:00
if (categories) {
for (let i = 0; i < categories.count(); i++) {
const c = categories.get(i)
categoriesOptions.push({
title: c.get('text'),
value: c.get('id'),
})
}
}
const submitDisabled = ((!titleValue || !category || !descriptionValue) && !groupId) || isSubmitting
2019-07-17 19:22:19 +01:00
return (
2020-04-28 06:33:58 +01:00
<Form onSubmit={onSubmit}>
2020-03-05 15:44:17 +00:00
<Input
2020-08-06 05:59:14 +01:00
id='group-title'
title={`${intl.formatMessage(messages.title)} *`}
2020-04-28 06:33:58 +01:00
value={titleValue}
onChange={onTitleChange}
disabled={isSubmitting}
placeholder={intl.formatMessage(messages.titlePlaceholder)}
isRequired
2020-03-05 15:44:17 +00:00
/>
2020-04-23 07:13:29 +01:00
<Divider isInvisible />
2020-03-05 15:44:17 +00:00
2020-08-06 05:59:14 +01:00
{
memberCount >= 50 && !hasGroupSlug &&
<React.Fragment>
2020-08-06 05:59:14 +01:00
<Input
id='group-id'
title={intl.formatMessage(messages.idTitle)}
value={idValue}
onChange={onChangeGroupId}
disabled={isSubmitting}
/>
<Text className={[_s.mt5, _s.pl15]} size='small' color='secondary'>
{
!!idValue &&
<b>g/{idValue}&nbsp;&nbsp;</b>
}
{intl.formatMessage(messages.idDescription)}
</Text>
<Divider isInvisible />
</React.Fragment>
2020-08-06 05:59:14 +01:00
}
<Textarea
title={`${intl.formatMessage(messages.description)} *`}
value={descriptionValue}
onChange={onDescriptionChange}
placeholder={intl.formatMessage(messages.descriptionPlaceholder)}
2020-08-06 05:59:14 +01:00
disabled={isSubmitting}
isRequired
2020-08-06 05:59:14 +01:00
/>
<Divider isInvisible />
<div className={_s.d}>
2020-08-06 05:59:14 +01:00
<Text className={[_s.pl15, _s.mb10].join(' ')} size='small' weight='medium' color='secondary'>
{intl.formatMessage(messages.categoryTitle)} *
2020-08-06 05:59:14 +01:00
</Text>
<Select
value={category}
onChange={onChangeGroupCategory}
options={categoriesOptions}
/>
<Text className={[_s.mt5, _s.pl15].join(' ')} size='small' color='tertiary'>
2020-08-06 05:59:14 +01:00
{intl.formatMessage(messages.categoryDescription)}
</Text>
<Divider isInvisible />
</div>
<Input
id='group-tags'
title={intl.formatMessage(messages.tagsTitle)}
value={tags}
onChange={onChangeGroupTags}
2020-04-28 06:33:58 +01:00
disabled={isSubmitting}
2020-03-05 15:44:17 +00:00
/>
<Text className={[_s.mt5, _s.pl15, _s.mb15, _s.pb5].join(' ')} size='small' color='tertiary'>
{intl.formatMessage(messages.tagsDescription)}
</Text>
2020-03-05 15:44:17 +00:00
<Divider />
2020-03-05 15:44:17 +00:00
<FileInput
2020-04-28 06:33:58 +01:00
disabled={isSubmitting}
id='group-cover-photo'
2020-03-05 15:44:17 +00:00
title={intl.formatMessage(coverImage === null ? messages.coverImage : messages.coverImageChange)}
onChange={this.handleCoverImageChange}
file={group ? group.get('cover_image_url') : undefined}
2020-03-05 15:44:17 +00:00
width='340px'
height='145px'
isBordered
2020-03-05 15:44:17 +00:00
/>
<Text className={[_s.mt5, _s.pl15, _s.mb15, _s.pb5].join(' ')} size='small' color='tertiary'>
2020-08-06 05:59:14 +01:00
{intl.formatMessage(messages.coverImageDescription)}
</Text>
<Divider />
2020-03-05 15:44:17 +00:00
<Input
id='group-password'
title={intl.formatMessage(messages.passwordTitle)}
value={passwordValue}
onChange={onChangeGroupPassword}
disabled={isSubmitting}
2020-08-06 05:59:14 +01:00
/>
<Text className={[_s.mt5, _s.pl15, _s.mb15, _s.pb5].join(' ')} size='small' color='tertiary'>
{intl.formatMessage(messages.passwordDescription)}
2020-08-06 05:59:14 +01:00
</Text>
<Divider />
<div className={[_s.d, _s.pl15].join(' ')}>
<Switch
label={'Private'}
id='group-isprivate'
checked={isPrivate}
onChange={onChangeGroupIsPrivate}
labelProps={{
size: 'small',
weight: 'medium',
color: 'secondary',
}}
/>
<Text className={_s.mt5} size='small' color='tertiary'>
{intl.formatMessage(messages.isPrivateDescription)}
</Text>
<Divider isInvisible />
<Switch
label={'Visible'}
id='group-isvisible'
checked={isVisible}
onChange={onChangeGroupIsVisible}
labelProps={{
size: 'small',
weight: 'medium',
color: 'secondary',
}}
/>
<Text className={_s.mt5} size='small' color='tertiary'>
{intl.formatMessage(messages.isVisibleDescription)}
</Text>
</div>
2020-08-06 05:59:14 +01:00
<Divider isInvisible />
2020-04-28 06:33:58 +01:00
<Button
isDisabled={submitDisabled}
2020-04-28 06:33:58 +01:00
onClick={this.handleSubmit}
>
<Text color='inherit' align='center' weight='medium'>
2020-03-06 15:38:22 +00:00
{intl.formatMessage(!!group ? messages.update : messages.create)}
2020-03-05 15:44:17 +00:00
</Text>
</Button>
2020-04-28 06:33:58 +01:00
</Form>
2020-02-29 15:42:47 +00:00
)
2019-07-17 19:22:19 +01:00
}
2019-07-02 08:10:25 +01:00
}
const messages = defineMessages({
title: { id: 'groups.form.title', defaultMessage: 'Title' },
idTitle: { id: 'groups.form.id_title', defaultMessage: 'Unique id' },
idDescription: { id: 'groups.form.id_description', defaultMessage: 'A unique id that links to this group. (Cannot be changed)' },
tagsTitle: { id: 'groups.form.tags_title', defaultMessage: 'Tags' },
tagsDescription: { id: 'groups.form.tags_description', defaultMessage: '(Optional) Add tags seperated by commas to increase group visibility' },
passwordTitle: { id: 'groups.form.password_title', defaultMessage: 'Password' },
passwordDescription: { id: 'groups.form.password_description', defaultMessage: '(Optional) Add a password to restrict access to this group. This password is NOT encrypted and is only visible to group admins.' },
categoryTitle: { id: 'groups.form.category_title', defaultMessage: 'Category' },
categoryDescription: { id: 'groups.form.category_description', defaultMessage: 'Add a general category for your group' },
description: { id: 'groups.form.description', defaultMessage: 'Description' },
coverImage: { id: 'groups.form.coverImage', defaultMessage: 'Cover image' },
coverImageDescription: { id: 'groups.form.coverImage_description', defaultMessage: '(Optional) Max: 5MB. Accepted image types: .jpg, .png' },
coverImageChange: { id: 'groups.form.coverImageChange', defaultMessage: 'Cover image selected' },
create: { id: 'groups.form.create', defaultMessage: 'Create group' },
update: { id: 'groups.form.update', defaultMessage: 'Update group' },
titlePlaceholder: { id: 'groups.form.title_placeholder', defaultMessage: 'New group title...' },
descriptionPlaceholder: { id: 'groups.form.description_placeholder', defaultMessage: 'This group is about...' },
isPrivateDescription: { id: 'groups.form.is_private_description', defaultMessage: 'Only members can see group posts.' },
isVisibleDescription: { id: 'groups.form.is_visible_description', defaultMessage: 'Anyone can find a visible group in search and other places on Gab.' },
})
const mapStateToProps = (state, { params }) => {
const groupId = isObject(params) ? params['id'] : null
const group = state.getIn(['groups', groupId])
let isAdmin = false
if (groupId) {
const relationships = state.getIn(['group_relationships', groupId])
if (relationships) {
isAdmin = relationships.get('admin')
}
}
return {
group,
groupId,
isAdmin,
error: (groupId && !group) || (group && !isAdmin),
titleValue: state.getIn(['group_editor', 'title']),
passwordValue: state.getIn(['group_editor', 'password']),
descriptionValue: state.getIn(['group_editor', 'description']),
coverImage: state.getIn(['group_editor', 'coverImage']),
isSubmitting: state.getIn(['group_editor', 'isSubmitting']),
idValue: state.getIn(['group_editor', 'id']),
tags: state.getIn(['group_editor', 'tags']),
category: state.getIn(['group_editor', 'category']),
isPrivate: state.getIn(['group_editor', 'isPrivate']),
isVisible: state.getIn(['group_editor', 'isVisible']),
categories: state.getIn(['group_categories', 'items']),
isPro: state.getIn(['accounts', me, 'is_pro']),
}
}
const mapDispatchToProps = (dispatch) => ({
onTitleChange(value) {
dispatch(changeGroupTitle(value))
},
onDescriptionChange(value) {
dispatch(changeGroupDescription(value))
},
onChangeGroupPassword(value) {
dispatch(changeGroupPassword(value))
},
onCoverImageChange(imageData) {
dispatch(changeGroupCoverImage(imageData))
},
onChangeGroupId(value) {
dispatch(changeGroupId(value))
},
onChangeGroupTags(value) {
dispatch(changeGroupTags(value))
},
onChangeGroupCategory(e) {
dispatch(changeGroupCategory(e.target.value))
},
onChangeGroupIsPrivate(checked) {
dispatch(changeGroupIsPrivate(checked))
},
onChangeGroupIsVisible(checked) {
dispatch(changeGroupIsVisible(checked))
},
onResetEditor() {
dispatch(resetEditor())
},
onSetGroup(group) {
dispatch(setGroup(group))
},
onSubmit(routerHistory) {
dispatch(submit(routerHistory))
dispatch(closeModal())
},
onFetchGroup(groupId) {
dispatch(fetchGroup(groupId))
},
onFetchGroupCategories() {
dispatch(fetchGroupCategories())
},
onOpenProUpgradeModal() {
dispatch(openModal(MODAL_PRO_UPGRADE))
},
})
GroupCreate.propTypes = {
group: ImmutablePropTypes.map,
titleValue: PropTypes.string.isRequired,
descriptionValue: PropTypes.string.isRequired,
coverImage: PropTypes.object,
intl: PropTypes.object.isRequired,
onTitleChange: PropTypes.func.isRequired,
onDescriptionChange: PropTypes.func.isRequired,
onChangeGroupId: PropTypes.func.isRequired,
onChangeGroupTags: PropTypes.func.isRequired,
onChangeGroupPassword: PropTypes.func.isRequired,
onChangeGroupCategory: PropTypes.func.isRequired,
onChangeGroupIsPrivate: PropTypes.func.isRequired,
onChangeGroupIsVisible: PropTypes.func.isRequired,
onFetchGroup: PropTypes.func.isRequired,
onFetchGroupCategories: PropTypes.func.isRequired,
onResetEditor: PropTypes.func.isRequired,
onSetGroup: PropTypes.func.isRequired,
onSubmit: PropTypes.func.isRequired,
onOpenProUpgradeModal: PropTypes.func.isRequired,
isSubmitting: PropTypes.bool,
isAdmin: PropTypes.bool,
onClose: PropTypes.func,
idValue: PropTypes.string.isRequired,
tags: PropTypes.string.isRequired,
category: PropTypes.string.isRequired,
isPrivate: PropTypes.bool.isRequired,
isVisible: PropTypes.bool.isRequired,
categories: ImmutablePropTypes.list.isRequired,
}
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(GroupCreate))