Fix merge conflict

Signed-off-by: Stefan Parviainen <pafcu@iki.fi>
This commit is contained in:
Stefan Parviainen 2017-11-01 15:47:51 +01:00
commit d6f1e12bee
10 changed files with 178 additions and 83 deletions

View file

@ -19,7 +19,7 @@ import PlatformPeg from './PlatformPeg';
import SdkConfig from './SdkConfig';
function getRedactedUrl() {
const redactedHash = window.location.hash.replace(/#\/(room|user)\/(.+)/, "#/$1/<redacted>");
const redactedHash = window.location.hash.replace(/#\/(group|room|user)\/(.+)/, "#/$1/<redacted>");
// hardcoded url to make piwik happy
return 'https://riot.im/app/' + redactedHash;
}

View file

@ -143,6 +143,50 @@ export default class Login {
Object.assign(loginParams, legacyParams);
const client = this._createTemporaryClient();
const tryFallbackHs = (originalError) => {
const fbClient = Matrix.createClient({
baseUrl: self._fallbackHsUrl,
idBaseUrl: this._isUrl,
});
return fbClient.login('m.login.password', loginParams).then(function(data) {
return Promise.resolve({
homeserverUrl: self._fallbackHsUrl,
identityServerUrl: self._isUrl,
userId: data.user_id,
deviceId: data.device_id,
accessToken: data.access_token,
});
}).catch((fallback_error) => {
console.log("fallback HS login failed", fallback_error);
// throw the original error
throw originalError;
});
};
const tryLowercaseUsername = (originalError) => {
const loginParamsLowercase = Object.assign({}, loginParams, {
user: username.toLowerCase(),
identifier: {
user: username.toLowerCase(),
},
});
return client.login('m.login.password', loginParamsLowercase).then(function(data) {
return Promise.resolve({
homeserverUrl: self._hsUrl,
identityServerUrl: self._isUrl,
userId: data.user_id,
deviceId: data.device_id,
accessToken: data.access_token,
});
}).catch((fallback_error) => {
console.log("Lowercase username login failed", fallback_error);
// throw the original error
throw originalError;
});
};
let originalLoginError = null;
return client.login('m.login.password', loginParams).then(function(data) {
return Promise.resolve({
homeserverUrl: self._hsUrl,
@ -151,28 +195,25 @@ export default class Login {
deviceId: data.device_id,
accessToken: data.access_token,
});
}, function(error) {
}).catch((error) => {
originalLoginError = error;
if (error.httpStatus === 403) {
if (self._fallbackHsUrl) {
const fbClient = Matrix.createClient({
baseUrl: self._fallbackHsUrl,
idBaseUrl: this._isUrl,
});
return fbClient.login('m.login.password', loginParams).then(function(data) {
return Promise.resolve({
homeserverUrl: self._fallbackHsUrl,
identityServerUrl: self._isUrl,
userId: data.user_id,
deviceId: data.device_id,
accessToken: data.access_token,
});
}, function(fallback_error) {
// throw the original error
throw error;
});
return tryFallbackHs(originalLoginError);
}
}
throw originalLoginError;
}).catch((error) => {
if (
error.httpStatus === 403 &&
loginParams.identifier.type === 'm.id.user' &&
username.search(/[A-Z]/) > -1
) {
return tryLowercaseUsername(originalLoginError);
}
throw originalLoginError;
}).catch((error) => {
console.log("Login failed", error);
throw error;
});
}

View file

@ -407,6 +407,10 @@ export default React.createClass({
getInitialState: function() {
return {
summary: null,
isGroupPublicised: null,
isUserPrivileged: null,
groupRooms: null,
groupRoomsLoading: null,
error: null,
editing: false,
saving: false,
@ -458,8 +462,14 @@ export default React.createClass({
}
this.setState({
summary,
summaryLoading: !this._groupStore.isStateReady(GroupStore.STATE_KEY.Summary),
isGroupPublicised: this._groupStore.getGroupPublicity(),
isUserPrivileged: this._groupStore.isUserPrivileged(),
groupRooms: this._groupStore.getGroupRooms(),
groupRoomsLoading: !this._groupStore.isStateReady(GroupStore.STATE_KEY.GroupRooms),
isUserMember: this._groupStore.getGroupMembers().some(
(m) => m.userId === MatrixClientPeg.get().credentials.userId,
),
error: null,
});
});
@ -650,6 +660,7 @@ export default React.createClass({
const RoomDetailList = sdk.getComponent('rooms.RoomDetailList');
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
const TintableSvg = sdk.getComponent('elements.TintableSvg');
const Spinner = sdk.getComponent('elements.Spinner');
const addRoomRow = this.state.editing ?
(<AccessibleButton className="mx_GroupView_rooms_header_addRow"
@ -667,7 +678,10 @@ export default React.createClass({
<h3>{ _t('Rooms') }</h3>
{ addRoomRow }
</div>
<RoomDetailList rooms={this._groupStore.getGroupRooms()} />
{ this.state.groupRoomsLoading ?
<Spinner /> :
<RoomDetailList rooms={this.state.groupRooms} />
}
</div>;
},
@ -863,7 +877,7 @@ export default React.createClass({
const Spinner = sdk.getComponent("elements.Spinner");
const TintableSvg = sdk.getComponent("elements.TintableSvg");
if (this.state.summary === null && this.state.error === null || this.state.saving) {
if (this.state.summaryLoading && this.state.error === null || this.state.saving) {
return <Spinner />;
} else if (this.state.summary) {
const summary = this.state.summary;
@ -884,6 +898,7 @@ export default React.createClass({
} else {
const GroupAvatar = sdk.getComponent('avatars.GroupAvatar');
avatarImage = <GroupAvatar groupId={this.props.groupId}
groupName={this.state.profileForm.name}
groupAvatarUrl={this.state.profileForm.avatar_url}
width={48} height={48} resizeMethod='crop'
/>;
@ -927,25 +942,28 @@ export default React.createClass({
tabIndex="2"
dir="auto" />;
} else {
const onGroupHeaderItemClick = this.state.isUserMember ? this._onEditClick : null;
const groupAvatarUrl = summary.profile ? summary.profile.avatar_url : null;
const groupName = summary.profile ? summary.profile.name : null;
avatarNode = <GroupAvatar
groupId={this.props.groupId}
groupAvatarUrl={groupAvatarUrl}
onClick={this._onEditClick}
groupName={groupName}
onClick={onGroupHeaderItemClick}
width={48} height={48}
/>;
if (summary.profile && summary.profile.name) {
nameNode = <div onClick={this._onEditClick}>
nameNode = <div onClick={onGroupHeaderItemClick}>
<span>{ summary.profile.name }</span>
<span className="mx_GroupView_header_groupid">
({ this.props.groupId })
</span>
</div>;
} else {
nameNode = <span onClick={this._onEditClick}>{ this.props.groupId }</span>;
nameNode = <span onClick={onGroupHeaderItemClick}>{ this.props.groupId }</span>;
}
if (summary.profile && summary.profile.short_description) {
shortDescNode = <span onClick={this._onEditClick}>{ summary.profile.short_description }</span>;
shortDescNode = <span onClick={onGroupHeaderItemClick}>{ summary.profile.short_description }</span>;
}
}
if (this.state.editing) {
@ -986,6 +1004,7 @@ export default React.createClass({
const headerClasses = {
mx_GroupView_header: true,
mx_GroupView_header_view: !this.state.editing,
mx_GroupView_header_isUserMember: this.state.isUserMember,
};
return (

View file

@ -62,7 +62,9 @@ const GroupTile = React.createClass({
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) : null;
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} />

View file

@ -24,6 +24,7 @@ export default React.createClass({
propTypes: {
groupId: PropTypes.string,
groupName: PropTypes.string,
groupAvatarUrl: PropTypes.string,
width: PropTypes.number,
height: PropTypes.number,
@ -57,7 +58,7 @@ export default React.createClass({
return (
<BaseAvatar
name={this.props.groupId[1]}
name={this.props.groupName || this.props.groupId[1]}
idName={this.props.groupId}
url={this.getGroupAvatarUrl()}
{...otherProps}

View file

@ -55,8 +55,8 @@ export default React.createClass({
_checkGroupId: function(e) {
let error = null;
if (!/^[a-zA-Z0-9]*$/.test(this.state.groupId)) {
error = _t("Community IDs may only contain alphanumeric characters");
if (!/^[a-z0-9=_\-\.\/]*$/.test(this.state.groupId)) {
error = _t("Community IDs may only contain characters a-z, 0-9, or '=_-./'");
}
this.setState({
groupIdError: error,

View file

@ -17,50 +17,66 @@ limitations under the License.
import PropTypes from 'prop-types';
import React from 'react';
import { MatrixClient } from 'matrix-js-sdk';
import dis from '../../../dispatcher';
import Modal from '../../../Modal';
import sdk from '../../../index';
import { _t } from '../../../languageHandler';
import { GroupMemberType } from '../../../groups';
import { groupMemberFromApiObject } from '../../../groups';
import withMatrixClient from '../../../wrappers/withMatrixClient';
import GroupStoreCache from '../../../stores/GroupStoreCache';
import AccessibleButton from '../elements/AccessibleButton';
import GeminiScrollbar from 'react-gemini-scrollbar';
module.exports = withMatrixClient(React.createClass({
module.exports = React.createClass({
displayName: 'GroupMemberInfo',
contextTypes: {
matrixClient: PropTypes.instanceOf(MatrixClient),
},
propTypes: {
matrixClient: PropTypes.object.isRequired,
groupId: PropTypes.string,
groupMember: GroupMemberType,
isInvited: PropTypes.bool,
},
getInitialState: function() {
return {
fetching: false,
removingUser: false,
groupMembers: null,
isUserPrivilegedInGroup: null,
};
},
componentWillMount: function() {
this._fetchMembers();
this._initGroupStore(this.props.groupId);
},
_fetchMembers: function() {
this.setState({fetching: true});
this.props.matrixClient.getGroupUsers(this.props.groupId).then((result) => {
this.setState({
groupMembers: result.chunk.map((apiMember) => {
return groupMemberFromApiObject(apiMember);
}),
fetching: false,
});
}).catch((e) => {
this.setState({fetching: false});
console.error("Failed to get group groupMember list: ", e);
componentWillReceiveProps(newProps) {
if (newProps.groupId !== this.props.groupId) {
this._unregisterGroupStore();
this._initGroupStore(newProps.groupId);
}
},
_initGroupStore(groupId) {
this._groupStore = GroupStoreCache.getGroupStore(
this.context.matrixClient, this.props.groupId,
);
this._groupStore.registerListener(this.onGroupStoreUpdated);
},
_unregisterGroupStore() {
if (this._groupStore) {
this._groupStore.unregisterListener(this.onGroupStoreUpdated);
}
},
onGroupStoreUpdated: function() {
this.setState({
isUserInvited: this._groupStore.getGroupInvitedMembers().some(
(m) => m.userId === this.props.groupMember.userId,
),
isUserPrivilegedInGroup: this._groupStore.isUserPrivileged(),
});
},
@ -68,14 +84,14 @@ module.exports = withMatrixClient(React.createClass({
const ConfirmUserActionDialog = sdk.getComponent("dialogs.ConfirmUserActionDialog");
Modal.createDialog(ConfirmUserActionDialog, {
groupMember: this.props.groupMember,
action: _t('Remove from community'),
title: _t('Remove this user from community?'),
action: this.state.isUserInvited ? _t('Disinvite') : _t('Remove from community'),
title: this.state.isUserInvited ? _t('Disinvite this user from community?') : _t('Remove this user from community?'),
danger: true,
onFinished: (proceed) => {
if (!proceed) return;
this.setState({removingUser: true});
this.props.matrixClient.removeUserFromGroup(
this.context.matrixClient.removeUserFromGroup(
this.props.groupId, this.props.groupMember.userId,
).then(() => {
// return to the user list
@ -87,7 +103,9 @@ module.exports = withMatrixClient(React.createClass({
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Failed to remove user from group', '', ErrorDialog, {
title: _t('Error'),
description: _t('Failed to remove user from community'),
description: this.state.isUserInvited ?
_t('Failed to withdraw invitation') :
_t('Failed to remove user from community'),
});
}).finally(() => {
this.setState({removingUser: false});
@ -112,24 +130,17 @@ module.exports = withMatrixClient(React.createClass({
},
render: function() {
if (this.state.fetching || this.state.removingUser) {
if (this.state.removingUser) {
const Spinner = sdk.getComponent("elements.Spinner");
return <Spinner />;
}
if (!this.state.groupMembers) return null;
const targetIsInGroup = this.state.groupMembers.some((m) => {
return m.userId === this.props.groupMember.userId;
});
let kickButton;
let adminButton;
if (targetIsInGroup) {
kickButton = (
let adminTools;
if (this.state.isUserPrivilegedInGroup) {
const kickButton = (
<AccessibleButton className="mx_MemberInfo_field"
onClick={this._onKick}>
{ _t('Remove from community') }
{ this.state.isUserInvited ? _t('Disinvite') : _t('Remove from community') }
</AccessibleButton>
);
@ -138,22 +149,19 @@ module.exports = withMatrixClient(React.createClass({
giveModButton = <AccessibleButton className="mx_MemberInfo_field" onClick={this.onModToggle}>
{giveOpLabel}
</AccessibleButton>;*/
if (kickButton) {
adminTools =
<div className="mx_MemberInfo_adminTools">
<h3>{ _t("Admin Tools") }</h3>
<div className="mx_MemberInfo_buttons">
{ kickButton }
</div>
</div>;
}
}
let adminTools;
if (kickButton || adminButton) {
adminTools =
<div className="mx_MemberInfo_adminTools">
<h3>{ _t("Admin Tools") }</h3>
<div className="mx_MemberInfo_buttons">
{ kickButton }
{ adminButton }
</div>
</div>;
}
const avatarUrl = this.props.matrixClient.mxcUrlToHttp(
const avatarUrl = this.context.matrixClient.mxcUrlToHttp(
this.props.groupMember.avatarUrl,
36, 36, 'crop',
);
@ -193,4 +201,4 @@ module.exports = withMatrixClient(React.createClass({
</div>
);
},
}));
});

View file

@ -184,7 +184,8 @@ module.exports = React.createClass({
});
},
onClickChange: function() {
onClickChange: function(ev) {
ev.preventDefault();
const oldPassword = this.state.cachedPassword || this.refs.old_input.value;
const newPassword = this.refs.new_input.value;
const confirmPassword = this.refs.confirm_input.value;

View file

@ -152,7 +152,6 @@
"%(widgetName)s widget removed by %(senderName)s": "%(widgetName)s widget removed by %(senderName)s",
"Communities": "Communities",
"Message Pinning": "Message Pinning",
"Mention": "Mention",
"%(displayName)s is typing": "%(displayName)s is typing",
"%(names)s and %(count)s others are typing|other": "%(names)s and %(count)s others are typing",
"%(names)s and %(count)s others are typing|one": "%(names)s and one other is typing",
@ -244,6 +243,7 @@
"Unignore": "Unignore",
"Ignore": "Ignore",
"Jump to read receipt": "Jump to read receipt",
"Mention": "Mention",
"Invite": "Invite",
"User Options": "User Options",
"Direct chats": "Direct chats",
@ -492,7 +492,9 @@
"Identity server URL": "Identity server URL",
"What does this mean?": "What does this mean?",
"Remove from community": "Remove from community",
"Disinvite this user from community?": "Disinvite this user from community?",
"Remove this user from community?": "Remove this user from community?",
"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",
"Filter community rooms": "Filter community rooms",
@ -593,7 +595,7 @@
"Start Chatting": "Start Chatting",
"Confirm Removal": "Confirm Removal",
"Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.": "Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.",
"Community IDs may only contain alphanumeric characters": "Community IDs may only contain alphanumeric characters",
"Community IDs may only contain characters a-z, 0-9, or '=_-./'": "Community IDs may only contain characters a-z, 0-9, or '=_-./'",
"Something went wrong whilst creating your community": "Something went wrong whilst creating your community",
"Create Community": "Create Community",
"Community Name": "Community Name",

View file

@ -23,12 +23,23 @@ import FlairStore from './FlairStore';
* other useful group APIs that may have an effect on the group summary.
*/
export default class GroupStore extends EventEmitter {
static STATE_KEY = {
GroupMembers: 'GroupMembers',
GroupInvitedMembers: 'GroupInvitedMembers',
Summary: 'Summary',
GroupRooms: 'GroupRooms',
};
constructor(matrixClient, groupId) {
super();
this.groupId = groupId;
this._matrixClient = matrixClient;
this._summary = {};
this._rooms = [];
this._members = [];
this._invitedMembers = [];
this._ready = {};
this.on('error', (err) => {
console.error(`GroupStore for ${this.groupId} encountered error`, err);
@ -40,6 +51,7 @@ export default class GroupStore extends EventEmitter {
this._members = result.chunk.map((apiMember) => {
return groupMemberFromApiObject(apiMember);
});
this._ready[GroupStore.STATE_KEY.GroupMembers] = true;
this._notifyListeners();
}).catch((err) => {
console.error("Failed to get group member list: " + err);
@ -50,6 +62,7 @@ export default class GroupStore extends EventEmitter {
this._invitedMembers = result.chunk.map((apiMember) => {
return groupMemberFromApiObject(apiMember);
});
this._ready[GroupStore.STATE_KEY.GroupInvitedMembers] = true;
this._notifyListeners();
}).catch((err) => {
// Invited users not visible to non-members
@ -64,6 +77,7 @@ export default class GroupStore extends EventEmitter {
_fetchSummary() {
this._matrixClient.getGroupSummary(this.groupId).then((resp) => {
this._summary = resp;
this._ready[GroupStore.STATE_KEY.Summary] = true;
this._notifyListeners();
}).catch((err) => {
this.emit('error', err);
@ -75,6 +89,7 @@ export default class GroupStore extends EventEmitter {
this._rooms = resp.chunk.map((apiRoom) => {
return groupRoomFromApiObject(apiRoom);
});
this._ready[GroupStore.STATE_KEY.GroupRooms] = true;
this._notifyListeners();
}).catch((err) => {
this.emit('error', err);
@ -87,6 +102,8 @@ export default class GroupStore extends EventEmitter {
registerListener(fn) {
this.on('update', fn);
// Call to set initial state (before fetching starts)
this.emit('update');
this._fetchSummary();
this._fetchRooms();
this._fetchMembers();
@ -96,6 +113,10 @@ export default class GroupStore extends EventEmitter {
this.removeListener('update', fn);
}
isStateReady(id) {
return this._ready[id];
}
getSummary() {
return this._summary;
}