Merge remote-tracking branch 'origin/develop' into matthew/status

This commit is contained in:
Matthew Hodgson
2017-11-08 04:29:52 -08:00
27 changed files with 519 additions and 75 deletions

View File

@@ -22,7 +22,7 @@ import MatrixClientPeg from '../../MatrixClientPeg';
import sdk from '../../index';
import dis from '../../dispatcher';
import { sanitizedHtmlNode } from '../../HtmlUtils';
import { _t } from '../../languageHandler';
import { _t, _td, _tJsx } from '../../languageHandler';
import AccessibleButton from '../views/elements/AccessibleButton';
import Modal from '../../Modal';
import classnames from 'classnames';
@@ -32,6 +32,17 @@ import GroupStore from '../../stores/GroupStore';
import { showGroupAddRoomDialog } from '../../GroupAddressPicker';
import GeminiScrollbar from 'react-gemini-scrollbar';
const LONG_DESC_PLACEHOLDER = _td(
`<h1>HTML for your community's page</h1>
<p>
Use the long description to introduce new members to the community, or distribute
some important <a href="foo">links</a>
</p>
<p>
You can even use 'img' tags
</p>
`);
const RoomSummaryType = PropTypes.shape({
room_id: PropTypes.string.isRequired,
profile: PropTypes.shape({
@@ -392,6 +403,8 @@ export default React.createClass({
propTypes: {
groupId: PropTypes.string.isRequired,
// Whether this is the first time the group admin is viewing the group
groupIsNew: PropTypes.bool,
},
childContextTypes: {
@@ -417,12 +430,13 @@ export default React.createClass({
uploadingAvatar: false,
membershipBusy: false,
publicityBusy: false,
inviterProfile: null,
};
},
componentWillMount: function() {
this._changeAvatarComponent = null;
this._initGroupStore(this.props.groupId);
this._initGroupStore(this.props.groupId, true);
MatrixClientPeg.get().on("Group.myMembership", this._onGroupMyMembership);
},
@@ -449,7 +463,11 @@ export default React.createClass({
this.setState({membershipBusy: false});
},
_initGroupStore: function(groupId) {
_initGroupStore: function(groupId, firstInit) {
const group = MatrixClientPeg.get().getGroup(groupId);
if (group && group.inviter && group.inviter.userId) {
this._fetchInviterProfile(group.inviter.userId);
}
this._groupStore = GroupStoreCache.getGroupStore(MatrixClientPeg.get(), groupId);
this._groupStore.registerListener(() => {
const summary = this._groupStore.getSummary();
@@ -472,6 +490,9 @@ export default React.createClass({
),
error: null,
});
if (this.props.groupIsNew && firstInit) {
this._onEditClick();
}
});
this._groupStore.on('error', (err) => {
this.setState({
@@ -481,6 +502,26 @@ export default React.createClass({
});
},
_fetchInviterProfile(userId) {
this.setState({
inviterProfileBusy: true,
});
MatrixClientPeg.get().getProfileInfo(userId).then((resp) => {
this.setState({
inviterProfile: {
avatarUrl: resp.avatar_url,
displayName: resp.displayname,
},
});
}).catch((e) => {
console.error('Error getting group inviter profile', e);
}).finally(() => {
this.setState({
inviterProfileBusy: false,
});
});
},
_onShowRhsClick: function(ev) {
dis.dispatch({ action: 'show_right_panel' });
},
@@ -575,7 +616,7 @@ export default React.createClass({
_onAcceptInviteClick: function() {
this.setState({membershipBusy: true});
MatrixClientPeg.get().acceptGroupInvite(this.props.groupId).then(() => {
this._groupStore.acceptGroupInvite().then(() => {
// don't reset membershipBusy here: wait for the membership change to come down the sync
}).catch((e) => {
this.setState({membershipBusy: false});
@@ -661,6 +702,14 @@ export default React.createClass({
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
const TintableSvg = sdk.getComponent('elements.TintableSvg');
const Spinner = sdk.getComponent('elements.Spinner');
const ToolTipButton = sdk.getComponent('elements.ToolTipButton');
const roomsHelpNode = this.state.editing ? <ToolTipButton helpText={
_t(
'These rooms are displayed to community members on the community page. '+
'Community members can join the rooms by clicking on them.',
)
} /> : <div />;
const addRoomRow = this.state.editing ?
(<AccessibleButton className="mx_GroupView_rooms_header_addRow"
@@ -673,14 +722,23 @@ export default React.createClass({
{ _t('Add rooms to this community') }
</div>
</AccessibleButton>) : <div />;
const roomDetailListClassName = classnames({
"mx_fadable": true,
"mx_fadable_faded": this.state.editing,
});
return <div className="mx_GroupView_rooms">
<div className="mx_GroupView_rooms_header">
<h3>{ _t('Rooms') }</h3>
<h3>
{ _t('Rooms') }
{ roomsHelpNode }
</h3>
{ addRoomRow }
</div>
{ this.state.groupRoomsLoading ?
<Spinner /> :
<RoomDetailList rooms={this.state.groupRooms} />
<RoomDetailList
rooms={this.state.groupRooms}
className={roomDetailListClassName} />
}
</div>;
},
@@ -769,20 +827,37 @@ export default React.createClass({
_getMembershipSection: function() {
const Spinner = sdk.getComponent("elements.Spinner");
const BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
const group = MatrixClientPeg.get().getGroup(this.props.groupId);
if (!group) return null;
if (group.myMembership === 'invite') {
if (this.state.membershipBusy) {
if (this.state.membershipBusy || this.state.inviterProfileBusy) {
return <div className="mx_GroupView_membershipSection">
<Spinner />
</div>;
}
const httpInviterAvatar = this.state.inviterProfile ?
MatrixClientPeg.get().mxcUrlToHttp(
this.state.inviterProfile.avatarUrl, 36, 36,
) : null;
let inviterName = group.inviter.userId;
if (this.state.inviterProfile) {
inviterName = this.state.inviterProfile.displayName || group.inviter.userId;
}
return <div className="mx_GroupView_membershipSection mx_GroupView_membershipSection_invited">
<div className="mx_GroupView_membershipSubSection">
<div className="mx_GroupView_membershipSection_description">
{ _t("%(inviter)s has invited you to join this community", {inviter: group.inviter.userId}) }
<BaseAvatar url={httpInviterAvatar}
name={inviterName}
width={36}
height={36}
/>
{ _t("%(inviter)s has invited you to join this community", {
inviter: inviterName,
}) }
</div>
<div className="mx_GroupView_membership_buttonContainer">
<AccessibleButton className="mx_GroupView_textButton mx_RoomHeader_textButton"
@@ -851,6 +926,18 @@ export default React.createClass({
let description = null;
if (summary.profile && summary.profile.long_description) {
description = sanitizedHtmlNode(summary.profile.long_description);
} else if (this.state.isUserPrivileged) {
description = <div
className="mx_GroupView_groupDesc_placeholder"
onClick={this._onEditClick}
>
{ _tJsx(
'Your community hasn\'t got a Long Description, a HTML page to show to community members.<br />' +
'Click here to open settings and give it one!',
[/<br \/>/],
[(sub) => <br />])
}
</div>;
}
const groupDescEditingClasses = classnames({
"mx_GroupView_groupDesc": true,
@@ -862,6 +949,7 @@ export default React.createClass({
<h3> { _t("Long Description (HTML)") } </h3>
<textarea
value={this.state.profileForm.long_description}
placeholder={_t(LONG_DESC_PLACEHOLDER)}
onChange={this._onLongDescChange}
tabIndex="4"
key="editLongDesc"

View File

@@ -301,6 +301,7 @@ export default React.createClass({
case PageTypes.GroupView:
page_element = <GroupView
groupId={this.props.currentGroupId}
isNew={this.props.currentGroupIsNew}
collapsedRhs={this.props.collapseRhs}
/>;
if (!this.props.collapseRhs) right_panel = <RightPanel groupId={this.props.currentGroupId} disabled={this.props.rightDisabled} />;

View File

@@ -493,7 +493,10 @@ module.exports = React.createClass({
case 'view_group':
{
const groupId = payload.group_id;
this.setState({currentGroupId: groupId});
this.setState({
currentGroupId: groupId,
currentGroupIsNew: payload.group_is_new,
});
this._setPage(PageTypes.GroupView);
this.notifyNewScreen('group/' + groupId);
}

View File

@@ -303,6 +303,15 @@ module.exports = React.createClass({
_shouldShowApps: function(room) {
if (!BROWSER_SUPPORTS_SANDBOX) return false;
// Check if user has previously chosen to hide the app drawer for this
// room. If so, do not show apps
let hideWidgetDrawer = localStorage.getItem(
room.roomId + "_hide_widget_drawer");
if (hideWidgetDrawer === "true") {
return false;
}
const appsStateEvents = room.currentState.getStateEvents('im.vector.modular.widgets');
// any valid widget = show apps
for (let i = 0; i < appsStateEvents.length; i++) {