Gab Social. All are welcome.
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
import React from 'react';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import PropTypes from 'prop-types';
|
||||
import InnerHeader from './inner_header';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
|
||||
export default class Header extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
group: ImmutablePropTypes.map,
|
||||
relationships: ImmutablePropTypes.map,
|
||||
toggleMembership: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
|
||||
render () {
|
||||
const { group, relationships, toggleMembership } = this.props;
|
||||
|
||||
if (group === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='account-timeline__header'>
|
||||
<InnerHeader
|
||||
group={group}
|
||||
relationships={relationships}
|
||||
toggleMembership={toggleMembership}
|
||||
/>
|
||||
|
||||
<div className='account__section-headline'>
|
||||
<NavLink exact to={`/groups/${group.get('id')}`}><FormattedMessage id='groups.posts' defaultMessage='Posts' /></NavLink>
|
||||
<NavLink exact to={`/groups/${group.get('id')}/accounts`}><FormattedMessage id='group.accounts' defaultMessage='Members' /></NavLink>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import Button from 'gabsocial/components/button';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import Icon from 'gabsocial/components/icon';
|
||||
import DropdownMenuContainer from 'gabsocial/containers/dropdown_menu_container';
|
||||
|
||||
const messages = defineMessages({
|
||||
join: { id: 'groups.join', defaultMessage: 'Join' },
|
||||
leave: { id: 'groups.leave', defaultMessage: 'Leave' },
|
||||
});
|
||||
|
||||
export default @injectIntl
|
||||
class InnerHeader extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
group: ImmutablePropTypes.map,
|
||||
relationships: ImmutablePropTypes.map,
|
||||
toggleMembership: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
isStatusesPageActive = (match, location) => {
|
||||
if (!match) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !location.pathname.match(/\/(accounts)\/?$/);
|
||||
}
|
||||
|
||||
render () {
|
||||
const { group, relationships, intl } = this.props;
|
||||
|
||||
if (!group || !relationships) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let info = [];
|
||||
let actionBtn = '';
|
||||
let lockedIcon = '';
|
||||
let menu = [];
|
||||
|
||||
if (relationships.get('admin')) {
|
||||
info.push(<span key='admin'><FormattedMessage id='group.admin' defaultMessage='You are an admin' /></span>);
|
||||
}
|
||||
|
||||
if (!relationships) { // Wait until the relationship is loaded
|
||||
actionBtn = '';
|
||||
} else if (!relationships.get('member')) {
|
||||
actionBtn = <Button className='logo-button' text={intl.formatMessage(messages.join)} onClick={() => this.props.toggleMembership(group, relationships)} />;
|
||||
} else if (relationships.get('member')) {
|
||||
actionBtn = <Button className='logo-button' text={intl.formatMessage(messages.leave, { name: group.get('title') })} onClick={() => this.props.toggleMembership(group, relationships)} />;
|
||||
}
|
||||
|
||||
if (group.get('archived')) {
|
||||
lockedIcon = <Icon id='lock' title={intl.formatMessage(messages.group_archived)} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='account__header'>
|
||||
<div className='account__header__image'>
|
||||
<div className='account__header__info'>
|
||||
<img src={group.get('cover_image_url')} alt='' className='parallax' />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='account__header__bar'>
|
||||
<div className='account__header__tabs'>
|
||||
<div className='account__header__tabs__buttons'>
|
||||
{actionBtn}
|
||||
|
||||
<DropdownMenuContainer items={menu} icon='ellipsis-v' size={24} direction='right' />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='account__header__tabs__name'>
|
||||
<h1>
|
||||
<span>{group.get('title')} {info}</span>
|
||||
<small>{lockedIcon}</small>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div className='account__header__extra'>
|
||||
<div className='account__header__bio'>
|
||||
{group.get('description').length > 0 && <div className='account__header__content'>{group.get('description')}</div>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { connect } from 'react-redux';
|
||||
import Header from '../components/header';
|
||||
import { joinGroup, leaveGroup } from '../../../../actions/groups';
|
||||
|
||||
const mapStateToProps = (state, { groupId }) => ({
|
||||
group: state.getIn(['groups', groupId]),
|
||||
relationships: state.getIn(['group_relationships', groupId]),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch, { intl }) => ({
|
||||
toggleMembership (group, relationships) {
|
||||
if (relationships.get('member')) {
|
||||
dispatch(leaveGroup(group.get('id')));
|
||||
} else {
|
||||
dispatch(joinGroup(group.get('id')));
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Header);
|
||||
105
app/javascript/gabsocial/features/groups/timeline/index.js
Normal file
105
app/javascript/gabsocial/features/groups/timeline/index.js
Normal file
@@ -0,0 +1,105 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import StatusListContainer from '../../ui/containers/status_list_container';
|
||||
import Column from '../../../components/column';
|
||||
import ColumnBackButton from '../../../components/column_back_button';
|
||||
import ColumnHeader from '../../../components/column_header';
|
||||
import { FormattedMessage, injectIntl } from 'react-intl';
|
||||
import { connectGroupStream } from '../../../actions/streaming';
|
||||
import { expandGroupTimeline } from '../../../actions/timelines';
|
||||
import { fetchGroup } from '../../../actions/groups';
|
||||
import MissingIndicator from '../../../components/missing_indicator';
|
||||
import LoadingIndicator from '../../../components/loading_indicator';
|
||||
import HeaderContainer from './containers/header_container';
|
||||
|
||||
const mapStateToProps = (state, props) => ({
|
||||
group: state.getIn(['groups', props.params.id]),
|
||||
hasUnread: state.getIn(['timelines', `group:${props.params.id}`, 'unread']) > 0,
|
||||
});
|
||||
|
||||
export default @connect(mapStateToProps)
|
||||
@injectIntl
|
||||
class GroupTimeline extends React.PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
params: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
columnId: PropTypes.string,
|
||||
hasUnread: PropTypes.bool,
|
||||
group: PropTypes.oneOfType([ImmutablePropTypes.map, PropTypes.bool]),
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
const { dispatch } = this.props;
|
||||
const { id } = this.props.params;
|
||||
|
||||
dispatch(fetchGroup(id));
|
||||
dispatch(expandGroupTimeline(id));
|
||||
|
||||
this.disconnect = dispatch(connectGroupStream(id));
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
if (this.disconnect) {
|
||||
this.disconnect();
|
||||
this.disconnect = null;
|
||||
}
|
||||
}
|
||||
|
||||
handleLoadMore = maxId => {
|
||||
const { id } = this.props.params;
|
||||
this.props.dispatch(expandGroupTimeline(id, { maxId }));
|
||||
}
|
||||
|
||||
render () {
|
||||
const { hasUnread, columnId, group } = this.props;
|
||||
const { id } = this.props.params;
|
||||
const title = group ? group.get('title') : id;
|
||||
|
||||
if (typeof group === 'undefined') {
|
||||
return (
|
||||
<Column>
|
||||
<div>
|
||||
<LoadingIndicator />
|
||||
</div>
|
||||
</Column>
|
||||
);
|
||||
} else if (group === false) {
|
||||
return (
|
||||
<Column>
|
||||
<ColumnBackButton />
|
||||
<MissingIndicator />
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Column label={title}>
|
||||
<ColumnHeader icon='list-ul' active={hasUnread} title={title}>
|
||||
<div className='column-header__links'>
|
||||
{/* Leave might be here */}
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
</ColumnHeader>
|
||||
|
||||
<StatusListContainer
|
||||
prepend={<HeaderContainer groupId={id} />}
|
||||
alwaysPrepend
|
||||
scrollKey={`group_timeline-${columnId}`}
|
||||
timelineId={`group:${id}`}
|
||||
onLoadMore={this.handleLoadMore}
|
||||
emptyMessage={<FormattedMessage id='empty_column.group' defaultMessage='There is nothing in this group yet. When members of this group post new statuses, they will appear here.' />}
|
||||
/>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user