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 { defineMessages, injectIntl } from 'react-intl' import isObject from 'lodash.isobject' import { changeGroupTitle, changeGroupPassword, changeGroupDescription, changeGroupCoverImage, changeGroupId, changeGroupTags, changeGroupCategory, changeGroupIsPrivate, changeGroupIsVisible, submit, setGroup, resetEditor, } from '../actions/group_editor' import { openModal, closeModal, } from '../actions/modal' import { fetchGroup } from '../actions/groups' import { fetchGroupCategories } from '../actions/group_categories' import { me } from '../initial_state' import { MODAL_PRO_UPGRADE } from '../constants' import ColumnIndicator from '../components/column_indicator' import Button from '../components/button' import Divider from '../components/divider' import Input from '../components/input' import Text from '../components/text' import Form from '../components/form' import Switch from '../components/switch' import Select from '../components/select' import Textarea from '../components/textarea' import FileInput from '../components/file_input' class GroupCreate extends ImmutablePureComponent { static contextTypes = { router: PropTypes.object } componentDidMount() { const { groupId, group } = this.props this.props.onFetchGroupCategories() if (!group) { if (groupId) { this.props.onFetchGroup(groupId) } else { this.props.onResetEditor() } } else { this.props.onSetGroup(group) } } componentWillReceiveProps(nextProps) { if (this.props.group !== nextProps.group && !!nextProps.group) { this.props.onSetGroup(nextProps.group) } } handleCoverImageChange = (e) => { try { this.props.onCoverImageChange(e.target.files[0]) } catch (error) { // } } handleSubmit = (e) => { 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 } if (this.props.onClose) this.props.onClose() this.props.onSubmit(this.context.router.history) } render() { const { group, error, titleValue, descriptionValue, passwordValue, coverImage, intl, onTitleChange, onChangeGroupPassword, onDescriptionChange, onChangeGroupId, onChangeGroupTags, onChangeGroupCategory, onChangeGroupIsPrivate, onChangeGroupIsVisible, isSubmitting, onSubmit, idValue, tags, category, isPrivate, isVisible, groupId, categories, isAdmin, isPro, } = this.props if (!group && groupId) { return <ColumnIndicator type='loading' /> } else if ((!group && error) || (groupId && !isAdmin)) { 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> )} /> } const memberCount = group ? group.get('member_count') : 0 const hasGroupSlug = group ? !!group.get('slug') : false let categoriesOptions = [{'title':'',value:''}] 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 return ( <Form onSubmit={onSubmit}> <Input id='group-title' title={`${intl.formatMessage(messages.title)} *`} value={titleValue} onChange={onTitleChange} disabled={isSubmitting} placeholder={intl.formatMessage(messages.titlePlaceholder)} isRequired /> <Divider isInvisible /> { memberCount >= 50 && !hasGroupSlug && <React.Fragment> <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} – </b> } {intl.formatMessage(messages.idDescription)} </Text> <Divider isInvisible /> </React.Fragment> } <Textarea title={`${intl.formatMessage(messages.description)} *`} value={descriptionValue} onChange={onDescriptionChange} placeholder={intl.formatMessage(messages.descriptionPlaceholder)} disabled={isSubmitting} isRequired /> <Divider isInvisible /> <div className={_s.d}> <Text className={[_s.pl15, _s.mb10].join(' ')} size='small' weight='medium' color='secondary'> {intl.formatMessage(messages.categoryTitle)} * </Text> <Select value={category} onChange={onChangeGroupCategory} options={categoriesOptions} /> <Text className={[_s.mt5, _s.pl15].join(' ')} size='small' color='tertiary'> {intl.formatMessage(messages.categoryDescription)} </Text> <Divider isInvisible /> </div> <Input id='group-tags' title={intl.formatMessage(messages.tagsTitle)} value={tags} onChange={onChangeGroupTags} disabled={isSubmitting} /> <Text className={[_s.mt5, _s.pl15, _s.mb15, _s.pb5].join(' ')} size='small' color='tertiary'> {intl.formatMessage(messages.tagsDescription)} </Text> <Divider /> <FileInput disabled={isSubmitting} id='group-cover-photo' title={intl.formatMessage(coverImage === null ? messages.coverImage : messages.coverImageChange)} onChange={this.handleCoverImageChange} file={group ? group.get('cover_image_url') : undefined} width='340px' height='145px' isBordered /> <Text className={[_s.mt5, _s.pl15, _s.mb15, _s.pb5].join(' ')} size='small' color='tertiary'> {intl.formatMessage(messages.coverImageDescription)} </Text> <Divider /> <Input id='group-password' title={intl.formatMessage(messages.passwordTitle)} value={passwordValue} onChange={onChangeGroupPassword} disabled={isSubmitting} /> <Text className={[_s.mt5, _s.pl15, _s.mb15, _s.pb5].join(' ')} size='small' color='tertiary'> {intl.formatMessage(messages.passwordDescription)} </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> <Divider isInvisible /> <Button isDisabled={submitDisabled} onClick={this.handleSubmit} > <Text color='inherit' align='center' weight='medium'> {intl.formatMessage(!!group ? messages.update : messages.create)} </Text> </Button> </Form> ) } } 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))