group creation form
This commit is contained in:
parent
62b1707a85
commit
ea1378ada7
78
app/javascript/gabsocial/actions/group_editor.js
Normal file
78
app/javascript/gabsocial/actions/group_editor.js
Normal 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,
|
||||
});
|
@ -1,78 +1,121 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
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 { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
const messages = defineMessages({
|
||||
label: { id: 'groups.new.title_placeholder', defaultMessage: 'New group title' },
|
||||
title: { id: 'groups.new.create', defaultMessage: 'Add group' },
|
||||
heading: { id: 'groups.create.heading', defaultMessage: 'New 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 => ({
|
||||
value: state.getIn(['groupEditor', 'title']),
|
||||
disabled: state.getIn(['groupEditor', 'isSubmitting']),
|
||||
title: state.getIn(['group_editor', 'title']),
|
||||
description: state.getIn(['group_editor', 'description']),
|
||||
coverImage: state.getIn(['group_editor', 'coverImage']),
|
||||
disabled: state.getIn(['group_editor', 'isSubmitting']),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
onChange: value => dispatch(changeListEditorTitle(value)),
|
||||
onSubmit: () => dispatch(submitListEditor(true)),
|
||||
onTitleChange: value => dispatch(changeValue('title', value)),
|
||||
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)
|
||||
@injectIntl
|
||||
class Create extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
value: PropTypes.string.isRequired,
|
||||
disabled: PropTypes.bool,
|
||||
intl: PropTypes.object.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onSubmit: PropTypes.func.isRequired,
|
||||
};
|
||||
static contextTypes = {
|
||||
router: PropTypes.object
|
||||
}
|
||||
|
||||
handleChange = e => {
|
||||
this.props.onChange(e.target.value);
|
||||
}
|
||||
static propTypes = {
|
||||
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 => {
|
||||
e.preventDefault();
|
||||
this.props.onSubmit();
|
||||
}
|
||||
componentWillMount() {
|
||||
this.props.reset();
|
||||
}
|
||||
|
||||
handleClick = () => {
|
||||
this.props.onSubmit();
|
||||
}
|
||||
handleTitleChange = e => {
|
||||
this.props.onTitleChange(e.target.value);
|
||||
}
|
||||
|
||||
render () {
|
||||
const { value, disabled, intl } = this.props;
|
||||
handleDescriptionChange = e => {
|
||||
this.props.onDescriptionChange(e.target.value);
|
||||
}
|
||||
|
||||
const label = intl.formatMessage(messages.label);
|
||||
const title = intl.formatMessage(messages.title);
|
||||
handleCoverImageChange = e => {
|
||||
this.props.onCoverImageChange(e.target.files[0]);
|
||||
}
|
||||
|
||||
return (
|
||||
<form className='column-inline-form' onSubmit={this.handleSubmit}>
|
||||
<label>
|
||||
<span style={{ display: 'none' }}>{label}</span>
|
||||
handleSubmit = e => {
|
||||
e.preventDefault();
|
||||
this.props.onSubmit(this.context.router.history);
|
||||
}
|
||||
|
||||
<input
|
||||
className='setting-text'
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
onChange={this.handleChange}
|
||||
placeholder={label}
|
||||
/>
|
||||
</label>
|
||||
handleClick = () => {
|
||||
this.props.onSubmit(this.context.router.history);
|
||||
}
|
||||
|
||||
<IconButton
|
||||
disabled={disabled}
|
||||
icon='plus'
|
||||
title={title}
|
||||
onClick={this.handleClick}
|
||||
/>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
render () {
|
||||
const { title, description, coverImage, disabled, intl } = this.props;
|
||||
|
||||
return (
|
||||
<form className='column-inline-form' onSubmit={this.handleSubmit}>
|
||||
<h2>{intl.formatMessage(messages.heading)}</h2>
|
||||
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -11,9 +11,10 @@ import GroupCard from './card';
|
||||
|
||||
const messages = defineMessages({
|
||||
heading: { id: 'column.groups', defaultMessage: 'Groups' },
|
||||
tab_featured: { id: 'column.groups_tab_featured', defaultMessage: 'Featured' },
|
||||
tab_member: { id: 'column.groups_tab_member', defaultMessage: 'Groups you\'re in' },
|
||||
tab_admin: { id: 'column.groups_tab_admin', defaultMessage: 'Groups you manage' },
|
||||
create: { id: 'groups.create', defaultMessage: 'Create group' },
|
||||
tab_featured: { id: 'groups.tab_featured', defaultMessage: 'Featured' },
|
||||
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 }) => ({
|
||||
@ -49,6 +50,7 @@ class Groups extends ImmutablePureComponent {
|
||||
<div>
|
||||
<div className="group-column-header">
|
||||
<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">
|
||||
<h1 className="column-header">
|
||||
|
@ -57,6 +57,7 @@ import {
|
||||
GroupTimeline,
|
||||
GroupMembers,
|
||||
GroupRemovedAccounts,
|
||||
GroupCreate,
|
||||
} from './util/async-components';
|
||||
import { me, meUsername } from '../../initial_state';
|
||||
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='/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/admin' page={GroupsPage} component={Groups} content={children} componentParams={{ activeTab: 'admin' }} />
|
||||
<WrappedRoute path='/groups/:id/members' page={GroupPage} component={GroupMembers} content={children} />
|
||||
|
@ -42,6 +42,10 @@ export function GroupRemovedAccounts () {
|
||||
return import(/* webpackChunkName: "features/groups/timeline" */'../../groups/removed_accounts');
|
||||
}
|
||||
|
||||
export function GroupCreate () {
|
||||
return import(/* webpackChunkName: "features/groups/timeline" */'../../groups/create');
|
||||
}
|
||||
|
||||
export function Groups () {
|
||||
return import(/* webpackChunkName: "features/groups/index" */'../../groups/index');
|
||||
}
|
||||
|
56
app/javascript/gabsocial/reducers/group_editor.js
Normal file
56
app/javascript/gabsocial/reducers/group_editor.js
Normal 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;
|
||||
}
|
||||
};
|
@ -35,6 +35,7 @@ import trends from './trends';
|
||||
import groups from './groups';
|
||||
import group_relationships from './group_relationships';
|
||||
import group_lists from './group_lists';
|
||||
import group_editor from './group_editor';
|
||||
|
||||
const reducers = {
|
||||
dropdown_menu,
|
||||
@ -73,6 +74,7 @@ const reducers = {
|
||||
groups,
|
||||
group_relationships,
|
||||
group_lists,
|
||||
group_editor,
|
||||
};
|
||||
|
||||
export default combineReducers(reducers);
|
||||
|
Loading…
Reference in New Issue
Block a user