From 9ed9422af84dc3095c3064820bc01b2b29422778 Mon Sep 17 00:00:00 2001 From: lukebarnard Date: Tue, 21 Nov 2017 11:50:41 +0000 Subject: [PATCH 1/4] 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
-

{ _t("Community Member Settings") }

-
- - -
-
; - }, - _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 -
- -
-
-

{ name }

-
{ desc }
-
{ this.props.groupId }
-
-
; - }, -}); - 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 ; + }, + _renderReferral: function() { const teamToken = this.props.teamToken; if (!teamToken) { @@ -1249,6 +1254,8 @@ module.exports = React.createClass({ { accountJsx } + { 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 = ; + 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
+ + +
; + }, +}); 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 ? +
{ profile.shortDescription }
: +
; + const httpUrl = profile.avatarUrl ? this.context.matrixClient.mxcUrlToHttp( + profile.avatarUrl, avatarHeight, avatarHeight, "crop", + ) : null; + return +
+ +
+
+
{ name }
+ { descElement } +
{ this.props.groupId }
+
+
; + }, +}); + +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 ; + }); + return
+

{ _t('Flair') }

+
+

+ { _t('Display your community flair in rooms configured to show it.') } +

+
+ + { groupPublicityToggles } + +
+
+
; + }, +}); 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 enabled URL previews by default.": "You have enabled URL previews by default.", "You have disabled URL previews by default.": "You have disabled 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.
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.
Click here to open settings and give it one!", "Long Description (HTML)": "Long Description (HTML)", "Description": "Description", From 3342754a7243a630ab364d4334acf846be5ed1a9 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 28 Nov 2017 10:11:25 +0000 Subject: [PATCH 2/4] Catch and log errors from getting group profile --- src/components/views/groups/GroupTile.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/views/groups/GroupTile.js b/src/components/views/groups/GroupTile.js index 35c8dde2ab..7ea67de98e 100644 --- a/src/components/views/groups/GroupTile.js +++ b/src/components/views/groups/GroupTile.js @@ -52,6 +52,8 @@ const GroupTile = React.createClass({ componentWillMount: function() { FlairStore.getGroupProfileCached(this.context.matrixClient, this.props.groupId).then((profile) => { this.setState({profile}); + }).catch((err) => { + console.error('Error whilst getting cached profile for GroupTile', err); }); }, From 0d5f7ef2460eb23169ecec8881b34f091323f7aa Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 28 Nov 2017 10:12:46 +0000 Subject: [PATCH 3/4] Avoid NPE --- src/components/views/groups/GroupUserSettings.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/groups/GroupUserSettings.js b/src/components/views/groups/GroupUserSettings.js index fd6fb3d85f..4d72c96a1f 100644 --- a/src/components/views/groups/GroupUserSettings.js +++ b/src/components/views/groups/GroupUserSettings.js @@ -31,7 +31,7 @@ export default React.createClass({ getInitialState() { return { err: null, - groups: [], + groups: null, }; }, @@ -46,7 +46,7 @@ export default React.createClass({ render() { const GroupPublicityToggle = sdk.getComponent('groups.GroupPublicityToggle'); - const groupPublicityToggles = this.state.groups.map((groupId, index) => { + const groupPublicityToggles = (this.state.groups || []).map((groupId, index) => { return ; }); return
From d4f5e7e6d71fbc84e8a95220328aef0249114b58 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 28 Nov 2017 10:46:20 +0000 Subject: [PATCH 4/4] Deal with errors, lack of groups, loading --- .../views/groups/GroupUserSettings.js | 49 ++++++++++++++----- src/i18n/strings/en_EN.json | 5 +- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/src/components/views/groups/GroupUserSettings.js b/src/components/views/groups/GroupUserSettings.js index 4d72c96a1f..755d6aae8f 100644 --- a/src/components/views/groups/GroupUserSettings.js +++ b/src/components/views/groups/GroupUserSettings.js @@ -30,37 +30,60 @@ export default React.createClass({ getInitialState() { return { - err: null, + error: null, groups: null, }; }, componentWillMount: function() { this.context.matrixClient.getJoinedGroups().done((result) => { - this.setState({groups: result.groups, error: null}); + 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 ; - }); + _renderGroupPublicity() { + let text = ""; + let scrollbox =
; + const groups = this.state.groups; + + if (this.state.error) { + text = _t('Something went wrong when trying to get your communities.'); + } else if (groups === null) { + text = _t('Loading...'); + } else if (groups.length > 0) { + const GroupPublicityToggle = sdk.getComponent('groups.GroupPublicityToggle'); + const groupPublicityToggles = groups.map((groupId, index) => { + return ; + }); + text = _t('Display your community flair in rooms configured to show it.'); + scrollbox =
+ + { groupPublicityToggles } + +
; + } else { + text = _t("You're not currently a member of any communities."); + } + return

{ _t('Flair') }

- { _t('Display your community flair in rooms configured to show it.') } + { text }

-
- - { groupPublicityToggles } - -
+ { scrollbox }
; }, + + render() { + const groupPublicity = this._renderGroupPublicity(); + + return
+ { groupPublicity } +
; + }, }); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index cf108b3a6e..b07fabbacf 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -530,8 +530,10 @@ "Visible to everyone": "Visible to everyone", "Only visible to community members": "Only visible to community members", "Filter community rooms": "Filter community rooms", - "Flair": "Flair", + "Something went wrong when trying to get your communities.": "Something went wrong when trying to get your communities.", "Display your community flair in rooms configured to show it.": "Display your community flair in rooms configured to show it.", + "You're not currently a member of any communities.": "You're not currently a member of any communities.", + "Flair": "Flair", "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:", @@ -742,7 +744,6 @@ "For security, this session has been signed out. Please sign in again.": "For security, this session has been signed out. Please sign in again.", "Logout": "Logout", "Your Communities": "Your Communities", - "You're not currently a member of any communities.": "You're not currently a member of any communities.", "Error whilst fetching joined communities": "Error whilst fetching joined communities", "Create a new community": "Create a new community", "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.",