diff --git a/app/javascript/gabsocial/actions/group_editor.js b/app/javascript/gabsocial/actions/group_editor.js
index eda9dc52..e464c1c6 100644
--- a/app/javascript/gabsocial/actions/group_editor.js
+++ b/app/javascript/gabsocial/actions/group_editor.js
@@ -62,6 +62,41 @@ export const createFail = error => ({
error,
});
+export const update = (groupId, title, description, coverImage, routerHistory) => (dispatch, getState) => {
+ if (!me) return;
+
+ dispatch(updateRequest());
+
+ const formData = new FormData();
+ formData.append('title', title);
+ formData.append('description', description);
+
+ if (coverImage !== null) {
+ formData.append('cover_image', coverImage);
+ }
+
+ api(getState).put(`/api/v1/groups/${groupId}`, formData, { headers: { 'Content-Type': 'multipart/form-data' } }).then(({ data }) => {
+ dispatch(updateSuccess(data));
+ routerHistory.push(`/groups/${data.id}`);
+ }).catch(err => dispatch(updateFail(err)));
+ };
+
+
+export const updateRequest = id => ({
+ type: GROUP_UPDATE_REQUEST,
+ id,
+});
+
+export const updateSuccess = group => ({
+ type: GROUP_UPDATE_SUCCESS,
+ group,
+});
+
+export const updateFail = error => ({
+ type: GROUP_UPDATE_FAIL,
+ error,
+});
+
export const changeValue = (field, value) => ({
type: GROUP_EDITOR_VALUE_CHANGE,
field,
diff --git a/app/javascript/gabsocial/features/groups/edit/index.js b/app/javascript/gabsocial/features/groups/edit/index.js
new file mode 100644
index 00000000..7f55a824
--- /dev/null
+++ b/app/javascript/gabsocial/features/groups/edit/index.js
@@ -0,0 +1,148 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import PropTypes from 'prop-types';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import { changeValue, submit, setUp } from '../../../actions/group_editor';
+import IconButton from '../../../components/icon_button';
+import { defineMessages, injectIntl } from 'react-intl';
+import LoadingIndicator from '../../../components/loading_indicator';
+import Column from '../../../components/column';
+
+const messages = defineMessages({
+ heading: { id: 'groups.edit.heading', defaultMessage: 'Edit group' },
+ title: { id: 'groups.form.title', defaultMessage: 'Title' },
+ description: { id: 'groups.form.description', defaultMessage: 'Description' },
+ coverImage: { id: 'groups.form.coverImage', defaultMessage: 'Cover Image' },
+ update: { id: 'groups.form.update', defaultMessage: 'Update group' },
+});
+
+const mapStateToProps = (state, props) => ({
+ group: state.getIn(['groups', props.params.id]),
+ 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 => ({
+ onTitleChange: value => dispatch(changeValue('title', value)),
+ onDescriptionChange: value => dispatch(changeValue('description', value)),
+ onCoverImageChange: value => dispatch(changeValue('coverImage', value)),
+ onSubmit: routerHistory => dispatch(submit(routerHistory)),
+ setUp: group => dispatch(setUp(group)),
+});
+
+export default @connect(mapStateToProps, mapDispatchToProps)
+@injectIntl
+class Edit extends React.PureComponent {
+
+ static contextTypes = {
+ router: PropTypes.object
+ }
+
+ static propTypes = {
+ group: ImmutablePropTypes.map,
+ 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,
+ };
+
+ componentWillMount(nextProps) {
+ if (this.props.group) {
+ this.props.setUp(this.props.group);
+ }
+ }
+
+ componentWillReceiveProps(nextProps) {
+ if (!this.props.group && nextProps.group) {
+ this.props.setUp(nextProps.group);
+ }
+ }
+
+ handleTitleChange = e => {
+ this.props.onTitleChange(e.target.value);
+ }
+
+ handleDescriptionChange = e => {
+ this.props.onDescriptionChange(e.target.value);
+ }
+
+ handleCoverImageChange = e => {
+ this.props.onCoverImageChange(e.target.files[0]);
+ }
+
+ handleSubmit = e => {
+ e.preventDefault();
+ this.props.onSubmit(this.context.router.history);
+ }
+
+ handleClick = () => {
+ this.props.onSubmit(this.context.router.history);
+ }
+
+ render () {
+ const { group, title, description, coverImage, disabled, intl } = this.props;
+
+ if (typeof group === 'undefined') {
+ return (
+
+
+
+ );
+ } else if (group === false) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+
+ );
+ }
+
+}
diff --git a/app/javascript/gabsocial/features/groups/index/index.js b/app/javascript/gabsocial/features/groups/index/index.js
index c375b892..8374cae2 100644
--- a/app/javascript/gabsocial/features/groups/index/index.js
+++ b/app/javascript/gabsocial/features/groups/index/index.js
@@ -49,8 +49,8 @@ class Groups extends ImmutablePureComponent {
return (
-
{intl.formatMessage(messages.heading)}
{intl.formatMessage(messages.create)}
+
{intl.formatMessage(messages.heading)}
diff --git a/app/javascript/gabsocial/features/groups/timeline/components/header.js b/app/javascript/gabsocial/features/groups/timeline/components/header.js
index 58d2cf2f..9691fca3 100644
--- a/app/javascript/gabsocial/features/groups/timeline/components/header.js
+++ b/app/javascript/gabsocial/features/groups/timeline/components/header.js
@@ -10,7 +10,8 @@ import DropdownMenuContainer from '../../../../containers/dropdown_menu_containe
const messages = defineMessages({
join: { id: 'groups.join', defaultMessage: 'Join group' },
leave: { id: 'groups.leave', defaultMessage: 'Leave group' },
- removed_accounts: { id: 'groups.removed_accounts', defaultMessage: 'Removed Accounts' }
+ removed_accounts: { id: 'groups.removed_accounts', defaultMessage: 'Removed Accounts' },
+ edit: { id: 'groups.edit', defaultMessage: 'Edit' }
});
export default @injectIntl
@@ -42,6 +43,7 @@ class Header extends ImmutablePureComponent {
const { group, intl } = this.props;
const menu = [
+ { text: intl.formatMessage(messages.edit), to: `/groups/${group.get('id')}/edit` },
{ text: intl.formatMessage(messages.removed_accounts), to: `/groups/${group.get('id')}/removed_accounts` },
];
diff --git a/app/javascript/gabsocial/features/ui/index.js b/app/javascript/gabsocial/features/ui/index.js
index 3c47de7f..d9d55dac 100644
--- a/app/javascript/gabsocial/features/ui/index.js
+++ b/app/javascript/gabsocial/features/ui/index.js
@@ -58,6 +58,7 @@ import {
GroupMembers,
GroupRemovedAccounts,
GroupCreate,
+ GroupEdit,
} from './util/async-components';
import { me, meUsername } from '../../initial_state';
import { previewState as previewMediaState } from './components/media_modal';
@@ -180,6 +181,7 @@ class SwitchingColumnsArea extends React.PureComponent {
+
diff --git a/app/javascript/gabsocial/features/ui/util/async-components.js b/app/javascript/gabsocial/features/ui/util/async-components.js
index 1aa0af28..40f75d7e 100644
--- a/app/javascript/gabsocial/features/ui/util/async-components.js
+++ b/app/javascript/gabsocial/features/ui/util/async-components.js
@@ -46,6 +46,10 @@ export function GroupCreate () {
return import(/* webpackChunkName: "features/groups/timeline" */'../../groups/create');
}
+export function GroupEdit () {
+ return import(/* webpackChunkName: "features/groups/timeline" */'../../groups/edit');
+}
+
export function Groups () {
return import(/* webpackChunkName: "features/groups/index" */'../../groups/index');
}
diff --git a/app/javascript/gabsocial/reducers/group_editor.js b/app/javascript/gabsocial/reducers/group_editor.js
index 054eedd8..c0f3cef8 100644
--- a/app/javascript/gabsocial/reducers/group_editor.js
+++ b/app/javascript/gabsocial/reducers/group_editor.js
@@ -28,6 +28,7 @@ export default function groupEditorReducer(state = initialState, action) {
return state.withMutations(map => {
map.set('groupId', action.group.get('id'));
map.set('title', action.group.get('title'));
+ map.set('description', action.group.get('description'));
map.set('isSubmitting', false);
});
case GROUP_EDITOR_VALUE_CHANGE:
diff --git a/app/javascript/gabsocial/reducers/groups.js b/app/javascript/gabsocial/reducers/groups.js
index 7ce12931..a8b8be23 100644
--- a/app/javascript/gabsocial/reducers/groups.js
+++ b/app/javascript/gabsocial/reducers/groups.js
@@ -3,6 +3,7 @@ import {
GROUP_FETCH_FAIL,
GROUPS_FETCH_SUCCESS,
} from '../actions/groups';
+import { GROUP_UPDATE_SUCCESS } from '../actions/group_editor';
import { Map as ImmutableMap, fromJS } from 'immutable';
const initialState = ImmutableMap();
@@ -20,6 +21,7 @@ const normalizeGroups = (state, groups) => {
export default function groups(state = initialState, action) {
switch(action.type) {
case GROUP_FETCH_SUCCESS:
+ case GROUP_UPDATE_SUCCESS:
return normalizeGroup(state, action.group);
case GROUPS_FETCH_SUCCESS:
return normalizeGroups(state, action.groups);
diff --git a/app/javascript/styles/gabsocial/components/group-card.scss b/app/javascript/styles/gabsocial/components/group-card.scss
index 32727bb1..799432ac 100644
--- a/app/javascript/styles/gabsocial/components/group-card.scss
+++ b/app/javascript/styles/gabsocial/components/group-card.scss
@@ -10,6 +10,16 @@ $height: 80px;
font-size: 20px;
font-weight: bold;
}
+
+ .group-column-header__cta {
+ float: right;
+ padding: 15px;
+ font-size: 17px;
+
+ a {
+ color: $primary-text-color;
+ }
+ }
}
.group-card-list {