1
0
mirror of https://github.com/matrix-org/matrix-react-sdk.git synced 2025-11-20 16:22:28 +03:00

Merge branches 'develop' and 't3chguy/m.relates_to' of github.com:matrix-org/matrix-react-sdk into t3chguy/m.relates_to

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

# Conflicts:
#	src/i18n/strings/en_EN.json
#	src/i18n/strings/eu.json
#	src/i18n/strings/fr.json
#	src/i18n/strings/lv.json
#	src/i18n/strings/ru.json
#	src/i18n/strings/zh_Hant.json
This commit is contained in:
Michael Telatynski
2018-04-12 09:48:06 +01:00
41 changed files with 2135 additions and 359 deletions

View File

@@ -30,11 +30,21 @@ module.exports = {
ContextualMenuContainerId: "mx_ContextualMenu_Container",
propTypes: {
top: PropTypes.number,
bottom: PropTypes.number,
left: PropTypes.number,
right: PropTypes.number,
menuWidth: PropTypes.number,
menuHeight: PropTypes.number,
chevronOffset: PropTypes.number,
menuColour: PropTypes.string,
chevronFace: PropTypes.string, // top, bottom, left, right
// Function to be called on menu close
onFinished: PropTypes.func,
menuPaddingTop: PropTypes.number,
menuPaddingRight: PropTypes.number,
menuPaddingBottom: PropTypes.number,
menuPaddingLeft: PropTypes.number,
},
getOrCreateContainer: function() {
@@ -138,6 +148,15 @@ module.exports = {
if (!isNaN(Number(props.menuPaddingTop))) {
menuStyle["paddingTop"] = props.menuPaddingTop;
}
if (!isNaN(Number(props.menuPaddingLeft))) {
menuStyle["paddingLeft"] = props.menuPaddingLeft;
}
if (!isNaN(Number(props.menuPaddingBottom))) {
menuStyle["paddingBottom"] = props.menuPaddingBottom;
}
if (!isNaN(Number(props.menuPaddingRight))) {
menuStyle["paddingRight"] = props.menuPaddingRight;
}
// FIXME: If a menu uses getDefaultProps it clobbers the onFinished
// property set here so you can't close the menu from a button click!

View File

@@ -1,6 +1,6 @@
/*
Copyright 2017 Vector Creations Ltd.
Copyright 2017 New Vector Ltd.
Copyright 2017, 2018 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.
@@ -399,6 +399,9 @@ FeaturedRoom.contextTypes = GroupContext;
RoleUserList.contextTypes = GroupContext;
FeaturedUser.contextTypes = GroupContext;
const GROUP_JOINPOLICY_OPEN = "open";
const GROUP_JOINPOLICY_INVITE = "invite";
export default React.createClass({
displayName: 'GroupView',
@@ -463,6 +466,10 @@ export default React.createClass({
_onGroupMyMembership: function(group) {
if (group.groupId !== this.props.groupId) return;
if (group.myMembership === 'leave') {
// Leave settings - the user might have clicked the "Leave" button
this._closeSettings();
}
this.setState({membershipBusy: false});
},
@@ -545,6 +552,12 @@ export default React.createClass({
this.setState({
editing: true,
profileForm: Object.assign({}, this.state.summary.profile),
joinableForm: {
policyType:
this.state.summary.profile.is_openly_joinable ?
GROUP_JOINPOLICY_OPEN :
GROUP_JOINPOLICY_INVITE,
},
});
dis.dispatch({
action: 'panel_disable',
@@ -553,6 +566,10 @@ export default React.createClass({
},
_onCancelClick: function() {
this._closeSettings();
},
_closeSettings() {
this.setState({
editing: false,
profileForm: null,
@@ -607,11 +624,15 @@ export default React.createClass({
}).done();
},
_onJoinableChange: function(ev) {
this.setState({
joinableForm: { policyType: ev.target.value },
});
},
_onSaveClick: function() {
this.setState({saving: true});
const savePromise = this.state.isUserPrivileged ?
this._matrixClient.setGroupProfile(this.props.groupId, this.state.profileForm) :
Promise.resolve();
const savePromise = this.state.isUserPrivileged ? this._saveGroup() : Promise.resolve();
savePromise.then((result) => {
this.setState({
saving: false,
@@ -642,8 +663,20 @@ export default React.createClass({
}).done();
},
_onAcceptInviteClick: function() {
_saveGroup: async function() {
await this._matrixClient.setGroupProfile(this.props.groupId, this.state.profileForm);
await this._matrixClient.setGroupJoinPolicy(this.props.groupId, {
type: this.state.joinableForm.policyType,
});
},
_onAcceptInviteClick: async function() {
this.setState({membershipBusy: true});
// Wait 500ms to prevent flashing. Do this before sending a request otherwise we risk the
// spinner disappearing after we have fetched new group data.
await Promise.delay(500);
this._groupStore.acceptGroupInvite().then(() => {
// don't reset membershipBusy here: wait for the membership change to come down the sync
}).catch((e) => {
@@ -656,9 +689,14 @@ export default React.createClass({
});
},
_onRejectInviteClick: function() {
_onRejectInviteClick: async function() {
this.setState({membershipBusy: true});
this._matrixClient.leaveGroup(this.props.groupId).then(() => {
// Wait 500ms to prevent flashing. Do this before sending a request otherwise we risk the
// spinner disappearing after we have fetched new group data.
await Promise.delay(500);
this._groupStore.leaveGroup().then(() => {
// don't reset membershipBusy here: wait for the membership change to come down the sync
}).catch((e) => {
this.setState({membershipBusy: false});
@@ -670,9 +708,14 @@ export default React.createClass({
});
},
_onJoinClick: function() {
_onJoinClick: async function() {
this.setState({membershipBusy: true});
this._matrixClient.joinGroup(this.props.groupId).then(() => {
// Wait 500ms to prevent flashing. Do this before sending a request otherwise we risk the
// spinner disappearing after we have fetched new group data.
await Promise.delay(500);
this._groupStore.joinGroup().then(() => {
// don't reset membershipBusy here: wait for the membership change to come down the sync
}).catch((e) => {
this.setState({membershipBusy: false});
@@ -691,11 +734,16 @@ export default React.createClass({
description: _t("Leave %(groupName)s?", {groupName: this.props.groupId}),
button: _t("Leave"),
danger: true,
onFinished: (confirmed) => {
onFinished: async (confirmed) => {
if (!confirmed) return;
this.setState({membershipBusy: true});
this._matrixClient.leaveGroup(this.props.groupId).then(() => {
// Wait 500ms to prevent flashing. Do this before sending a request otherwise we risk the
// spinner disappearing after we have fetched new group data.
await Promise.delay(500);
this._groupStore.leaveGroup().then(() => {
// don't reset membershipBusy here: wait for the membership change to come down the sync
}).catch((e) => {
this.setState({membershipBusy: false});
@@ -735,6 +783,7 @@ export default React.createClass({
return <div className={groupSettingsSectionClasses}>
{ header }
{ changeDelayWarning }
{ this._getJoinableNode() }
{ this._getLongDescriptionNode() }
{ this._getRoomsNode() }
</div>;
@@ -927,7 +976,7 @@ export default React.createClass({
if ((!group || group.myMembership === 'leave') &&
this.state.summary &&
this.state.summary.profile &&
Boolean(this.state.summary.profile.is_joinable)
Boolean(this.state.summary.profile.is_openly_joinable)
) {
membershipButtonText = _t("Join this community");
membershipButtonOnClick = this._onJoinClick;
@@ -968,8 +1017,8 @@ export default React.createClass({
return <div className={membershipContainerClasses}>
<div className="mx_GroupView_membershipSubSection">
{ /* Empty div for flex alignment */ }
<div />
{ /* The <div /> is for flex alignment */ }
{ this.state.membershipBusy ? <Spinner /> : <div /> }
<div className="mx_GroupView_membership_buttonContainer">
<AccessibleButton
className={membershipButtonClasses}
@@ -983,6 +1032,41 @@ export default React.createClass({
</div>;
},
_getJoinableNode: function() {
return this.state.editing ? <div>
<h3>
{ _t('Who can join this community?') }
{ this.state.groupJoinableLoading ?
<InlineSpinner /> : <div />
}
</h3>
<div>
<label>
<input type="radio"
value={GROUP_JOINPOLICY_INVITE}
checked={this.state.joinableForm.policyType === GROUP_JOINPOLICY_INVITE}
onClick={this._onJoinableChange}
/>
<div className="mx_GroupView_label_text">
{ _t('Only people who have been invited') }
</div>
</label>
</div>
<div>
<label>
<input type="radio"
value={GROUP_JOINPOLICY_OPEN}
checked={this.state.joinableForm.policyType === GROUP_JOINPOLICY_OPEN}
onClick={this._onJoinableChange}
/>
<div className="mx_GroupView_label_text">
{ _t('Everyone') }
</div>
</label>
</div>
</div> : null;
},
_getLongDescriptionNode: function() {
const summary = this.state.summary;
let description = null;

View File

@@ -447,15 +447,18 @@ module.exports = React.createClass({
// is this a continuation of the previous message?
let continuation = false;
// Some events should appear as continuations from previous events of
// different types.
const continuedTypes = ['m.sticker', 'm.room.message'];
const eventTypeContinues =
prevEvent !== null &&
continuedTypes.includes(mxEv.getType()) &&
continuedTypes.includes(prevEvent.getType());
if (prevEvent !== null
&& prevEvent.sender && mxEv.sender
&& mxEv.sender.userId === prevEvent.sender.userId
// The preferred way of checking for 'continuation messages' is by
// checking whether subsiquent messages from the same user have a
// message body. This is because all messages intended to be displayed
// should have a 'body' whereas some (non-m.room) messages (such as
// m.sticker) may not have a message 'type'.
&& Boolean(mxEv.getContent().body) == Boolean(prevEvent.getContent().body)) {
&& (mxEv.getType() == prevEvent.getType() || eventTypeContinues)) {
continuation = true;
}

View File

@@ -148,7 +148,7 @@ const TagPanel = React.createClass({
</Droppable>
</GeminiScrollbarWrapper>
<div className="mx_TagPanel_divider" />
<div className="mx_TagPanel_createGroupButton">
<div className="mx_TagPanel_groupsButton">
<GroupsButton tooltip={true} />
</div>
</div>;

View File

@@ -23,6 +23,7 @@ import { _t } from '../../../languageHandler';
import sdk from '../../../index';
import Modal from "../../../Modal";
import MatrixClientPeg from "../../../MatrixClientPeg";
import SdkConfig from "../../../SdkConfig";
import PasswordReset from "../../../PasswordReset";
@@ -185,7 +186,7 @@ module.exports = React.createClass({
);
} else {
let serverConfigSection;
if (!config.disable_custom_urls) {
if (!SdkConfig.get().disable_custom_urls) {
serverConfigSection = (
<ServerConfig ref="serverConfig"
withToggleButton={true}