From 9ed9422af84dc3095c3064820bc01b2b29422778 Mon Sep 17 00:00:00 2001 From: lukebarnard <luke.barnard99@gmail.com> Date: Tue, 21 Nov 2017 11:50:41 +0000 Subject: [PATCH] Move group publication toggles to UserSettings --- src/components/structures/GroupView.js | 32 ------- src/components/structures/MyGroups.js | 58 +----------- src/components/structures/UserSettings.js | 7 ++ .../views/groups/GroupPublicityToggle.js | 91 +++++++++++++++++++ src/components/views/groups/GroupTile.js | 91 +++++++++++++++++++ .../views/groups/GroupUserSettings.js | 66 ++++++++++++++ src/i18n/strings/en_EN.json | 10 +- 7 files changed, 262 insertions(+), 93 deletions(-) create mode 100644 src/components/views/groups/GroupPublicityToggle.js create mode 100644 src/components/views/groups/GroupTile.js create mode 100644 src/components/views/groups/GroupUserSettings.js diff --git a/src/components/structures/GroupView.js b/src/components/structures/GroupView.js index b137893bde..45befdd60f 100644 --- a/src/components/structures/GroupView.js +++ b/src/components/structures/GroupView.js @@ -672,18 +672,6 @@ export default React.createClass({ showGroupAddRoomDialog(this.props.groupId); }, - _onPublicityToggle: function() { - this.setState({ - publicityBusy: true, - }); - const publicity = !this.state.isGroupPublicised; - this._groupStore.setGroupPublicity(publicity).then(() => { - this.setState({ - publicityBusy: false, - }); - }); - }, - _getGroupSection: function() { const groupSettingsSectionClasses = classnames({ "mx_GroupView_group": this.state.editing, @@ -903,25 +891,6 @@ export default React.createClass({ return null; }, - _getMemberSettingsSection: function() { - return <div className="mx_GroupView_memberSettings"> - <h2> { _t("Community Member Settings") } </h2> - <div className="mx_GroupView_memberSettings_toggle"> - <input type="checkbox" - onClick={this._onPublicityToggle} - checked={this.state.isGroupPublicised} - tabIndex="3" - id="isGroupPublicised" - /> - <label htmlFor="isGroupPublicised" - onClick={this._onPublicityToggle} - > - { _t("Publish this community on your profile") } - </label> - </div> - </div>; - }, - _getLongDescriptionNode: function() { const summary = this.state.summary; let description = null; @@ -976,7 +945,6 @@ export default React.createClass({ let shortDescNode; const bodyNodes = [ this._getMembershipSection(), - this.state.editing ? this._getMemberSettingsSection() : null, this._getGroupSection(), ]; const rightButtons = []; diff --git a/src/components/structures/MyGroups.js b/src/components/structures/MyGroups.js index c669d7dd73..2e8bae52c3 100644 --- a/src/components/structures/MyGroups.js +++ b/src/components/structures/MyGroups.js @@ -15,70 +15,13 @@ limitations under the License. */ import React from 'react'; -import PropTypes from 'prop-types'; import GeminiScrollbar from 'react-gemini-scrollbar'; -import {MatrixClient} from 'matrix-js-sdk'; import sdk from '../../index'; import { _t } from '../../languageHandler'; import withMatrixClient from '../../wrappers/withMatrixClient'; import AccessibleButton from '../views/elements/AccessibleButton'; -import dis from '../../dispatcher'; import Modal from '../../Modal'; -import FlairStore from '../../stores/FlairStore'; - -const GroupTile = React.createClass({ - displayName: 'GroupTile', - - propTypes: { - groupId: PropTypes.string.isRequired, - }, - - contextTypes: { - matrixClient: React.PropTypes.instanceOf(MatrixClient).isRequired, - }, - - getInitialState() { - return { - profile: null, - }; - }, - - componentWillMount: function() { - FlairStore.getGroupProfileCached(this.context.matrixClient, this.props.groupId).then((profile) => { - this.setState({profile}); - }); - }, - - onClick: function(e) { - e.preventDefault(); - dis.dispatch({ - action: 'view_group', - group_id: this.props.groupId, - }); - }, - - render: function() { - const BaseAvatar = sdk.getComponent('avatars.BaseAvatar'); - const profile = this.state.profile || {}; - const name = profile.name || this.props.groupId; - const desc = profile.shortDescription; - const httpUrl = profile.avatarUrl ? this.context.matrixClient.mxcUrlToHttp( - profile.avatarUrl, 50, 50, "crop", - ) : null; - return <AccessibleButton className="mx_GroupTile" onClick={this.onClick}> - <div className="mx_GroupTile_avatar"> - <BaseAvatar name={name} url={httpUrl} width={50} height={50} /> - </div> - <div className="mx_GroupTile_profile"> - <h3 className="mx_GroupTile_name">{ name }</h3> - <div className="mx_GroupTile_desc">{ desc }</div> - <div className="mx_GroupTile_groupId">{ this.props.groupId }</div> - </div> - </AccessibleButton>; - }, -}); - export default withMatrixClient(React.createClass({ displayName: 'MyGroups', @@ -114,6 +57,7 @@ export default withMatrixClient(React.createClass({ const Loader = sdk.getComponent("elements.Spinner"); const SimpleRoomHeader = sdk.getComponent('rooms.SimpleRoomHeader'); const TintableSvg = sdk.getComponent("elements.TintableSvg"); + const GroupTile = sdk.getComponent("groups.GroupTile"); let content; let contentHeader; diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 88619266ce..09844c3d63 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -595,6 +595,11 @@ module.exports = React.createClass({ }); }, + _renderGroupSettings: function() { + const GroupUserSettings = sdk.getComponent('groups.GroupUserSettings'); + return <GroupUserSettings />; + }, + _renderReferral: function() { const teamToken = this.props.teamToken; if (!teamToken) { @@ -1249,6 +1254,8 @@ module.exports = React.createClass({ { accountJsx } </div> + { this._renderGroupSettings() } + { this._renderReferral() } { notificationArea } diff --git a/src/components/views/groups/GroupPublicityToggle.js b/src/components/views/groups/GroupPublicityToggle.js new file mode 100644 index 0000000000..ec910adcf5 --- /dev/null +++ b/src/components/views/groups/GroupPublicityToggle.js @@ -0,0 +1,91 @@ +/* +Copyright 2017 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from 'react'; +import PropTypes from 'prop-types'; +import { MatrixClient } from 'matrix-js-sdk'; +import sdk from '../../../index'; +import GroupStoreCache from '../../../stores/GroupStoreCache'; +import GroupStore from '../../../stores/GroupStore'; +import { _t } from '../../../languageHandler.js'; + +export default React.createClass({ + displayName: 'GroupPublicityToggle', + + propTypes: { + groupId: PropTypes.string.isRequired, + }, + + contextTypes: { + matrixClient: PropTypes.instanceOf(MatrixClient), + }, + + getInitialState() { + return { + busy: false, + ready: false, + isGroupPublicised: null, + }; + }, + + componentWillMount: function() { + this._initGroupStore(this.props.groupId); + }, + + _initGroupStore: function(groupId) { + this._groupStore = GroupStoreCache.getGroupStore(this.context.matrixClient, groupId); + this._groupStore.registerListener(() => { + this.setState({ + isGroupPublicised: this._groupStore.getGroupPublicity(), + ready: this._groupStore.isStateReady(GroupStore.STATE_KEY.Summary), + }); + }); + }, + + _onPublicityToggle: function(e) { + e.stopPropagation(); + this.setState({ + busy: true, + // Optimistic early update + isGroupPublicised: !this.state.isGroupPublicised, + }); + this._groupStore.setGroupPublicity(!this.state.isGroupPublicised).then(() => { + this.setState({ + busy: false, + }); + }); + }, + + render() { + const GroupTile = sdk.getComponent('groups.GroupTile'); + const input = <input type="checkbox" + onClick={this._onPublicityToggle} + checked={this.state.isGroupPublicised} + />; + const labelText = !this.state.ready ? _t("Loading...") : + (this.state.isGroupPublicised ? + _t("Flair will appear if enabled in room settings") : + _t("Flair will not appear") + ); + return <div className="mx_GroupPublicity_toggle"> + <GroupTile groupId={this.props.groupId} showDescription={false} avatarHeight={40} /> + <label onClick={this._onPublicityToggle}> + { input } + { labelText } + </label> + </div>; + }, +}); diff --git a/src/components/views/groups/GroupTile.js b/src/components/views/groups/GroupTile.js new file mode 100644 index 0000000000..35c8dde2ab --- /dev/null +++ b/src/components/views/groups/GroupTile.js @@ -0,0 +1,91 @@ +/* +Copyright 2017 Vector Creations Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from 'react'; +import PropTypes from 'prop-types'; +import {MatrixClient} from 'matrix-js-sdk'; +import sdk from '../../../index'; +import dis from '../../../dispatcher'; +import FlairStore from '../../../stores/FlairStore'; + +const GroupTile = React.createClass({ + displayName: 'GroupTile', + + propTypes: { + groupId: PropTypes.string.isRequired, + // Whether to show the short description of the group on the tile + showDescription: PropTypes.bool, + // Height of the group avatar in pixels + avatarHeight: PropTypes.number, + }, + + contextTypes: { + matrixClient: React.PropTypes.instanceOf(MatrixClient).isRequired, + }, + + getInitialState() { + return { + profile: null, + }; + }, + + getDefaultProps() { + return { + showDescription: true, + avatarHeight: 50, + }; + }, + + componentWillMount: function() { + FlairStore.getGroupProfileCached(this.context.matrixClient, this.props.groupId).then((profile) => { + this.setState({profile}); + }); + }, + + onClick: function(e) { + e.preventDefault(); + dis.dispatch({ + action: 'view_group', + group_id: this.props.groupId, + }); + }, + + render: function() { + const BaseAvatar = sdk.getComponent('avatars.BaseAvatar'); + const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); + const profile = this.state.profile || {}; + const name = profile.name || this.props.groupId; + const avatarHeight = this.props.avatarHeight; + const descElement = this.props.showDescription ? + <div className="mx_GroupTile_desc">{ profile.shortDescription }</div> : + <div />; + const httpUrl = profile.avatarUrl ? this.context.matrixClient.mxcUrlToHttp( + profile.avatarUrl, avatarHeight, avatarHeight, "crop", + ) : null; + return <AccessibleButton className="mx_GroupTile" onClick={this.onClick}> + <div className="mx_GroupTile_avatar"> + <BaseAvatar name={name} url={httpUrl} width={avatarHeight} height={avatarHeight} /> + </div> + <div className="mx_GroupTile_profile"> + <div className="mx_GroupTile_name">{ name }</div> + { descElement } + <div className="mx_GroupTile_groupId">{ this.props.groupId }</div> + </div> + </AccessibleButton>; + }, +}); + +export default GroupTile; diff --git a/src/components/views/groups/GroupUserSettings.js b/src/components/views/groups/GroupUserSettings.js new file mode 100644 index 0000000000..fd6fb3d85f --- /dev/null +++ b/src/components/views/groups/GroupUserSettings.js @@ -0,0 +1,66 @@ +/* +Copyright 2017 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from 'react'; +import PropTypes from 'prop-types'; +import GeminiScrollbar from 'react-gemini-scrollbar'; +import sdk from '../../../index'; +import { MatrixClient } from 'matrix-js-sdk'; +import { _t } from '../../../languageHandler'; + +export default React.createClass({ + displayName: 'GroupUserSettings', + + contextTypes: { + matrixClient: PropTypes.instanceOf(MatrixClient), + }, + + getInitialState() { + return { + err: null, + groups: [], + }; + }, + + componentWillMount: function() { + this.context.matrixClient.getJoinedGroups().done((result) => { + this.setState({groups: result.groups, error: null}); + }, (err) => { + console.error(err); + this.setState({groups: null, error: err}); + }); + }, + + render() { + const GroupPublicityToggle = sdk.getComponent('groups.GroupPublicityToggle'); + const groupPublicityToggles = this.state.groups.map((groupId, index) => { + return <GroupPublicityToggle key={index} groupId={groupId} />; + }); + return <div> + <h3>{ _t('Flair') }</h3> + <div className="mx_UserSettings_section"> + <p> + { _t('Display your community flair in rooms configured to show it.') } + </p> + <div className="mx_GroupUserSettings_groupPublicity_scrollbox"> + <GeminiScrollbar> + { groupPublicityToggles } + </GeminiScrollbar> + </div> + </div> + </div>; + }, +}); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 128a07bc15..cf108b3a6e 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -227,8 +227,6 @@ "Delete": "Delete", "Disable Notifications": "Disable Notifications", "Enable Notifications": "Enable Notifications", - "URL previews are enabled by default for participants in this room.": "URL previews are enabled by default for participants in this room.", - "URL previews are disabled by default for participants in this room.": "URL previews are disabled by default for participants in this room.", "Cannot add any more widgets": "Cannot add any more widgets", "The maximum permitted number of widgets have already been added to this room.": "The maximum permitted number of widgets have already been added to this room.", "Add a widget": "Add a widget", @@ -456,6 +454,8 @@ "New community ID (e.g. +foo:%(localDomain)s)": "New community ID (e.g. +foo:%(localDomain)s)", "You have <a>enabled</a> URL previews by default.": "You have <a>enabled</a> URL previews by default.", "You have <a>disabled</a> URL previews by default.": "You have <a>disabled</a> URL previews by default.", + "URL previews are enabled by default for participants in this room.": "URL previews are enabled by default for participants in this room.", + "URL previews are disabled by default for participants in this room.": "URL previews are disabled by default for participants in this room.", "URL Previews": "URL Previews", "Error decrypting audio": "Error decrypting audio", "Error decrypting attachment": "Error decrypting attachment", @@ -517,6 +517,8 @@ "Failed to withdraw invitation": "Failed to withdraw invitation", "Failed to remove user from community": "Failed to remove user from community", "Filter community members": "Filter community members", + "Flair will appear if enabled in room settings": "Flair will appear if enabled in room settings", + "Flair will not appear": "Flair will not appear", "Are you sure you want to remove '%(roomName)s' from %(groupId)s?": "Are you sure you want to remove '%(roomName)s' from %(groupId)s?", "Removing a room from the community will also remove it from the community page.": "Removing a room from the community will also remove it from the community page.", "Remove": "Remove", @@ -528,6 +530,8 @@ "Visible to everyone": "Visible to everyone", "Only visible to community members": "Only visible to community members", "Filter community rooms": "Filter community rooms", + "Flair": "Flair", + "Display your community flair in rooms configured to show it.": "Display your community flair in rooms configured to show it.", "Unknown Address": "Unknown Address", "NOTE: Apps are not end-to-end encrypted": "NOTE: Apps are not end-to-end encrypted", "Do you want to load widget from URL:": "Do you want to load widget from URL:", @@ -723,8 +727,6 @@ "%(inviter)s has invited you to join this community": "%(inviter)s has invited you to join this community", "You are an administrator of this community": "You are an administrator of this community", "You are a member of this community": "You are a member of this community", - "Community Member Settings": "Community Member Settings", - "Publish this community on your profile": "Publish this community on your profile", "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!": "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!", "Long Description (HTML)": "Long Description (HTML)", "Description": "Description",