group creation form

This commit is contained in:
2458773093 2019-07-17 21:22:19 +03:00
parent 62b1707a85
commit ea1378ada7
7 changed files with 239 additions and 52 deletions

View File

@ -0,0 +1,78 @@
import api from '../api';
import { me } from 'gabsocial/initial_state';
export const GROUP_CREATE_REQUEST = 'GROUP_CREATE_REQUEST';
export const GROUP_CREATE_SUCCESS = 'GROUP_CREATE_SUCCESS';
export const GROUP_CREATE_FAIL = 'GROUP_CREATE_FAIL';
export const GROUP_UPDATE_REQUEST = 'GROUP_UPDATE_REQUEST';
export const GROUP_UPDATE_SUCCESS = 'GROUP_UPDATE_SUCCESS';
export const GROUP_UPDATE_FAIL = 'GROUP_UPDATE_FAIL';
export const GROUP_EDITOR_VALUE_CHANGE = 'GROUP_EDITOR_VALUE_CHANGE';
export const GROUP_EDITOR_RESET = 'GROUP_EDITOR_RESET';
export const GROUP_EDITOR_SETUP = 'GROUP_EDITOR_SETUP';
export const submit = (routerHistory) => (dispatch, getState) => {
const groupId = getState().getIn(['group_editor', 'groupId']);
const title = getState().getIn(['group_editor', 'title']);
const description = getState().getIn(['group_editor', 'description']);
const coverImage = getState().getIn(['group_editor', 'coverImage']);
if (groupId === null) {
dispatch(create(title, description, coverImage, routerHistory));
} else {
dispatch(update(groupId, title, description, coverImage, routerHistory));
}
};
export const create = (title, description, coverImage, routerHistory) => (dispatch, getState) => {
if (!me) return;
dispatch(createRequest());
const formData = new FormData();
formData.append('title', title);
formData.append('description', description);
debugger;
if (coverImage !== null) {
formData.append('cover_image', coverImage);
}
api(getState).post('/api/v1/groups', formData, { headers: { 'Content-Type': 'multipart/form-data' } }).then(({ data }) => {
dispatch(createSuccess(data));
routerHistory.push(`/groups/${data.id}`);
}).catch(err => dispatch(createFail(err)));
};
export const createRequest = id => ({
type: GROUP_CREATE_REQUEST,
id,
});
export const createSuccess = group => ({
type: GROUP_CREATE_SUCCESS,
group,
});
export const createFail = error => ({
type: GROUP_CREATE_FAIL,
error,
});
export const changeValue = (field, value) => ({
type: GROUP_EDITOR_VALUE_CHANGE,
field,
value,
});
export const reset = () => ({
type: GROUP_EDITOR_RESET
});
export const setUp = (group) => ({
type: GROUP_EDITOR_SETUP,
group,
});

View File

@ -1,78 +1,121 @@
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { changeListEditorTitle, submitListEditor } from '../../../actions/lists'; import { changeValue, submit, reset } from '../../../actions/group_editor';
import IconButton from '../../../components/icon_button'; import IconButton from '../../../components/icon_button';
import { defineMessages, injectIntl } from 'react-intl'; import { defineMessages, injectIntl } from 'react-intl';
const messages = defineMessages({ const messages = defineMessages({
label: { id: 'groups.new.title_placeholder', defaultMessage: 'New group title' }, heading: { id: 'groups.create.heading', defaultMessage: 'New group' },
title: { id: 'groups.new.create', defaultMessage: 'Add group' }, title: { id: 'groups.form.title', defaultMessage: 'Title' },
description: { id: 'groups.form.description', defaultMessage: 'Description' },
coverImage: { id: 'groups.form.coverImage', defaultMessage: 'Cover Image' },
create: { id: 'groups.form.create', defaultMessage: 'Create group' },
}); });
const mapStateToProps = state => ({ const mapStateToProps = state => ({
value: state.getIn(['groupEditor', 'title']), title: state.getIn(['group_editor', 'title']),
disabled: state.getIn(['groupEditor', 'isSubmitting']), description: state.getIn(['group_editor', 'description']),
coverImage: state.getIn(['group_editor', 'coverImage']),
disabled: state.getIn(['group_editor', 'isSubmitting']),
}); });
const mapDispatchToProps = dispatch => ({ const mapDispatchToProps = dispatch => ({
onChange: value => dispatch(changeListEditorTitle(value)), onTitleChange: value => dispatch(changeValue('title', value)),
onSubmit: () => dispatch(submitListEditor(true)), onDescriptionChange: value => dispatch(changeValue('description', value)),
onCoverImageChange: value => dispatch(changeValue('coverImage', value)),
onSubmit: routerHistory => dispatch(submit(routerHistory)),
reset: () => dispatch(reset()),
}); });
export default @connect(mapStateToProps, mapDispatchToProps) export default @connect(mapStateToProps, mapDispatchToProps)
@injectIntl @injectIntl
class Create extends React.PureComponent { class Create extends React.PureComponent {
static propTypes = { static contextTypes = {
value: PropTypes.string.isRequired, router: PropTypes.object
disabled: PropTypes.bool, }
intl: PropTypes.object.isRequired,
onChange: PropTypes.func.isRequired,
onSubmit: PropTypes.func.isRequired,
};
handleChange = e => { static propTypes = {
this.props.onChange(e.target.value); title: PropTypes.string.isRequired,
} description: PropTypes.string.isRequired,
coverImage: PropTypes.object,
disabled: PropTypes.bool,
intl: PropTypes.object.isRequired,
onTitleChange: PropTypes.func.isRequired,
onSubmit: PropTypes.func.isRequired,
};
handleSubmit = e => { componentWillMount() {
e.preventDefault(); this.props.reset();
this.props.onSubmit(); }
}
handleClick = () => { handleTitleChange = e => {
this.props.onSubmit(); this.props.onTitleChange(e.target.value);
} }
render () { handleDescriptionChange = e => {
const { value, disabled, intl } = this.props; this.props.onDescriptionChange(e.target.value);
}
const label = intl.formatMessage(messages.label); handleCoverImageChange = e => {
const title = intl.formatMessage(messages.title); this.props.onCoverImageChange(e.target.files[0]);
}
return ( handleSubmit = e => {
<form className='column-inline-form' onSubmit={this.handleSubmit}> e.preventDefault();
<label> this.props.onSubmit(this.context.router.history);
<span style={{ display: 'none' }}>{label}</span> }
<input handleClick = () => {
className='setting-text' this.props.onSubmit(this.context.router.history);
value={value} }
disabled={disabled}
onChange={this.handleChange}
placeholder={label}
/>
</label>
<IconButton render () {
disabled={disabled} const { title, description, coverImage, disabled, intl } = this.props;
icon='plus'
title={title} return (
onClick={this.handleClick} <form className='column-inline-form' onSubmit={this.handleSubmit}>
/> <h2>{intl.formatMessage(messages.heading)}</h2>
</form>
); <label>
} <input
className='setting-text'
value={title}
disabled={disabled}
onChange={this.handleTitleChange}
placeholder={intl.formatMessage(messages.title)}
/>
</label>
<label>
<input
className='setting-text'
value={description}
disabled={disabled}
onChange={this.handleDescriptionChange}
placeholder={intl.formatMessage(messages.description)}
/>
</label>
<label>
<input
type='file'
className='setting-text'
disabled={disabled}
onChange={this.handleCoverImageChange}
placeholder={intl.formatMessage(messages.coverImage)}
/>
</label>
<IconButton
disabled={disabled}
icon='plus'
title={title}
onClick={this.handleClick}
/>
</form>
);
}
} }

View File

@ -11,9 +11,10 @@ import GroupCard from './card';
const messages = defineMessages({ const messages = defineMessages({
heading: { id: 'column.groups', defaultMessage: 'Groups' }, heading: { id: 'column.groups', defaultMessage: 'Groups' },
tab_featured: { id: 'column.groups_tab_featured', defaultMessage: 'Featured' }, create: { id: 'groups.create', defaultMessage: 'Create group' },
tab_member: { id: 'column.groups_tab_member', defaultMessage: 'Groups you\'re in' }, tab_featured: { id: 'groups.tab_featured', defaultMessage: 'Featured' },
tab_admin: { id: 'column.groups_tab_admin', defaultMessage: 'Groups you manage' }, tab_member: { id: 'groups.tab_member', defaultMessage: 'Groups you\'re in' },
tab_admin: { id: 'groups.tab_admin', defaultMessage: 'Groups you manage' },
}); });
const mapStateToProps = (state, { activeTab }) => ({ const mapStateToProps = (state, { activeTab }) => ({
@ -49,6 +50,7 @@ class Groups extends ImmutablePureComponent {
<div> <div>
<div className="group-column-header"> <div className="group-column-header">
<div className="group-column-header__title">{intl.formatMessage(messages.heading)}</div> <div className="group-column-header__title">{intl.formatMessage(messages.heading)}</div>
<div className="group-column-header__cta"><Link to="/groups/create">{intl.formatMessage(messages.create)}</Link></div>
<div className="column-header__wrapper"> <div className="column-header__wrapper">
<h1 className="column-header"> <h1 className="column-header">

View File

@ -57,6 +57,7 @@ import {
GroupTimeline, GroupTimeline,
GroupMembers, GroupMembers,
GroupRemovedAccounts, GroupRemovedAccounts,
GroupCreate,
} from './util/async-components'; } from './util/async-components';
import { me, meUsername } from '../../initial_state'; import { me, meUsername } from '../../initial_state';
import { previewState as previewMediaState } from './components/media_modal'; import { previewState as previewMediaState } from './components/media_modal';
@ -174,6 +175,7 @@ class SwitchingColumnsArea extends React.PureComponent {
<WrappedRoute path='/timeline/all' exact page={HomePage} component={CommunityTimeline} content={children} /> <WrappedRoute path='/timeline/all' exact page={HomePage} component={CommunityTimeline} content={children} />
<WrappedRoute path='/groups' exact page={GroupsPage} component={Groups} content={children} componentParams={{ activeTab: 'featured' }} /> <WrappedRoute path='/groups' exact page={GroupsPage} component={Groups} content={children} componentParams={{ activeTab: 'featured' }} />
<WrappedRoute path='/groups/create' page={GroupsPage} component={GroupCreate} content={children} />
<WrappedRoute path='/groups/browse/member' page={GroupsPage} component={Groups} content={children} componentParams={{ activeTab: 'member' }} /> <WrappedRoute path='/groups/browse/member' page={GroupsPage} component={Groups} content={children} componentParams={{ activeTab: 'member' }} />
<WrappedRoute path='/groups/browse/admin' page={GroupsPage} component={Groups} content={children} componentParams={{ activeTab: 'admin' }} /> <WrappedRoute path='/groups/browse/admin' page={GroupsPage} component={Groups} content={children} componentParams={{ activeTab: 'admin' }} />
<WrappedRoute path='/groups/:id/members' page={GroupPage} component={GroupMembers} content={children} /> <WrappedRoute path='/groups/:id/members' page={GroupPage} component={GroupMembers} content={children} />

View File

@ -42,6 +42,10 @@ export function GroupRemovedAccounts () {
return import(/* webpackChunkName: "features/groups/timeline" */'../../groups/removed_accounts'); return import(/* webpackChunkName: "features/groups/timeline" */'../../groups/removed_accounts');
} }
export function GroupCreate () {
return import(/* webpackChunkName: "features/groups/timeline" */'../../groups/create');
}
export function Groups () { export function Groups () {
return import(/* webpackChunkName: "features/groups/index" */'../../groups/index'); return import(/* webpackChunkName: "features/groups/index" */'../../groups/index');
} }

View File

@ -0,0 +1,56 @@
import { Map as ImmutableMap } from 'immutable';
import {
GROUP_CREATE_REQUEST,
GROUP_CREATE_FAIL,
GROUP_CREATE_SUCCESS,
GROUP_UPDATE_REQUEST,
GROUP_UPDATE_FAIL,
GROUP_UPDATE_SUCCESS,
GROUP_EDITOR_RESET,
GROUP_EDITOR_SETUP,
GROUP_EDITOR_VALUE_CHANGE,
} from '../actions/group_editor';
const initialState = ImmutableMap({
groupId: null,
isSubmitting: false,
isChanged: false,
title: '',
description: '',
coverImage: null,
});
export default function groupEditorReducer(state = initialState, action) {
switch(action.type) {
case GROUP_EDITOR_RESET:
return initialState;
case GROUP_EDITOR_SETUP:
return state.withMutations(map => {
map.set('groupId', action.group.get('id'));
map.set('title', action.group.get('title'));
map.set('isSubmitting', false);
});
case GROUP_EDITOR_VALUE_CHANGE:
return state.withMutations(map => {
map.set(action.field, action.value);
map.set('isChanged', true);
});
case GROUP_CREATE_REQUEST:
case GROUP_UPDATE_REQUEST:
return state.withMutations(map => {
map.set('isSubmitting', true);
map.set('isChanged', false);
});
case GROUP_CREATE_FAIL:
case GROUP_UPDATE_FAIL:
return state.set('isSubmitting', false);
case GROUP_CREATE_SUCCESS:
case GROUP_UPDATE_SUCCESS:
return state.withMutations(map => {
map.set('isSubmitting', false);
map.set('groupId', action.group.id);
});
default:
return state;
}
};

View File

@ -35,6 +35,7 @@ import trends from './trends';
import groups from './groups'; import groups from './groups';
import group_relationships from './group_relationships'; import group_relationships from './group_relationships';
import group_lists from './group_lists'; import group_lists from './group_lists';
import group_editor from './group_editor';
const reducers = { const reducers = {
dropdown_menu, dropdown_menu,
@ -73,6 +74,7 @@ const reducers = {
groups, groups,
group_relationships, group_relationships,
group_lists, group_lists,
group_editor,
}; };
export default combineReducers(reducers); export default combineReducers(reducers);