Merge remote-tracking branch 'origin/develop' into matthew/status
This commit is contained in:
commit
858c3a2548
44 changed files with 576 additions and 317 deletions
|
@ -232,9 +232,14 @@ for (const path of SEARCH_PATHS) {
|
||||||
|
|
||||||
const trObj = {};
|
const trObj = {};
|
||||||
for (const tr of translatables) {
|
for (const tr of translatables) {
|
||||||
trObj[tr] = tr;
|
|
||||||
if (tr.includes("|")) {
|
if (tr.includes("|")) {
|
||||||
|
if (inputTranslationsRaw[tr]) {
|
||||||
trObj[tr] = inputTranslationsRaw[tr];
|
trObj[tr] = inputTranslationsRaw[tr];
|
||||||
|
} else {
|
||||||
|
trObj[tr] = tr.split("|")[0];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
trObj[tr] = tr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -96,13 +96,6 @@ function _onGroupInviteFinished(groupId, addrs) {
|
||||||
title: _t("Failed to invite the following users to %(groupId)s:", {groupId: groupId}),
|
title: _t("Failed to invite the following users to %(groupId)s:", {groupId: groupId}),
|
||||||
description: errorList.join(", "),
|
description: errorList.join(", "),
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
|
||||||
Modal.createTrackedDialog('Group invitations sent', '', QuestionDialog, {
|
|
||||||
title: _t("Invites sent"),
|
|
||||||
description: _t("Your community invitations have been sent."),
|
|
||||||
hasCancelButton: false,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||||
|
|
|
@ -21,6 +21,8 @@ import Modal from './Modal';
|
||||||
import { getAddressType } from './UserAddress';
|
import { getAddressType } from './UserAddress';
|
||||||
import createRoom from './createRoom';
|
import createRoom from './createRoom';
|
||||||
import sdk from './';
|
import sdk from './';
|
||||||
|
import dis from './dispatcher';
|
||||||
|
import DMRoomMap from './utils/DMRoomMap';
|
||||||
import { _t } from './languageHandler';
|
import { _t } from './languageHandler';
|
||||||
|
|
||||||
export function inviteToRoom(roomId, addr) {
|
export function inviteToRoom(roomId, addr) {
|
||||||
|
@ -79,15 +81,40 @@ function _onStartChatFinished(shouldInvite, addrs) {
|
||||||
const addrTexts = addrs.map((addr) => addr.address);
|
const addrTexts = addrs.map((addr) => addr.address);
|
||||||
|
|
||||||
if (_isDmChat(addrTexts)) {
|
if (_isDmChat(addrTexts)) {
|
||||||
|
const rooms = _getDirectMessageRooms(addrTexts[0]);
|
||||||
|
if (rooms.length > 0) {
|
||||||
|
// A Direct Message room already exists for this user, so select a
|
||||||
|
// room from a list that is similar to the one in MemberInfo panel
|
||||||
|
const ChatCreateOrReuseDialog = sdk.getComponent(
|
||||||
|
"views.dialogs.ChatCreateOrReuseDialog",
|
||||||
|
);
|
||||||
|
const close = Modal.createTrackedDialog('Create or Reuse', '', ChatCreateOrReuseDialog, {
|
||||||
|
userId: addrTexts[0],
|
||||||
|
onNewDMClick: () => {
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'start_chat',
|
||||||
|
user_id: addrTexts[0],
|
||||||
|
});
|
||||||
|
close(true);
|
||||||
|
},
|
||||||
|
onExistingRoomSelected: (roomId) => {
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'view_room',
|
||||||
|
room_id: roomId,
|
||||||
|
});
|
||||||
|
close(true);
|
||||||
|
},
|
||||||
|
}).close;
|
||||||
|
} else {
|
||||||
// Start a new DM chat
|
// Start a new DM chat
|
||||||
createRoom({dmUserId: addrTexts[0]}).catch((err) => {
|
createRoom({dmUserId: addrTexts[0]}).catch((err) => {
|
||||||
console.error(err.stack);
|
|
||||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||||
Modal.createTrackedDialog('Failed to invite user', '', ErrorDialog, {
|
Modal.createTrackedDialog('Failed to invite user', '', ErrorDialog, {
|
||||||
title: _t("Failed to invite user"),
|
title: _t("Failed to invite user"),
|
||||||
description: ((err && err.message) ? err.message : _t("Operation failed")),
|
description: ((err && err.message) ? err.message : _t("Operation failed")),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Start multi user chat
|
// Start multi user chat
|
||||||
let room;
|
let room;
|
||||||
|
@ -153,3 +180,19 @@ function _showAnyInviteErrors(addrs, room) {
|
||||||
return addrs;
|
return addrs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _getDirectMessageRooms(addr) {
|
||||||
|
const dmRoomMap = new DMRoomMap(MatrixClientPeg.get());
|
||||||
|
const dmRooms = dmRoomMap.getDMRoomsForUserId(addr);
|
||||||
|
const rooms = [];
|
||||||
|
dmRooms.forEach((dmRoom) => {
|
||||||
|
const room = MatrixClientPeg.get().getRoom(dmRoom);
|
||||||
|
if (room) {
|
||||||
|
const me = room.getMember(MatrixClientPeg.get().credentials.userId);
|
||||||
|
if (me.membership == 'join') {
|
||||||
|
rooms.push(room);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return rooms;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -116,7 +116,6 @@ const FilePanel = React.createClass({
|
||||||
timelineSet={this.state.timelineSet}
|
timelineSet={this.state.timelineSet}
|
||||||
showUrlPreview = {false}
|
showUrlPreview = {false}
|
||||||
tileShape="file_grid"
|
tileShape="file_grid"
|
||||||
opacity={this.props.opacity}
|
|
||||||
empty={_t('There are no visible files in this room')}
|
empty={_t('There are no visible files in this room')}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -447,7 +447,7 @@ export default React.createClass({
|
||||||
|
|
||||||
_initGroupStore: function(groupId) {
|
_initGroupStore: function(groupId) {
|
||||||
this._groupStore = GroupStoreCache.getGroupStore(MatrixClientPeg.get(), groupId);
|
this._groupStore = GroupStoreCache.getGroupStore(MatrixClientPeg.get(), groupId);
|
||||||
this._groupStore.on('update', () => {
|
this._groupStore.registerListener(() => {
|
||||||
const summary = this._groupStore.getSummary();
|
const summary = this._groupStore.getSummary();
|
||||||
if (summary.profile) {
|
if (summary.profile) {
|
||||||
// Default profile fields should be "" for later sending to the server (which
|
// Default profile fields should be "" for later sending to the server (which
|
||||||
|
@ -464,7 +464,6 @@ export default React.createClass({
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this._groupStore.on('error', (err) => {
|
this._groupStore.on('error', (err) => {
|
||||||
console.error(err);
|
|
||||||
this.setState({
|
this.setState({
|
||||||
summary: null,
|
summary: null,
|
||||||
error: err,
|
error: err,
|
||||||
|
@ -481,6 +480,10 @@ export default React.createClass({
|
||||||
editing: true,
|
editing: true,
|
||||||
profileForm: Object.assign({}, this.state.summary.profile),
|
profileForm: Object.assign({}, this.state.summary.profile),
|
||||||
});
|
});
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'panel_disable',
|
||||||
|
sideDisabled: true,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
_onCancelClick: function() {
|
_onCancelClick: function() {
|
||||||
|
@ -488,6 +491,7 @@ export default React.createClass({
|
||||||
editing: false,
|
editing: false,
|
||||||
profileForm: null,
|
profileForm: null,
|
||||||
});
|
});
|
||||||
|
dis.dispatch({action: 'panel_disable'});
|
||||||
},
|
},
|
||||||
|
|
||||||
_onNameChange: function(value) {
|
_onNameChange: function(value) {
|
||||||
|
@ -535,12 +539,16 @@ export default React.createClass({
|
||||||
|
|
||||||
_onSaveClick: function() {
|
_onSaveClick: function() {
|
||||||
this.setState({saving: true});
|
this.setState({saving: true});
|
||||||
MatrixClientPeg.get().setGroupProfile(this.props.groupId, this.state.profileForm).then((result) => {
|
const savePromise = this.state.isUserPrivileged ?
|
||||||
|
MatrixClientPeg.get().setGroupProfile(this.props.groupId, this.state.profileForm) :
|
||||||
|
Promise.resolve();
|
||||||
|
savePromise.then((result) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
saving: false,
|
saving: false,
|
||||||
editing: false,
|
editing: false,
|
||||||
summary: null,
|
summary: null,
|
||||||
});
|
});
|
||||||
|
dis.dispatch({action: 'panel_disable'});
|
||||||
this._initGroupStore(this.props.groupId);
|
this._initGroupStore(this.props.groupId);
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -624,23 +632,40 @@ export default React.createClass({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_getGroupSection: function() {
|
||||||
|
const groupSettingsSectionClasses = classnames({
|
||||||
|
"mx_GroupView_group": this.state.editing,
|
||||||
|
"mx_GroupView_group_disabled": this.state.editing && !this.state.isUserPrivileged,
|
||||||
|
});
|
||||||
|
|
||||||
|
const header = this.state.editing ? <h2> { _t('Community Settings') } </h2> : <div />;
|
||||||
|
return <div className={groupSettingsSectionClasses}>
|
||||||
|
{ header }
|
||||||
|
{ this._getLongDescriptionNode() }
|
||||||
|
{ this._getRoomsNode() }
|
||||||
|
</div>;
|
||||||
|
},
|
||||||
|
|
||||||
_getRoomsNode: function() {
|
_getRoomsNode: function() {
|
||||||
const RoomDetailList = sdk.getComponent('rooms.RoomDetailList');
|
const RoomDetailList = sdk.getComponent('rooms.RoomDetailList');
|
||||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
const TintableSvg = sdk.getComponent('elements.TintableSvg');
|
const TintableSvg = sdk.getComponent('elements.TintableSvg');
|
||||||
const addButton = this.state.editing ?
|
|
||||||
(<AccessibleButton onClick={this._onAddRoomsClick} >
|
const addRoomRow = this.state.editing ?
|
||||||
<div className="mx_GroupView_rooms_header_addButton" >
|
(<AccessibleButton className="mx_GroupView_rooms_header_addRow"
|
||||||
|
onClick={this._onAddRoomsClick}
|
||||||
|
>
|
||||||
|
<div className="mx_GroupView_rooms_header_addRow_button">
|
||||||
<TintableSvg src="img/icons-room-add.svg" width="24" height="24" />
|
<TintableSvg src="img/icons-room-add.svg" width="24" height="24" />
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_GroupView_rooms_header_addButton_label">
|
<div className="mx_GroupView_rooms_header_addRow_label">
|
||||||
{ _t('Add rooms to this community') }
|
{ _t('Add rooms to this community') }
|
||||||
</div>
|
</div>
|
||||||
</AccessibleButton>) : <div />;
|
</AccessibleButton>) : <div />;
|
||||||
return <div className="mx_GroupView_rooms">
|
return <div className="mx_GroupView_rooms">
|
||||||
<div className="mx_GroupView_rooms_header">
|
<div className="mx_GroupView_rooms_header">
|
||||||
<h3>{ _t('Rooms') }</h3>
|
<h3>{ _t('Rooms') }</h3>
|
||||||
{ addButton }
|
{ addRoomRow }
|
||||||
</div>
|
</div>
|
||||||
<RoomDetailList rooms={this._groupStore.getGroupRooms()} />
|
<RoomDetailList rooms={this._groupStore.getGroupRooms()} />
|
||||||
</div>;
|
</div>;
|
||||||
|
@ -761,8 +786,8 @@ export default React.createClass({
|
||||||
</div>;
|
</div>;
|
||||||
} else if (group.myMembership === 'join' && this.state.editing) {
|
} else if (group.myMembership === 'join' && this.state.editing) {
|
||||||
const leaveButtonTooltip = this.state.isUserPrivileged ?
|
const leaveButtonTooltip = this.state.isUserPrivileged ?
|
||||||
_t("You are a member of this community") :
|
_t("You are an administrator of this community") :
|
||||||
_t("You are an administrator of this community");
|
_t("You are a member of this community");
|
||||||
const leaveButtonClasses = classnames({
|
const leaveButtonClasses = classnames({
|
||||||
"mx_RoomHeader_textButton": true,
|
"mx_RoomHeader_textButton": true,
|
||||||
"mx_GroupView_textButton": true,
|
"mx_GroupView_textButton": true,
|
||||||
|
@ -790,7 +815,7 @@ export default React.createClass({
|
||||||
|
|
||||||
_getMemberSettingsSection: function() {
|
_getMemberSettingsSection: function() {
|
||||||
return <div className="mx_GroupView_memberSettings">
|
return <div className="mx_GroupView_memberSettings">
|
||||||
<h3> { _t("Community Member Settings") } </h3>
|
<h2> { _t("Community Member Settings") } </h2>
|
||||||
<div className="mx_GroupView_memberSettings_toggle">
|
<div className="mx_GroupView_memberSettings_toggle">
|
||||||
<input type="checkbox"
|
<input type="checkbox"
|
||||||
onClick={this._onPublicityToggle}
|
onClick={this._onPublicityToggle}
|
||||||
|
@ -813,8 +838,13 @@ export default React.createClass({
|
||||||
if (summary.profile && summary.profile.long_description) {
|
if (summary.profile && summary.profile.long_description) {
|
||||||
description = sanitizedHtmlNode(summary.profile.long_description);
|
description = sanitizedHtmlNode(summary.profile.long_description);
|
||||||
}
|
}
|
||||||
return this.state.editing && this.state.isUserPrivileged ?
|
const groupDescEditingClasses = classnames({
|
||||||
<div className="mx_GroupView_groupDesc">
|
"mx_GroupView_groupDesc": true,
|
||||||
|
"mx_GroupView_groupDesc_disabled": !this.state.isUserPrivileged,
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.state.editing ?
|
||||||
|
<div className={groupDescEditingClasses}>
|
||||||
<h3> { _t("Long Description (HTML)") } </h3>
|
<h3> { _t("Long Description (HTML)") } </h3>
|
||||||
<textarea
|
<textarea
|
||||||
value={this.state.profileForm.long_description}
|
value={this.state.profileForm.long_description}
|
||||||
|
@ -844,14 +874,10 @@ export default React.createClass({
|
||||||
const bodyNodes = [
|
const bodyNodes = [
|
||||||
this._getMembershipSection(),
|
this._getMembershipSection(),
|
||||||
this.state.editing ? this._getMemberSettingsSection() : null,
|
this.state.editing ? this._getMemberSettingsSection() : null,
|
||||||
this._getLongDescriptionNode(),
|
this._getGroupSection(),
|
||||||
this._getRoomsNode(),
|
|
||||||
];
|
];
|
||||||
const rightButtons = [];
|
const rightButtons = [];
|
||||||
const headerClasses = {
|
if (this.state.editing && this.state.isUserPrivileged) {
|
||||||
mx_GroupView_header: true,
|
|
||||||
};
|
|
||||||
if (this.state.editing) {
|
|
||||||
let avatarImage;
|
let avatarImage;
|
||||||
if (this.state.uploadingAvatar) {
|
if (this.state.uploadingAvatar) {
|
||||||
avatarImage = <Spinner />;
|
avatarImage = <Spinner />;
|
||||||
|
@ -900,24 +926,12 @@ export default React.createClass({
|
||||||
onValueChanged={this._onShortDescChange}
|
onValueChanged={this._onShortDescChange}
|
||||||
tabIndex="2"
|
tabIndex="2"
|
||||||
dir="auto" />;
|
dir="auto" />;
|
||||||
rightButtons.push(
|
|
||||||
<AccessibleButton className="mx_GroupView_textButton mx_RoomHeader_textButton"
|
|
||||||
onClick={this._onSaveClick} key="_saveButton"
|
|
||||||
>
|
|
||||||
{ _t('Save') }
|
|
||||||
</AccessibleButton>,
|
|
||||||
);
|
|
||||||
rightButtons.push(
|
|
||||||
<AccessibleButton className="mx_RoomHeader_cancelButton" onClick={this._onCancelClick} key="_cancelButton">
|
|
||||||
<img src="img/cancel.svg" className="mx_filterFlipColor"
|
|
||||||
width="18" height="18" alt={_t("Cancel")} />
|
|
||||||
</AccessibleButton>,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
const groupAvatarUrl = summary.profile ? summary.profile.avatar_url : null;
|
const groupAvatarUrl = summary.profile ? summary.profile.avatar_url : null;
|
||||||
avatarNode = <GroupAvatar
|
avatarNode = <GroupAvatar
|
||||||
groupId={this.props.groupId}
|
groupId={this.props.groupId}
|
||||||
groupAvatarUrl={groupAvatarUrl}
|
groupAvatarUrl={groupAvatarUrl}
|
||||||
|
onClick={this._onEditClick}
|
||||||
width={48} height={48}
|
width={48} height={48}
|
||||||
/>;
|
/>;
|
||||||
if (summary.profile && summary.profile.name) {
|
if (summary.profile && summary.profile.name) {
|
||||||
|
@ -933,6 +947,23 @@ export default React.createClass({
|
||||||
if (summary.profile && summary.profile.short_description) {
|
if (summary.profile && summary.profile.short_description) {
|
||||||
shortDescNode = <span onClick={this._onEditClick}>{ summary.profile.short_description }</span>;
|
shortDescNode = <span onClick={this._onEditClick}>{ summary.profile.short_description }</span>;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (this.state.editing) {
|
||||||
|
rightButtons.push(
|
||||||
|
<AccessibleButton className="mx_GroupView_textButton mx_RoomHeader_textButton"
|
||||||
|
onClick={this._onSaveClick} key="_saveButton"
|
||||||
|
>
|
||||||
|
{ _t('Save') }
|
||||||
|
</AccessibleButton>,
|
||||||
|
);
|
||||||
|
rightButtons.push(
|
||||||
|
<AccessibleButton className="mx_RoomHeader_cancelButton" onClick={this._onCancelClick} key="_cancelButton">
|
||||||
|
<img src="img/cancel.svg" className="mx_filterFlipColor"
|
||||||
|
width="18" height="18" alt={_t("Cancel")} />
|
||||||
|
</AccessibleButton>,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
if (summary.user && summary.user.membership === 'join') {
|
||||||
rightButtons.push(
|
rightButtons.push(
|
||||||
<AccessibleButton className="mx_GroupHeader_button"
|
<AccessibleButton className="mx_GroupHeader_button"
|
||||||
onClick={this._onEditClick} title={_t("Community Settings")} key="_editButton"
|
onClick={this._onEditClick} title={_t("Community Settings")} key="_editButton"
|
||||||
|
@ -940,6 +971,7 @@ export default React.createClass({
|
||||||
<TintableSvg src="img/icons-settings-room.svg" width="16" height="16" />
|
<TintableSvg src="img/icons-settings-room.svg" width="16" height="16" />
|
||||||
</AccessibleButton>,
|
</AccessibleButton>,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
if (this.props.collapsedRhs) {
|
if (this.props.collapsedRhs) {
|
||||||
rightButtons.push(
|
rightButtons.push(
|
||||||
<AccessibleButton className="mx_GroupHeader_button"
|
<AccessibleButton className="mx_GroupHeader_button"
|
||||||
|
@ -949,10 +981,13 @@ export default React.createClass({
|
||||||
</AccessibleButton>,
|
</AccessibleButton>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
headerClasses.mx_GroupView_header_view = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const headerClasses = {
|
||||||
|
mx_GroupView_header: true,
|
||||||
|
mx_GroupView_header_view: !this.state.editing,
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_GroupView">
|
<div className="mx_GroupView">
|
||||||
<div className={classnames(headerClasses)}>
|
<div className={classnames(headerClasses)}>
|
||||||
|
|
|
@ -240,11 +240,13 @@ export default React.createClass({
|
||||||
oobData={this.props.roomOobData}
|
oobData={this.props.roomOobData}
|
||||||
eventPixelOffset={this.props.initialEventPixelOffset}
|
eventPixelOffset={this.props.initialEventPixelOffset}
|
||||||
key={this.props.currentRoomId || 'roomview'}
|
key={this.props.currentRoomId || 'roomview'}
|
||||||
opacity={this.props.middleOpacity}
|
disabled={this.props.middleDisabled}
|
||||||
collapsedRhs={this.props.collapseRhs}
|
collapsedRhs={this.props.collapseRhs}
|
||||||
ConferenceHandler={this.props.ConferenceHandler}
|
ConferenceHandler={this.props.ConferenceHandler}
|
||||||
/>;
|
/>;
|
||||||
if (!this.props.collapseRhs) right_panel = <RightPanel roomId={this.props.currentRoomId} opacity={this.props.rightOpacity} />;
|
if (!this.props.collapseRhs) {
|
||||||
|
right_panel = <RightPanel roomId={this.props.currentRoomId} disabled={this.props.rightDisabled} />;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PageTypes.UserSettings:
|
case PageTypes.UserSettings:
|
||||||
|
@ -254,7 +256,7 @@ export default React.createClass({
|
||||||
referralBaseUrl={this.props.config.referralBaseUrl}
|
referralBaseUrl={this.props.config.referralBaseUrl}
|
||||||
teamToken={this.props.teamToken}
|
teamToken={this.props.teamToken}
|
||||||
/>;
|
/>;
|
||||||
if (!this.props.collapseRhs) right_panel = <RightPanel opacity={this.props.rightOpacity} />;
|
if (!this.props.collapseRhs) right_panel = <RightPanel disabled={this.props.rightDisabled} />;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PageTypes.MyGroups:
|
case PageTypes.MyGroups:
|
||||||
|
@ -266,7 +268,7 @@ export default React.createClass({
|
||||||
onRoomCreated={this.props.onRoomCreated}
|
onRoomCreated={this.props.onRoomCreated}
|
||||||
collapsedRhs={this.props.collapseRhs}
|
collapsedRhs={this.props.collapseRhs}
|
||||||
/>;
|
/>;
|
||||||
if (!this.props.collapseRhs) right_panel = <RightPanel opacity={this.props.rightOpacity} />;
|
if (!this.props.collapseRhs) right_panel = <RightPanel disabled={this.props.rightDisabled} />;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PageTypes.RoomDirectory:
|
case PageTypes.RoomDirectory:
|
||||||
|
@ -294,14 +296,14 @@ export default React.createClass({
|
||||||
|
|
||||||
case PageTypes.UserView:
|
case PageTypes.UserView:
|
||||||
page_element = null; // deliberately null for now
|
page_element = null; // deliberately null for now
|
||||||
right_panel = <RightPanel opacity={this.props.rightOpacity} />;
|
right_panel = <RightPanel disabled={this.props.rightDisabled} />;
|
||||||
break;
|
break;
|
||||||
case PageTypes.GroupView:
|
case PageTypes.GroupView:
|
||||||
page_element = <GroupView
|
page_element = <GroupView
|
||||||
groupId={this.props.currentGroupId}
|
groupId={this.props.currentGroupId}
|
||||||
collapsedRhs={this.props.collapseRhs}
|
collapsedRhs={this.props.collapseRhs}
|
||||||
/>;
|
/>;
|
||||||
if (!this.props.collapseRhs) right_panel = <RightPanel groupId={this.props.currentGroupId} opacity={this.props.rightOpacity} />;
|
if (!this.props.collapseRhs) right_panel = <RightPanel groupId={this.props.currentGroupId} disabled={this.props.rightDisabled} />;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,7 +336,7 @@ export default React.createClass({
|
||||||
<LeftPanel
|
<LeftPanel
|
||||||
selectedRoom={this.props.currentRoomId}
|
selectedRoom={this.props.currentRoomId}
|
||||||
collapsed={this.props.collapseLhs || false}
|
collapsed={this.props.collapseLhs || false}
|
||||||
opacity={this.props.leftOpacity}
|
disabled={this.props.leftDisabled}
|
||||||
/>
|
/>
|
||||||
<main className='mx_MatrixChat_middlePanel'>
|
<main className='mx_MatrixChat_middlePanel'>
|
||||||
{ page_element }
|
{ page_element }
|
||||||
|
|
|
@ -145,9 +145,9 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
collapseLhs: false,
|
collapseLhs: false,
|
||||||
collapseRhs: false,
|
collapseRhs: false,
|
||||||
leftOpacity: 1.0,
|
leftDisabled: false,
|
||||||
middleOpacity: 1.0,
|
middleDisabled: false,
|
||||||
rightOpacity: 1.0,
|
rightDisabled: false,
|
||||||
|
|
||||||
version: null,
|
version: null,
|
||||||
newVersion: null,
|
newVersion: null,
|
||||||
|
@ -537,12 +537,11 @@ module.exports = React.createClass({
|
||||||
collapseRhs: false,
|
collapseRhs: false,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'ui_opacity': {
|
case 'panel_disable': {
|
||||||
const sideDefault = payload.sideOpacity >= 0.0 ? payload.sideOpacity : 1.0;
|
|
||||||
this.setState({
|
this.setState({
|
||||||
leftOpacity: payload.leftOpacity >= 0.0 ? payload.leftOpacity : sideDefault,
|
leftDisabled: payload.leftDisabled || payload.sideDisabled || false,
|
||||||
middleOpacity: payload.middleOpacity || 1.0,
|
middleDisabled: payload.middleDisabled || false,
|
||||||
rightOpacity: payload.rightOpacity >= 0.0 ? payload.rightOpacity : sideDefault,
|
rightDisabled: payload.rightDisabled || payload.sideDisabled || false,
|
||||||
});
|
});
|
||||||
break; }
|
break; }
|
||||||
case 'set_theme':
|
case 'set_theme':
|
||||||
|
|
|
@ -16,6 +16,7 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
|
import classNames from 'classnames';
|
||||||
import UserSettingsStore from '../../UserSettingsStore';
|
import UserSettingsStore from '../../UserSettingsStore';
|
||||||
import shouldHideEvent from '../../shouldHideEvent';
|
import shouldHideEvent from '../../shouldHideEvent';
|
||||||
import dis from "../../dispatcher";
|
import dis from "../../dispatcher";
|
||||||
|
@ -78,9 +79,6 @@ module.exports = React.createClass({
|
||||||
// callback which is called when more content is needed.
|
// callback which is called when more content is needed.
|
||||||
onFillRequest: React.PropTypes.func,
|
onFillRequest: React.PropTypes.func,
|
||||||
|
|
||||||
// opacity for dynamic UI fading effects
|
|
||||||
opacity: React.PropTypes.number,
|
|
||||||
|
|
||||||
// className for the panel
|
// className for the panel
|
||||||
className: React.PropTypes.string.isRequired,
|
className: React.PropTypes.string.isRequired,
|
||||||
|
|
||||||
|
@ -353,7 +351,7 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isMembershipChange(collapsedMxEv) ||
|
if (!isMembershipChange(collapsedMxEv) ||
|
||||||
this._wantsDateSeparator(this.props.events[i], collapsedMxEv.getDate())) {
|
this._wantsDateSeparator(mxEv, collapsedMxEv.getDate())) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -376,9 +374,7 @@ module.exports = React.createClass({
|
||||||
// of MemberEventListSummary, render each member event as if the previous
|
// of MemberEventListSummary, render each member event as if the previous
|
||||||
// one was itself. This way, the timestamp of the previous event === the
|
// one was itself. This way, the timestamp of the previous event === the
|
||||||
// timestamp of the current event, and no DateSeperator is inserted.
|
// timestamp of the current event, and no DateSeperator is inserted.
|
||||||
const ret = this._getTilesForEvent(e, e, e === lastShownEvent);
|
return this._getTilesForEvent(e, e, e === lastShownEvent);
|
||||||
prevEvent = e;
|
|
||||||
return ret;
|
|
||||||
}).reduce((a, b) => a.concat(b));
|
}).reduce((a, b) => a.concat(b));
|
||||||
|
|
||||||
if (eventTiles.length === 0) {
|
if (eventTiles.length === 0) {
|
||||||
|
@ -397,6 +393,7 @@ module.exports = React.createClass({
|
||||||
ret.push(this._getReadMarkerTile(visible));
|
ret.push(this._getReadMarkerTile(visible));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prevEvent = mxEv;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -649,12 +646,13 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
const style = this.props.hidden ? { display: 'none' } : {};
|
const style = this.props.hidden ? { display: 'none' } : {};
|
||||||
style.opacity = this.props.opacity;
|
|
||||||
|
|
||||||
let className = this.props.className + " mx_fadable";
|
const className = classNames(
|
||||||
if (this.props.alwaysShowTimestamps) {
|
this.props.className,
|
||||||
className += " mx_MessagePanel_alwaysShowTimestamps";
|
{
|
||||||
}
|
"mx_MessagePanel_alwaysShowTimestamps": this.props.alwaysShowTimestamps,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollPanel ref="scrollPanel" className={className}
|
<ScrollPanel ref="scrollPanel" className={className}
|
||||||
|
|
|
@ -62,7 +62,9 @@ const GroupTile = React.createClass({
|
||||||
const profile = this.state.profile || {};
|
const profile = this.state.profile || {};
|
||||||
const name = profile.name || this.props.groupId;
|
const name = profile.name || this.props.groupId;
|
||||||
const desc = profile.shortDescription;
|
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}>
|
return <AccessibleButton className="mx_GroupTile" onClick={this.onClick}>
|
||||||
<div className="mx_GroupTile_avatar">
|
<div className="mx_GroupTile_avatar">
|
||||||
<BaseAvatar name={name} url={httpUrl} width={50} height={50} />
|
<BaseAvatar name={name} url={httpUrl} width={50} height={50} />
|
||||||
|
|
|
@ -45,7 +45,6 @@ const NotificationPanel = React.createClass({
|
||||||
manageReadMarkers={false}
|
manageReadMarkers={false}
|
||||||
timelineSet={timelineSet}
|
timelineSet={timelineSet}
|
||||||
showUrlPreview = {false}
|
showUrlPreview = {false}
|
||||||
opacity={this.props.opacity}
|
|
||||||
tileShape="notif"
|
tileShape="notif"
|
||||||
empty={_t('You have no visible notifications')}
|
empty={_t('You have no visible notifications')}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1697,7 +1697,7 @@ module.exports = React.createClass({
|
||||||
onResize={this.onChildResize}
|
onResize={this.onChildResize}
|
||||||
uploadFile={this.uploadFile}
|
uploadFile={this.uploadFile}
|
||||||
callState={this.state.callState}
|
callState={this.state.callState}
|
||||||
opacity={this.props.opacity}
|
disabled={this.props.disabled}
|
||||||
showApps={this.state.showApps}
|
showApps={this.state.showApps}
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
|
@ -1758,7 +1758,6 @@ module.exports = React.createClass({
|
||||||
className="mx_RoomView_messagePanel mx_RoomView_searchResultsPanel"
|
className="mx_RoomView_messagePanel mx_RoomView_searchResultsPanel"
|
||||||
onFillRequest={this.onSearchResultsFillRequest}
|
onFillRequest={this.onSearchResultsFillRequest}
|
||||||
onResize={this.onSearchResultsResize}
|
onResize={this.onSearchResultsResize}
|
||||||
style={{ opacity: this.props.opacity }}
|
|
||||||
>
|
>
|
||||||
<li className={scrollheader_classes}></li>
|
<li className={scrollheader_classes}></li>
|
||||||
{ this.getSearchResultTiles() }
|
{ this.getSearchResultTiles() }
|
||||||
|
@ -1789,7 +1788,6 @@ module.exports = React.createClass({
|
||||||
onScroll={this.onMessageListScroll}
|
onScroll={this.onMessageListScroll}
|
||||||
onReadMarkerUpdated={this._updateTopUnreadMessagesBar}
|
onReadMarkerUpdated={this._updateTopUnreadMessagesBar}
|
||||||
showUrlPreview = {this.state.showUrlPreview}
|
showUrlPreview = {this.state.showUrlPreview}
|
||||||
opacity={this.props.opacity}
|
|
||||||
className="mx_RoomView_messagePanel"
|
className="mx_RoomView_messagePanel"
|
||||||
/>);
|
/>);
|
||||||
|
|
||||||
|
@ -1797,7 +1795,7 @@ module.exports = React.createClass({
|
||||||
if (this.state.showTopUnreadMessagesBar) {
|
if (this.state.showTopUnreadMessagesBar) {
|
||||||
const TopUnreadMessagesBar = sdk.getComponent('rooms.TopUnreadMessagesBar');
|
const TopUnreadMessagesBar = sdk.getComponent('rooms.TopUnreadMessagesBar');
|
||||||
topUnreadMessagesBar = (
|
topUnreadMessagesBar = (
|
||||||
<div className="mx_RoomView_topUnreadMessagesBar mx_fadable" style={{ opacity: this.props.opacity }}>
|
<div className="mx_RoomView_topUnreadMessagesBar">
|
||||||
<TopUnreadMessagesBar
|
<TopUnreadMessagesBar
|
||||||
onScrollUpClick={this.jumpToReadMarker}
|
onScrollUpClick={this.jumpToReadMarker}
|
||||||
onCloseClick={this.forgetReadMarker}
|
onCloseClick={this.forgetReadMarker}
|
||||||
|
@ -1805,10 +1803,19 @@ module.exports = React.createClass({
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let statusBarAreaClass = "mx_RoomView_statusArea mx_fadable";
|
const statusBarAreaClass = classNames(
|
||||||
if (isStatusAreaExpanded) {
|
"mx_RoomView_statusArea",
|
||||||
statusBarAreaClass += " mx_RoomView_statusArea_expanded";
|
{
|
||||||
}
|
"mx_RoomView_statusArea_expanded": isStatusAreaExpanded,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const fadableSectionClasses = classNames(
|
||||||
|
"mx_RoomView_body", "mx_fadable",
|
||||||
|
{
|
||||||
|
"mx_fadable_faded": this.props.disabled,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={"mx_RoomView" + (inCall ? " mx_RoomView_inCall" : "")} ref="roomView">
|
<div className={"mx_RoomView" + (inCall ? " mx_RoomView_inCall" : "")} ref="roomView">
|
||||||
|
@ -1827,10 +1834,11 @@ module.exports = React.createClass({
|
||||||
onLeaveClick={(myMember && myMember.membership === "join") ? this.onLeaveClick : null}
|
onLeaveClick={(myMember && myMember.membership === "join") ? this.onLeaveClick : null}
|
||||||
/>
|
/>
|
||||||
{ auxPanel }
|
{ auxPanel }
|
||||||
|
<div className={fadableSectionClasses}>
|
||||||
{ topUnreadMessagesBar }
|
{ topUnreadMessagesBar }
|
||||||
{ messagePanel }
|
{ messagePanel }
|
||||||
{ searchResultsPanel }
|
{ searchResultsPanel }
|
||||||
<div className={statusBarAreaClass} style={{opacity: this.props.opacity}}>
|
<div className={statusBarAreaClass}>
|
||||||
<div className="mx_RoomView_statusAreaBox">
|
<div className="mx_RoomView_statusAreaBox">
|
||||||
<div className="mx_RoomView_statusAreaBox_line"></div>
|
<div className="mx_RoomView_statusAreaBox_line"></div>
|
||||||
{ statusBar }
|
{ statusBar }
|
||||||
|
@ -1838,6 +1846,7 @@ module.exports = React.createClass({
|
||||||
</div>
|
</div>
|
||||||
{ messageComposer }
|
{ messageComposer }
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -89,9 +89,6 @@ var TimelinePanel = React.createClass({
|
||||||
// callback which is called when the read-up-to mark is updated.
|
// callback which is called when the read-up-to mark is updated.
|
||||||
onReadMarkerUpdated: React.PropTypes.func,
|
onReadMarkerUpdated: React.PropTypes.func,
|
||||||
|
|
||||||
// opacity for dynamic UI fading effects
|
|
||||||
opacity: React.PropTypes.number,
|
|
||||||
|
|
||||||
// maximum number of events to show in a timeline
|
// maximum number of events to show in a timeline
|
||||||
timelineCap: React.PropTypes.number,
|
timelineCap: React.PropTypes.number,
|
||||||
|
|
||||||
|
@ -1157,7 +1154,6 @@ var TimelinePanel = React.createClass({
|
||||||
onScroll={this.onMessageListScroll}
|
onScroll={this.onMessageListScroll}
|
||||||
onFillRequest={this.onMessageListFillRequest}
|
onFillRequest={this.onMessageListFillRequest}
|
||||||
onUnfillRequest={this.onMessageListUnfillRequest}
|
onUnfillRequest={this.onMessageListUnfillRequest}
|
||||||
opacity={this.props.opacity}
|
|
||||||
isTwelveHour={this.state.isTwelveHour}
|
isTwelveHour={this.state.isTwelveHour}
|
||||||
alwaysShowTimestamps={this.state.alwaysShowTimestamps}
|
alwaysShowTimestamps={this.state.alwaysShowTimestamps}
|
||||||
className={this.props.className}
|
className={this.props.className}
|
||||||
|
|
|
@ -118,6 +118,10 @@ const SETTINGS_LABELS = [
|
||||||
id: 'TextualBody.disableBigEmoji',
|
id: 'TextualBody.disableBigEmoji',
|
||||||
label: _td('Disable big emoji in chat'),
|
label: _td('Disable big emoji in chat'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'VideoView.flipVideoHorizontally',
|
||||||
|
label: _td('Mirror local video feed'),
|
||||||
|
},
|
||||||
/*
|
/*
|
||||||
{
|
{
|
||||||
id: 'useFixedWidthFont',
|
id: 'useFixedWidthFont',
|
||||||
|
@ -276,9 +280,9 @@ module.exports = React.createClass({
|
||||||
MatrixClientPeg.get().on("RoomMember.membership", this._onInviteStateChange);
|
MatrixClientPeg.get().on("RoomMember.membership", this._onInviteStateChange);
|
||||||
|
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'ui_opacity',
|
action: 'panel_disable',
|
||||||
sideOpacity: 0.3,
|
sideDisabled: true,
|
||||||
middleOpacity: 0.3,
|
middleDisabled: true,
|
||||||
});
|
});
|
||||||
this._refreshFromServer();
|
this._refreshFromServer();
|
||||||
|
|
||||||
|
@ -316,9 +320,9 @@ module.exports = React.createClass({
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount: function() {
|
||||||
this._unmounted = true;
|
this._unmounted = true;
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'ui_opacity',
|
action: 'panel_disable',
|
||||||
sideOpacity: 1.0,
|
sideDisabled: false,
|
||||||
middleOpacity: 1.0,
|
middleDisabled: false,
|
||||||
});
|
});
|
||||||
dis.unregister(this.dispatcherRef);
|
dis.unregister(this.dispatcherRef);
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
|
@ -1333,8 +1337,11 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
<div className="mx_UserSettings_avatarPicker">
|
<div className="mx_UserSettings_avatarPicker">
|
||||||
<div className="mx_UserSettings_avatarPicker_remove" onClick={this.onAvatarRemoveClick}>
|
<div className="mx_UserSettings_avatarPicker_remove" onClick={this.onAvatarRemoveClick}>
|
||||||
<img src="img/cancel.svg" width="15" height="15"
|
<img src="img/cancel.svg"
|
||||||
alt={_t("Remove avatar")} title={_t("Remove avatar")} />
|
width="15" height="15"
|
||||||
|
className="mx_filterFlipColor"
|
||||||
|
alt={_t("Remove avatar")}
|
||||||
|
title={_t("Remove avatar")} />
|
||||||
</div>
|
</div>
|
||||||
<div onClick={this.onAvatarPickerClick} className="mx_UserSettings_avatarPicker_imgContainer">
|
<div onClick={this.onAvatarPickerClick} className="mx_UserSettings_avatarPicker_imgContainer">
|
||||||
<ChangeAvatar ref="changeAvatar" initialAvatarUrl={avatarUrl}
|
<ChangeAvatar ref="changeAvatar" initialAvatarUrl={avatarUrl}
|
||||||
|
|
|
@ -303,7 +303,7 @@ module.exports = React.createClass({
|
||||||
} : {};
|
} : {};
|
||||||
|
|
||||||
return this._matrixClient.register(
|
return this._matrixClient.register(
|
||||||
this.state.formVals.username,
|
this.state.formVals.username.toLowerCase(),
|
||||||
this.state.formVals.password,
|
this.state.formVals.password,
|
||||||
undefined, // session id: included in the auth dict already
|
undefined, // session id: included in the auth dict already
|
||||||
auth,
|
auth,
|
||||||
|
|
|
@ -28,6 +28,7 @@ export default React.createClass({
|
||||||
width: PropTypes.number,
|
width: PropTypes.number,
|
||||||
height: PropTypes.number,
|
height: PropTypes.number,
|
||||||
resizeMethod: PropTypes.string,
|
resizeMethod: PropTypes.string,
|
||||||
|
onClick: PropTypes.func,
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
|
|
|
@ -272,20 +272,27 @@ module.exports = React.createClass({
|
||||||
const topicEvent = room.currentState.getStateEvents('m.room.topic', '');
|
const topicEvent = room.currentState.getStateEvents('m.room.topic', '');
|
||||||
const name = nameEvent ? nameEvent.getContent().name : '';
|
const name = nameEvent ? nameEvent.getContent().name : '';
|
||||||
const canonicalAlias = room.getCanonicalAlias();
|
const canonicalAlias = room.getCanonicalAlias();
|
||||||
|
const aliasEvents = room.currentState.getStateEvents('m.room.aliases');
|
||||||
|
const aliases = aliasEvents.map((ev) => ev.getContent().aliases).reduce((a, b) => {
|
||||||
|
return a.concat(b);
|
||||||
|
}, []);
|
||||||
const topic = topicEvent ? topicEvent.getContent().topic : '';
|
const topic = topicEvent ? topicEvent.getContent().topic : '';
|
||||||
|
|
||||||
const nameMatch = (name || '').toLowerCase().includes(lowerCaseQuery);
|
const nameMatch = (name || '').toLowerCase().includes(lowerCaseQuery);
|
||||||
const aliasMatch = (canonicalAlias || '').toLowerCase().includes(lowerCaseQuery);
|
const aliasMatch = aliases.some((alias) =>
|
||||||
|
(alias || '').toLowerCase().includes(lowerCaseQuery),
|
||||||
|
);
|
||||||
const topicMatch = (topic || '').toLowerCase().includes(lowerCaseQuery);
|
const topicMatch = (topic || '').toLowerCase().includes(lowerCaseQuery);
|
||||||
if (!(nameMatch || topicMatch || aliasMatch)) {
|
if (!(nameMatch || topicMatch || aliasMatch)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const avatarEvent = room.currentState.getStateEvents('m.room.avatar', '');
|
const avatarEvent = room.currentState.getStateEvents('m.room.avatar', '');
|
||||||
const avatarUrl = avatarEvent ? avatarEvent.getContent().url : undefined;
|
const avatarUrl = avatarEvent ? avatarEvent.getContent().url : undefined;
|
||||||
|
|
||||||
results.push({
|
results.push({
|
||||||
room_id: room.roomId,
|
room_id: room.roomId,
|
||||||
avatar_url: avatarUrl,
|
avatar_url: avatarUrl,
|
||||||
name: name || canonicalAlias,
|
name: name || canonicalAlias || aliases[0] || _t('Unnamed Room'),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this._processResults(results, query);
|
this._processResults(results, query);
|
||||||
|
|
|
@ -108,7 +108,7 @@ export default React.createClass({
|
||||||
// rather than displaying what the server gives us, but synapse doesn't give
|
// rather than displaying what the server gives us, but synapse doesn't give
|
||||||
// any yet.
|
// any yet.
|
||||||
createErrorNode = <div className="error">
|
createErrorNode = <div className="error">
|
||||||
<div>{ _t('Room creation failed') }</div>
|
<div>{ _t('Something went wrong whilst creating your community') }</div>
|
||||||
<div>{ this.state.createError.message }</div>
|
<div>{ this.state.createError.message }</div>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,19 +172,30 @@ export default React.createClass({
|
||||||
*/
|
*/
|
||||||
_onDeleteClick: function() {
|
_onDeleteClick: function() {
|
||||||
if (this._canUserModify()) {
|
if (this._canUserModify()) {
|
||||||
console.log("Delete widget %s", this.props.id);
|
// Show delete confirmation dialog
|
||||||
|
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||||
|
Modal.createTrackedDialog('Delete Widget', '', QuestionDialog, {
|
||||||
|
title: _t("Delete Widget"),
|
||||||
|
description: _t(
|
||||||
|
"Deleting a widget removes it for all users in this room." +
|
||||||
|
" Are you sure you want to delete this widget?"),
|
||||||
|
button: _t("Delete widget"),
|
||||||
|
onFinished: (confirmed) => {
|
||||||
|
if (!confirmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.setState({deleting: true});
|
this.setState({deleting: true});
|
||||||
MatrixClientPeg.get().sendStateEvent(
|
MatrixClientPeg.get().sendStateEvent(
|
||||||
this.props.room.roomId,
|
this.props.room.roomId,
|
||||||
'im.vector.modular.widgets',
|
'im.vector.modular.widgets',
|
||||||
{}, // empty content
|
{}, // empty content
|
||||||
this.props.id,
|
this.props.id,
|
||||||
).then(() => {
|
).catch((e) => {
|
||||||
console.log('Deleted widget');
|
|
||||||
}, (e) => {
|
|
||||||
console.error('Failed to delete widget', e);
|
console.error('Failed to delete widget', e);
|
||||||
this.setState({deleting: false});
|
this.setState({deleting: false});
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
console.log("Revoke widget permissions - %s", this.props.id);
|
console.log("Revoke widget permissions - %s", this.props.id);
|
||||||
this._revokeWidgetPermission();
|
this._revokeWidgetPermission();
|
||||||
|
@ -305,7 +316,7 @@ export default React.createClass({
|
||||||
let deleteIcon = 'img/cancel.svg';
|
let deleteIcon = 'img/cancel.svg';
|
||||||
let deleteClasses = 'mx_filterFlipColor mx_AppTileMenuBarWidget';
|
let deleteClasses = 'mx_filterFlipColor mx_AppTileMenuBarWidget';
|
||||||
if(this._canUserModify()) {
|
if(this._canUserModify()) {
|
||||||
deleteIcon = 'img/cancel-red.svg';
|
deleteIcon = 'img/icon-delete-pink.svg';
|
||||||
deleteClasses += ' mx_AppTileMenuBarWidgetDelete';
|
deleteClasses += ' mx_AppTileMenuBarWidgetDelete';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,9 @@ module.exports = React.createClass({
|
||||||
onNewItemChanged: PropTypes.func,
|
onNewItemChanged: PropTypes.func,
|
||||||
onItemAdded: PropTypes.func,
|
onItemAdded: PropTypes.func,
|
||||||
onItemEdited: PropTypes.func,
|
onItemEdited: PropTypes.func,
|
||||||
onItemRemoved: PropTypes. func,
|
onItemRemoved: PropTypes.func,
|
||||||
|
|
||||||
|
canEdit: PropTypes.bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
|
@ -136,6 +138,7 @@ module.exports = React.createClass({
|
||||||
{ label }
|
{ label }
|
||||||
</div>
|
</div>
|
||||||
{ editableItems }
|
{ editableItems }
|
||||||
|
{ this.props.canEdit ?
|
||||||
<EditableItem
|
<EditableItem
|
||||||
key={-1}
|
key={-1}
|
||||||
initialValue={this.props.newItem}
|
initialValue={this.props.newItem}
|
||||||
|
@ -143,7 +146,8 @@ module.exports = React.createClass({
|
||||||
onChange={this.onNewItemChanged}
|
onChange={this.onNewItemChanged}
|
||||||
addOnChange={true}
|
addOnChange={true}
|
||||||
placeholder={this.props.placeholder}
|
placeholder={this.props.placeholder}
|
||||||
/>
|
/> : <div />
|
||||||
|
}
|
||||||
</div>);
|
</div>);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -127,7 +127,7 @@ export default class Flair extends React.Component {
|
||||||
}
|
}
|
||||||
const profiles = await this._getGroupProfiles(groups);
|
const profiles = await this._getGroupProfiles(groups);
|
||||||
if (!this.unmounted) {
|
if (!this.unmounted) {
|
||||||
this.setState({profiles});
|
this.setState({profiles: profiles.filter((profile) => {return profile.avatarUrl;})});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -173,7 +173,7 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
<div className="mx_MemberInfo">
|
<div className="mx_MemberInfo">
|
||||||
<GeminiScrollbar autoshow={true}>
|
<GeminiScrollbar autoshow={true}>
|
||||||
<AccessibleButton className="mx_MemberInfo_cancel"onClick={this._onCancel}>
|
<AccessibleButton className="mx_MemberInfo_cancel"onClick={this._onCancel}>
|
||||||
<img src="img/cancel.svg" width="18" height="18" />
|
<img src="img/cancel.svg" width="18" height="18" className="mx_filterFlipColor" />
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
<div className="mx_MemberInfo_avatar">
|
<div className="mx_MemberInfo_avatar">
|
||||||
{ avatar }
|
{ avatar }
|
||||||
|
|
|
@ -17,7 +17,7 @@ limitations under the License.
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import { groupMemberFromApiObject } from '../../../groups';
|
import GroupStoreCache from '../../../stores/GroupStoreCache';
|
||||||
import GeminiScrollbar from 'react-gemini-scrollbar';
|
import GeminiScrollbar from 'react-gemini-scrollbar';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import withMatrixClient from '../../../wrappers/withMatrixClient';
|
import withMatrixClient from '../../../wrappers/withMatrixClient';
|
||||||
|
@ -27,15 +27,16 @@ const INITIAL_LOAD_NUM_MEMBERS = 30;
|
||||||
export default withMatrixClient(React.createClass({
|
export default withMatrixClient(React.createClass({
|
||||||
displayName: 'GroupMemberList',
|
displayName: 'GroupMemberList',
|
||||||
|
|
||||||
propTypes: {
|
contextTypes: {
|
||||||
matrixClient: PropTypes.object.isRequired,
|
matrixClient: PropTypes.object.isRequired,
|
||||||
|
},
|
||||||
|
|
||||||
|
propTypes: {
|
||||||
groupId: PropTypes.string.isRequired,
|
groupId: PropTypes.string.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
return {
|
return {
|
||||||
fetching: false,
|
|
||||||
fetchingInvitedMembers: false,
|
|
||||||
members: null,
|
members: null,
|
||||||
invitedMembers: null,
|
invitedMembers: null,
|
||||||
truncateAt: INITIAL_LOAD_NUM_MEMBERS,
|
truncateAt: INITIAL_LOAD_NUM_MEMBERS,
|
||||||
|
@ -44,36 +45,21 @@ export default withMatrixClient(React.createClass({
|
||||||
|
|
||||||
componentWillMount: function() {
|
componentWillMount: function() {
|
||||||
this._unmounted = false;
|
this._unmounted = false;
|
||||||
|
this._initGroupStore(this.props.groupId);
|
||||||
|
},
|
||||||
|
|
||||||
|
_initGroupStore: function(groupId) {
|
||||||
|
this._groupStore = GroupStoreCache.getGroupStore(this.context.matrixClient, groupId);
|
||||||
|
this._groupStore.registerListener(() => {
|
||||||
this._fetchMembers();
|
this._fetchMembers();
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
_fetchMembers: function() {
|
_fetchMembers: function() {
|
||||||
|
if (this._unmounted) return;
|
||||||
this.setState({
|
this.setState({
|
||||||
fetching: true,
|
members: this._groupStore.getGroupMembers(),
|
||||||
fetchingInvitedMembers: true,
|
invitedMembers: this._groupStore.getGroupInvitedMembers(),
|
||||||
});
|
|
||||||
this.props.matrixClient.getGroupUsers(this.props.groupId).then((result) => {
|
|
||||||
this.setState({
|
|
||||||
members: result.chunk.map((apiMember) => {
|
|
||||||
return groupMemberFromApiObject(apiMember);
|
|
||||||
}),
|
|
||||||
fetching: false,
|
|
||||||
});
|
|
||||||
}).catch((e) => {
|
|
||||||
this.setState({fetching: false});
|
|
||||||
console.error("Failed to get group member list: " + e);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.props.matrixClient.getGroupInvitedUsers(this.props.groupId).then((result) => {
|
|
||||||
this.setState({
|
|
||||||
invitedMembers: result.chunk.map((apiMember) => {
|
|
||||||
return groupMemberFromApiObject(apiMember);
|
|
||||||
}),
|
|
||||||
fetchingInvitedMembers: false,
|
|
||||||
});
|
|
||||||
}).catch((e) => {
|
|
||||||
this.setState({fetchingInvitedMembers: false});
|
|
||||||
console.error("Failed to get group invited member list: " + e);
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -117,12 +103,11 @@ export default withMatrixClient(React.createClass({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
memberList = memberList.map((m) => {
|
const uniqueMembers = {};
|
||||||
return (
|
memberList.forEach((m) => {
|
||||||
<GroupMemberTile key={m.userId} groupId={this.props.groupId} member={m} />
|
if (!uniqueMembers[m.userId]) uniqueMembers[m.userId] = m;
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
memberList = Object.keys(uniqueMembers).map((userId) => uniqueMembers[userId]);
|
||||||
memberList.sort((a, b) => {
|
memberList.sort((a, b) => {
|
||||||
// TODO: should put admins at the top: we don't yet have that info
|
// TODO: should put admins at the top: we don't yet have that info
|
||||||
if (a < b) {
|
if (a < b) {
|
||||||
|
@ -134,10 +119,16 @@ export default withMatrixClient(React.createClass({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const memberTiles = memberList.map((m) => {
|
||||||
|
return (
|
||||||
|
<GroupMemberTile key={m.userId} groupId={this.props.groupId} member={m} />
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
return <TruncatedList className="mx_MemberList_wrapper" truncateAt={this.state.truncateAt}
|
return <TruncatedList className="mx_MemberList_wrapper" truncateAt={this.state.truncateAt}
|
||||||
createOverflowElement={this._createOverflowTile}
|
createOverflowElement={this._createOverflowTile}
|
||||||
>
|
>
|
||||||
{ memberList }
|
{ memberTiles }
|
||||||
</TruncatedList>;
|
</TruncatedList>;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,6 @@ limitations under the License.
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import { groupRoomFromApiObject } from '../../../groups';
|
|
||||||
import GroupStoreCache from '../../../stores/GroupStoreCache';
|
import GroupStoreCache from '../../../stores/GroupStoreCache';
|
||||||
import GeminiScrollbar from 'react-gemini-scrollbar';
|
import GeminiScrollbar from 'react-gemini-scrollbar';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
@ -48,24 +47,20 @@ export default React.createClass({
|
||||||
|
|
||||||
_initGroupStore: function(groupId) {
|
_initGroupStore: function(groupId) {
|
||||||
this._groupStore = GroupStoreCache.getGroupStore(this.context.matrixClient, groupId);
|
this._groupStore = GroupStoreCache.getGroupStore(this.context.matrixClient, groupId);
|
||||||
this._groupStore.on('update', () => {
|
this._groupStore.registerListener(() => {
|
||||||
this._fetchRooms();
|
this._fetchRooms();
|
||||||
});
|
});
|
||||||
this._groupStore.on('error', (err) => {
|
this._groupStore.on('error', (err) => {
|
||||||
console.error('Error in group store (listened to by GroupRoomList)', err);
|
|
||||||
this.setState({
|
this.setState({
|
||||||
rooms: null,
|
rooms: null,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this._fetchRooms();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_fetchRooms: function() {
|
_fetchRooms: function() {
|
||||||
if (this._unmounted) return;
|
if (this._unmounted) return;
|
||||||
this.setState({
|
this.setState({
|
||||||
rooms: this._groupStore.getGroupRooms().map((apiRoom) => {
|
rooms: this._groupStore.getGroupRooms(),
|
||||||
return groupRoomFromApiObject(apiRoom);
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -120,8 +120,11 @@ const GroupRoomTile = React.createClass({
|
||||||
<div className="mx_GroupRoomTile_name">
|
<div className="mx_GroupRoomTile_name">
|
||||||
{ this.state.name }
|
{ this.state.name }
|
||||||
</div>
|
</div>
|
||||||
<AccessibleButton className="mx_GroupRoomTile_delete" onClick={this.onDeleteClick}>
|
<AccessibleButton className="mx_GroupRoomTile_delete"
|
||||||
<img src="img/cancel-small.svg" />
|
onClick={this.onDeleteClick}
|
||||||
|
tooltip={_t("Remove this room from the community")}
|
||||||
|
>
|
||||||
|
<img src="img/cancel.svg" width="15" height="15" className="mx_filterFlipColor" />
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
|
|
|
@ -25,7 +25,10 @@ module.exports = React.createClass({
|
||||||
render: function() {
|
render: function() {
|
||||||
let tooltip = _t("Removed or unknown message type");
|
let tooltip = _t("Removed or unknown message type");
|
||||||
if (this.props.mxEvent.isRedacted()) {
|
if (this.props.mxEvent.isRedacted()) {
|
||||||
tooltip = _t("Message removed by %(userId)s", {userId: this.props.mxEvent.getSender()});
|
const redactedBecauseUserId = this.props.mxEvent.getUnsigned().redacted_because.sender;
|
||||||
|
tooltip = redactedBecauseUserId ?
|
||||||
|
_t("Message removed by %(userId)s", { userId: redactedBecauseUserId }) :
|
||||||
|
_t("Message removed");
|
||||||
}
|
}
|
||||||
|
|
||||||
const text = this.props.mxEvent.getContent().body;
|
const text = this.props.mxEvent.getContent().body;
|
||||||
|
|
|
@ -262,6 +262,7 @@ module.exports = React.createClass({
|
||||||
items={this.state.domainToAliases[localDomain] || []}
|
items={this.state.domainToAliases[localDomain] || []}
|
||||||
newItem={this.state.newAlias}
|
newItem={this.state.newAlias}
|
||||||
onNewItemChanged={this.onNewAliasChanged}
|
onNewItemChanged={this.onNewAliasChanged}
|
||||||
|
canEdit={this.props.canSetAliases}
|
||||||
onItemAdded={this.onLocalAliasAdded}
|
onItemAdded={this.onLocalAliasAdded}
|
||||||
onItemEdited={this.onLocalAliasChanged}
|
onItemEdited={this.onLocalAliasChanged}
|
||||||
onItemRemoved={this.onLocalAliasDeleted}
|
onItemRemoved={this.onLocalAliasDeleted}
|
||||||
|
|
|
@ -27,7 +27,7 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
roomId: React.PropTypes.string.isRequired,
|
roomId: React.PropTypes.string.isRequired,
|
||||||
canSetRelatedRooms: React.PropTypes.bool.isRequired,
|
canSetRelatedGroups: React.PropTypes.bool.isRequired,
|
||||||
relatedGroupsEvent: React.PropTypes.instanceOf(MatrixEvent),
|
relatedGroupsEvent: React.PropTypes.instanceOf(MatrixEvent),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
return {
|
return {
|
||||||
canSetRelatedRooms: false,
|
canSetRelatedGroups: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -110,6 +110,7 @@ module.exports = React.createClass({
|
||||||
items={this.state.newGroupsList}
|
items={this.state.newGroupsList}
|
||||||
className={"mx_RelatedGroupSettings"}
|
className={"mx_RelatedGroupSettings"}
|
||||||
newItem={this.state.newGroupId}
|
newItem={this.state.newGroupId}
|
||||||
|
canEdit={this.props.canSetRelatedGroups}
|
||||||
onNewItemChanged={this.onNewGroupChanged}
|
onNewItemChanged={this.onNewGroupChanged}
|
||||||
onItemAdded={this.onGroupAdded}
|
onItemAdded={this.onGroupAdded}
|
||||||
onItemEdited={this.onGroupEdited}
|
onItemEdited={this.onGroupEdited}
|
||||||
|
|
|
@ -30,10 +30,9 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
componentWillMount: function() {
|
componentWillMount: function() {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'ui_opacity',
|
action: 'panel_disable',
|
||||||
leftOpacity: 1.0,
|
rightDisabled: true,
|
||||||
rightOpacity: 0.3,
|
middleDisabled: true,
|
||||||
middleOpacity: 0.5,
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -43,9 +42,9 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount: function() {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'ui_opacity',
|
action: 'panel_disable',
|
||||||
sideOpacity: 1.0,
|
sideDisabled: false,
|
||||||
middleOpacity: 1.0,
|
middleDisabled: false,
|
||||||
});
|
});
|
||||||
document.removeEventListener('keydown', this._onKeyDown);
|
document.removeEventListener('keydown', this._onKeyDown);
|
||||||
},
|
},
|
||||||
|
|
|
@ -133,7 +133,8 @@ module.exports = React.createClass({
|
||||||
{ p["og:description"] }
|
{ p["og:description"] }
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<img className="mx_LinkPreviewWidget_cancel" src="img/cancel.svg" width="18" height="18"
|
<img className="mx_LinkPreviewWidget_cancel mx_filterFlipColor"
|
||||||
|
src="img/cancel.svg" width="18" height="18"
|
||||||
onClick={this.props.onCancelClick} />
|
onClick={this.props.onCancelClick} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -39,6 +39,7 @@ import { findReadReceiptFromUserId } from '../../../utils/Receipt';
|
||||||
import withMatrixClient from '../../../wrappers/withMatrixClient';
|
import withMatrixClient from '../../../wrappers/withMatrixClient';
|
||||||
import AccessibleButton from '../elements/AccessibleButton';
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
import GeminiScrollbar from 'react-gemini-scrollbar';
|
import GeminiScrollbar from 'react-gemini-scrollbar';
|
||||||
|
import RoomViewStore from '../../../stores/RoomViewStore';
|
||||||
|
|
||||||
|
|
||||||
module.exports = withMatrixClient(React.createClass({
|
module.exports = withMatrixClient(React.createClass({
|
||||||
|
@ -81,6 +82,7 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
cli.on("Room.receipt", this.onRoomReceipt);
|
cli.on("Room.receipt", this.onRoomReceipt);
|
||||||
cli.on("RoomState.events", this.onRoomStateEvents);
|
cli.on("RoomState.events", this.onRoomStateEvents);
|
||||||
cli.on("RoomMember.name", this.onRoomMemberName);
|
cli.on("RoomMember.name", this.onRoomMemberName);
|
||||||
|
cli.on("RoomMember.membership", this.onRoomMemberMembership);
|
||||||
cli.on("accountData", this.onAccountData);
|
cli.on("accountData", this.onAccountData);
|
||||||
|
|
||||||
this._checkIgnoreState();
|
this._checkIgnoreState();
|
||||||
|
@ -91,7 +93,7 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillReceiveProps: function(newProps) {
|
componentWillReceiveProps: function(newProps) {
|
||||||
if (this.props.member.userId != newProps.member.userId) {
|
if (this.props.member.userId !== newProps.member.userId) {
|
||||||
this._updateStateForNewMember(newProps.member);
|
this._updateStateForNewMember(newProps.member);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -107,6 +109,7 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
client.removeListener("Room.receipt", this.onRoomReceipt);
|
client.removeListener("Room.receipt", this.onRoomReceipt);
|
||||||
client.removeListener("RoomState.events", this.onRoomStateEvents);
|
client.removeListener("RoomState.events", this.onRoomStateEvents);
|
||||||
client.removeListener("RoomMember.name", this.onRoomMemberName);
|
client.removeListener("RoomMember.name", this.onRoomMemberName);
|
||||||
|
client.removeListener("RoomMember.membership", this.onRoomMemberMembership);
|
||||||
client.removeListener("accountData", this.onAccountData);
|
client.removeListener("accountData", this.onAccountData);
|
||||||
}
|
}
|
||||||
if (this._cancelDeviceList) {
|
if (this._cancelDeviceList) {
|
||||||
|
@ -122,12 +125,12 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
_disambiguateDevices: function(devices) {
|
_disambiguateDevices: function(devices) {
|
||||||
const names = Object.create(null);
|
const names = Object.create(null);
|
||||||
for (let i = 0; i < devices.length; i++) {
|
for (let i = 0; i < devices.length; i++) {
|
||||||
var name = devices[i].getDisplayName();
|
const name = devices[i].getDisplayName();
|
||||||
const indexList = names[name] || [];
|
const indexList = names[name] || [];
|
||||||
indexList.push(i);
|
indexList.push(i);
|
||||||
names[name] = indexList;
|
names[name] = indexList;
|
||||||
}
|
}
|
||||||
for (name in names) {
|
for (const name in names) {
|
||||||
if (names[name].length > 1) {
|
if (names[name].length > 1) {
|
||||||
names[name].forEach((j)=>{
|
names[name].forEach((j)=>{
|
||||||
devices[j].ambiguous = true;
|
devices[j].ambiguous = true;
|
||||||
|
@ -141,7 +144,7 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userId == this.props.member.userId) {
|
if (userId === this.props.member.userId) {
|
||||||
// no need to re-download the whole thing; just update our copy of
|
// no need to re-download the whole thing; just update our copy of
|
||||||
// the list.
|
// the list.
|
||||||
|
|
||||||
|
@ -186,8 +189,12 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onRoomMemberMembership: function(ev, member) {
|
||||||
|
if (this.props.member.userId === member.userId) this.forceUpdate();
|
||||||
|
},
|
||||||
|
|
||||||
onAccountData: function(ev) {
|
onAccountData: function(ev) {
|
||||||
if (ev.getType() == 'm.direct') {
|
if (ev.getType() === 'm.direct') {
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -242,7 +249,9 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
ignoredUsers.push(this.props.member.userId);
|
ignoredUsers.push(this.props.member.userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.matrixClient.setIgnoredUsers(ignoredUsers).then(() => this.setState({isIgnoring: !this.state.isIgnoring}));
|
this.props.matrixClient.setIgnoredUsers(ignoredUsers).then(() => {
|
||||||
|
return this.setState({isIgnoring: !this.state.isIgnoring});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
onKick: function() {
|
onKick: function() {
|
||||||
|
@ -252,7 +261,7 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
Modal.createTrackedDialog('Confirm User Action Dialog', 'onKick', ConfirmUserActionDialog, {
|
Modal.createTrackedDialog('Confirm User Action Dialog', 'onKick', ConfirmUserActionDialog, {
|
||||||
member: this.props.member,
|
member: this.props.member,
|
||||||
action: kickLabel,
|
action: kickLabel,
|
||||||
askReason: membership == "join",
|
askReason: membership === "join",
|
||||||
danger: true,
|
danger: true,
|
||||||
onFinished: (proceed, reason) => {
|
onFinished: (proceed, reason) => {
|
||||||
if (!proceed) return;
|
if (!proceed) return;
|
||||||
|
@ -284,15 +293,15 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
const ConfirmUserActionDialog = sdk.getComponent("dialogs.ConfirmUserActionDialog");
|
const ConfirmUserActionDialog = sdk.getComponent("dialogs.ConfirmUserActionDialog");
|
||||||
Modal.createTrackedDialog('Confirm User Action Dialog', 'onBanOrUnban', ConfirmUserActionDialog, {
|
Modal.createTrackedDialog('Confirm User Action Dialog', 'onBanOrUnban', ConfirmUserActionDialog, {
|
||||||
member: this.props.member,
|
member: this.props.member,
|
||||||
action: this.props.member.membership == 'ban' ? _t("Unban") : _t("Ban"),
|
action: this.props.member.membership === 'ban' ? _t("Unban") : _t("Ban"),
|
||||||
askReason: this.props.member.membership != 'ban',
|
askReason: this.props.member.membership !== 'ban',
|
||||||
danger: this.props.member.membership != 'ban',
|
danger: this.props.member.membership !== 'ban',
|
||||||
onFinished: (proceed, reason) => {
|
onFinished: (proceed, reason) => {
|
||||||
if (!proceed) return;
|
if (!proceed) return;
|
||||||
|
|
||||||
this.setState({ updating: this.state.updating + 1 });
|
this.setState({ updating: this.state.updating + 1 });
|
||||||
let promise;
|
let promise;
|
||||||
if (this.props.member.membership == 'ban') {
|
if (this.props.member.membership === 'ban') {
|
||||||
promise = this.props.matrixClient.unban(
|
promise = this.props.matrixClient.unban(
|
||||||
this.props.member.roomId, this.props.member.userId,
|
this.props.member.roomId, this.props.member.userId,
|
||||||
);
|
);
|
||||||
|
@ -327,15 +336,11 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
const roomId = this.props.member.roomId;
|
const roomId = this.props.member.roomId;
|
||||||
const target = this.props.member.userId;
|
const target = this.props.member.userId;
|
||||||
const room = this.props.matrixClient.getRoom(roomId);
|
const room = this.props.matrixClient.getRoom(roomId);
|
||||||
if (!room) {
|
if (!room) return;
|
||||||
return;
|
|
||||||
}
|
const powerLevelEvent = room.currentState.getStateEvents("m.room.power_levels", "");
|
||||||
const powerLevelEvent = room.currentState.getStateEvents(
|
if (!powerLevelEvent) return;
|
||||||
"m.room.power_levels", "",
|
|
||||||
);
|
|
||||||
if (!powerLevelEvent) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const isMuted = this.state.muted;
|
const isMuted = this.state.muted;
|
||||||
const powerLevels = powerLevelEvent.getContent();
|
const powerLevels = powerLevelEvent.getContent();
|
||||||
const levelToSend = (
|
const levelToSend = (
|
||||||
|
@ -350,7 +355,7 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
}
|
}
|
||||||
level = parseInt(level);
|
level = parseInt(level);
|
||||||
|
|
||||||
if (level !== NaN) {
|
if (!isNaN(level)) {
|
||||||
this.setState({ updating: this.state.updating + 1 });
|
this.setState({ updating: this.state.updating + 1 });
|
||||||
this.props.matrixClient.setPowerLevel(roomId, target, level, powerLevelEvent).then(
|
this.props.matrixClient.setPowerLevel(roomId, target, level, powerLevelEvent).then(
|
||||||
function() {
|
function() {
|
||||||
|
@ -375,19 +380,14 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
const roomId = this.props.member.roomId;
|
const roomId = this.props.member.roomId;
|
||||||
const target = this.props.member.userId;
|
const target = this.props.member.userId;
|
||||||
const room = this.props.matrixClient.getRoom(roomId);
|
const room = this.props.matrixClient.getRoom(roomId);
|
||||||
if (!room) {
|
if (!room) return;
|
||||||
return;
|
|
||||||
}
|
const powerLevelEvent = room.currentState.getStateEvents("m.room.power_levels", "");
|
||||||
const powerLevelEvent = room.currentState.getStateEvents(
|
if (!powerLevelEvent) return;
|
||||||
"m.room.power_levels", "",
|
|
||||||
);
|
|
||||||
if (!powerLevelEvent) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const me = room.getMember(this.props.matrixClient.credentials.userId);
|
const me = room.getMember(this.props.matrixClient.credentials.userId);
|
||||||
if (!me) {
|
if (!me) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
const defaultLevel = powerLevelEvent.getContent().users_default;
|
const defaultLevel = powerLevelEvent.getContent().users_default;
|
||||||
let modLevel = me.powerLevel - 1;
|
let modLevel = me.powerLevel - 1;
|
||||||
if (modLevel > 50 && defaultLevel < 50) modLevel = 50; // try to stick with the vector level defaults
|
if (modLevel > 50 && defaultLevel < 50) modLevel = 50; // try to stick with the vector level defaults
|
||||||
|
@ -400,7 +400,7 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
// get out of sync if we force setState here!
|
// get out of sync if we force setState here!
|
||||||
console.log("Mod toggle success");
|
console.log("Mod toggle success");
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
if (err.errcode == 'M_GUEST_ACCESS_FORBIDDEN') {
|
if (err.errcode === 'M_GUEST_ACCESS_FORBIDDEN') {
|
||||||
dis.dispatch({action: 'view_set_mxid'});
|
dis.dispatch({action: 'view_set_mxid'});
|
||||||
} else {
|
} else {
|
||||||
console.error("Toggle moderator error:" + err);
|
console.error("Toggle moderator error:" + err);
|
||||||
|
@ -436,7 +436,6 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
onPowerChange: function(powerLevel) {
|
onPowerChange: function(powerLevel) {
|
||||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
|
||||||
const roomId = this.props.member.roomId;
|
const roomId = this.props.member.roomId;
|
||||||
const target = this.props.member.userId;
|
const target = this.props.member.userId;
|
||||||
const room = this.props.matrixClient.getRoom(roomId);
|
const room = this.props.matrixClient.getRoom(roomId);
|
||||||
|
@ -497,19 +496,14 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
modifyLevel: false,
|
modifyLevel: false,
|
||||||
};
|
};
|
||||||
const room = this.props.matrixClient.getRoom(member.roomId);
|
const room = this.props.matrixClient.getRoom(member.roomId);
|
||||||
if (!room) {
|
if (!room) return defaultPerms;
|
||||||
return defaultPerms;
|
|
||||||
}
|
const powerLevels = room.currentState.getStateEvents("m.room.power_levels", "");
|
||||||
const powerLevels = room.currentState.getStateEvents(
|
if (!powerLevels) return defaultPerms;
|
||||||
"m.room.power_levels", "",
|
|
||||||
);
|
|
||||||
if (!powerLevels) {
|
|
||||||
return defaultPerms;
|
|
||||||
}
|
|
||||||
const me = room.getMember(this.props.matrixClient.credentials.userId);
|
const me = room.getMember(this.props.matrixClient.credentials.userId);
|
||||||
if (!me) {
|
if (!me) return defaultPerms;
|
||||||
return defaultPerms;
|
|
||||||
}
|
|
||||||
const them = member;
|
const them = member;
|
||||||
return {
|
return {
|
||||||
can: this._calculateCanPermissions(
|
can: this._calculateCanPermissions(
|
||||||
|
@ -545,14 +539,13 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
can.ban = me.powerLevel >= powerLevels.ban;
|
can.ban = me.powerLevel >= powerLevels.ban;
|
||||||
can.mute = me.powerLevel >= editPowerLevel;
|
can.mute = me.powerLevel >= editPowerLevel;
|
||||||
can.toggleMod = me.powerLevel > them.powerLevel && them.powerLevel >= levelToSend;
|
can.toggleMod = me.powerLevel > them.powerLevel && them.powerLevel >= levelToSend;
|
||||||
can.modifyLevel = me.powerLevel > them.powerLevel;
|
can.modifyLevel = me.powerLevel > them.powerLevel && me.powerLevel >= editPowerLevel;
|
||||||
return can;
|
return can;
|
||||||
},
|
},
|
||||||
|
|
||||||
_isMuted: function(member, powerLevelContent) {
|
_isMuted: function(member, powerLevelContent) {
|
||||||
if (!powerLevelContent || !member) {
|
if (!powerLevelContent || !member) return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const levelToSend = (
|
const levelToSend = (
|
||||||
(powerLevelContent.events ? powerLevelContent.events["m.room.message"] : null) ||
|
(powerLevelContent.events ? powerLevelContent.events["m.room.message"] : null) ||
|
||||||
powerLevelContent.events_default
|
powerLevelContent.events_default
|
||||||
|
@ -568,14 +561,15 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
onMemberAvatarClick: function() {
|
onMemberAvatarClick: function() {
|
||||||
const avatarUrl = this.props.member.user ? this.props.member.user.avatarUrl : this.props.member.events.member.getContent().avatar_url;
|
const member = this.props.member;
|
||||||
|
const avatarUrl = member.user ? member.user.avatarUrl : member.events.member.getContent().avatar_url;
|
||||||
if(!avatarUrl) return;
|
if(!avatarUrl) return;
|
||||||
|
|
||||||
const httpUrl = this.props.matrixClient.mxcUrlToHttp(avatarUrl);
|
const httpUrl = this.props.matrixClient.mxcUrlToHttp(avatarUrl);
|
||||||
const ImageView = sdk.getComponent("elements.ImageView");
|
const ImageView = sdk.getComponent("elements.ImageView");
|
||||||
const params = {
|
const params = {
|
||||||
src: httpUrl,
|
src: httpUrl,
|
||||||
name: this.props.member.name,
|
name: member.name,
|
||||||
};
|
};
|
||||||
|
|
||||||
Modal.createDialog(ImageView, params, "mx_Dialog_lightbox");
|
Modal.createDialog(ImageView, params, "mx_Dialog_lightbox");
|
||||||
|
@ -589,9 +583,7 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
_renderDevices: function() {
|
_renderDevices: function() {
|
||||||
if (!this._enableDevices) {
|
if (!this._enableDevices) return null;
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const devices = this.state.devices;
|
const devices = this.state.devices;
|
||||||
const MemberDeviceInfo = sdk.getComponent('rooms.MemberDeviceInfo');
|
const MemberDeviceInfo = sdk.getComponent('rooms.MemberDeviceInfo');
|
||||||
|
@ -629,6 +621,8 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
const member = this.props.member;
|
const member = this.props.member;
|
||||||
|
|
||||||
let ignoreButton = null;
|
let ignoreButton = null;
|
||||||
|
let insertPillButton = null;
|
||||||
|
let inviteUserButton = null;
|
||||||
let readReceiptButton = null;
|
let readReceiptButton = null;
|
||||||
|
|
||||||
// Only allow the user to ignore the user if its not ourselves
|
// Only allow the user to ignore the user if its not ourselves
|
||||||
|
@ -653,29 +647,71 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onInsertPillButton = function() {
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'insert_mention',
|
||||||
|
user_id: member.userId,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
readReceiptButton = (
|
readReceiptButton = (
|
||||||
<AccessibleButton onClick={onReadReceiptButton} className="mx_MemberInfo_field">
|
<AccessibleButton onClick={onReadReceiptButton} className="mx_MemberInfo_field">
|
||||||
{ _t('Jump to read receipt') }
|
{ _t('Jump to read receipt') }
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
insertPillButton = (
|
||||||
|
<AccessibleButton onClick={onInsertPillButton} className={"mx_MemberInfo_field"}>
|
||||||
|
{ _t('Mention') }
|
||||||
|
</AccessibleButton>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!member || !member.membership || member.membership === 'leave') {
|
||||||
|
const roomId = member && member.roomId ? member.roomId : RoomViewStore.getRoomId();
|
||||||
|
const onInviteUserButton = async () => {
|
||||||
|
try {
|
||||||
|
await cli.invite(roomId, member.userId);
|
||||||
|
} catch (err) {
|
||||||
|
const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
|
||||||
|
Modal.createTrackedDialog('Failed to invite', '', ErrorDialog, {
|
||||||
|
title: _t('Failed to invite'),
|
||||||
|
description: ((err && err.message) ? err.message : "Operation failed"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inviteUserButton = (
|
||||||
|
<AccessibleButton onClick={onInviteUserButton} className="mx_MemberInfo_field">
|
||||||
|
{ _t('Invite') }
|
||||||
|
</AccessibleButton>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ignoreButton && !readReceiptButton) return null;
|
if (!ignoreButton && !readReceiptButton && !insertPillButton && !inviteUserButton) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h3>{ _t("User Options") }</h3>
|
<h3>{ _t("User Options") }</h3>
|
||||||
<div className="mx_MemberInfo_buttons">
|
<div className="mx_MemberInfo_buttons">
|
||||||
{ readReceiptButton }
|
{ readReceiptButton }
|
||||||
|
{ insertPillButton }
|
||||||
{ ignoreButton }
|
{ ignoreButton }
|
||||||
|
{ inviteUserButton }
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
let startChat, kickButton, banButton, muteButton, giveModButton, spinner;
|
let startChat;
|
||||||
|
let kickButton;
|
||||||
|
let banButton;
|
||||||
|
let muteButton;
|
||||||
|
let giveModButton;
|
||||||
|
let spinner;
|
||||||
|
|
||||||
if (this.props.member.userId !== this.props.matrixClient.credentials.userId) {
|
if (this.props.member.userId !== this.props.matrixClient.credentials.userId) {
|
||||||
const dmRoomMap = new DMRoomMap(this.props.matrixClient);
|
const dmRoomMap = new DMRoomMap(this.props.matrixClient);
|
||||||
const dmRooms = dmRoomMap.getDMRoomsForUserId(this.props.member.userId);
|
const dmRooms = dmRoomMap.getDMRoomsForUserId(this.props.member.userId);
|
||||||
|
@ -689,7 +725,7 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
const me = room.getMember(this.props.matrixClient.credentials.userId);
|
const me = room.getMember(this.props.matrixClient.credentials.userId);
|
||||||
const highlight = (
|
const highlight = (
|
||||||
room.getUnreadNotificationCount('highlight') > 0 ||
|
room.getUnreadNotificationCount('highlight') > 0 ||
|
||||||
me.membership == "invite"
|
me.membership === "invite"
|
||||||
);
|
);
|
||||||
tiles.push(
|
tiles.push(
|
||||||
<RoomTile key={room.roomId} room={room}
|
<RoomTile key={room.roomId} room={room}
|
||||||
|
@ -697,7 +733,7 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
selected={false}
|
selected={false}
|
||||||
unread={Unread.doesRoomHaveUnreadMessages(room)}
|
unread={Unread.doesRoomHaveUnreadMessages(room)}
|
||||||
highlight={highlight}
|
highlight={highlight}
|
||||||
isInvite={me.membership == "invite"}
|
isInvite={me.membership === "invite"}
|
||||||
onClick={this.onRoomTileClick}
|
onClick={this.onRoomTileClick}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
@ -742,7 +778,7 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
}
|
}
|
||||||
if (this.state.can.ban) {
|
if (this.state.can.ban) {
|
||||||
let label = _t("Ban");
|
let label = _t("Ban");
|
||||||
if (this.props.member.membership == 'ban') {
|
if (this.props.member.membership === 'ban') {
|
||||||
label = _t("Unban");
|
label = _t("Unban");
|
||||||
}
|
}
|
||||||
banButton = (
|
banButton = (
|
||||||
|
@ -768,9 +804,6 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
</AccessibleButton>;
|
</AccessibleButton>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: we should have an invite button if this MemberInfo is showing a user who isn't actually in the current room yet
|
|
||||||
// e.g. clicking on a linkified userid in a room
|
|
||||||
|
|
||||||
let adminTools;
|
let adminTools;
|
||||||
if (kickButton || banButton || muteButton || giveModButton) {
|
if (kickButton || banButton || muteButton || giveModButton) {
|
||||||
adminTools =
|
adminTools =
|
||||||
|
@ -788,16 +821,39 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
|
|
||||||
const memberName = this.props.member.name;
|
const memberName = this.props.member.name;
|
||||||
|
|
||||||
|
let presenceState;
|
||||||
|
let presenceLastActiveAgo;
|
||||||
|
let presenceCurrentlyActive;
|
||||||
|
|
||||||
if (this.props.member.user) {
|
if (this.props.member.user) {
|
||||||
var presenceState = this.props.member.user.presence;
|
presenceState = this.props.member.user.presence;
|
||||||
var presenceLastActiveAgo = this.props.member.user.lastActiveAgo;
|
presenceLastActiveAgo = this.props.member.user.lastActiveAgo;
|
||||||
const presenceLastTs = this.props.member.user.lastPresenceTs;
|
presenceCurrentlyActive = this.props.member.user.currentlyActive;
|
||||||
var presenceCurrentlyActive = this.props.member.user.currentlyActive;
|
}
|
||||||
|
|
||||||
|
let roomMemberDetails = null;
|
||||||
|
|
||||||
|
if (this.props.member.roomId) { // is in room
|
||||||
|
const PowerSelector = sdk.getComponent('elements.PowerSelector');
|
||||||
|
const PresenceLabel = sdk.getComponent('rooms.PresenceLabel');
|
||||||
|
roomMemberDetails = <div>
|
||||||
|
<div className="mx_MemberInfo_profileField">
|
||||||
|
{ _t("Level:") } <b>
|
||||||
|
<PowerSelector controlled={true}
|
||||||
|
value={parseInt(this.props.member.powerLevel)}
|
||||||
|
disabled={!this.state.can.modifyLevel}
|
||||||
|
onChange={this.onPowerChange} />
|
||||||
|
</b>
|
||||||
|
</div>
|
||||||
|
<div className="mx_MemberInfo_profileField">
|
||||||
|
<PresenceLabel activeAgo={presenceLastActiveAgo}
|
||||||
|
currentlyActive={presenceCurrentlyActive}
|
||||||
|
presenceState={presenceState} />
|
||||||
|
</div>
|
||||||
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
|
const MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
|
||||||
const PowerSelector = sdk.getComponent('elements.PowerSelector');
|
|
||||||
const PresenceLabel = sdk.getComponent('rooms.PresenceLabel');
|
|
||||||
const EmojiText = sdk.getComponent('elements.EmojiText');
|
const EmojiText = sdk.getComponent('elements.EmojiText');
|
||||||
return (
|
return (
|
||||||
<div className="mx_MemberInfo">
|
<div className="mx_MemberInfo">
|
||||||
|
@ -813,14 +869,7 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
<div className="mx_MemberInfo_profileField">
|
<div className="mx_MemberInfo_profileField">
|
||||||
{ this.props.member.userId }
|
{ this.props.member.userId }
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_MemberInfo_profileField">
|
{ roomMemberDetails }
|
||||||
{ _t("Level:") } <b><PowerSelector controlled={true} value={parseInt(this.props.member.powerLevel)} disabled={!this.state.can.modifyLevel} onChange={this.onPowerChange} /></b>
|
|
||||||
</div>
|
|
||||||
<div className="mx_MemberInfo_profileField">
|
|
||||||
<PresenceLabel activeAgo={presenceLastActiveAgo}
|
|
||||||
currentlyActive={presenceCurrentlyActive}
|
|
||||||
presenceState={presenceState} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{ this._renderUserOptions() }
|
{ this._renderUserOptions() }
|
||||||
|
|
|
@ -371,7 +371,7 @@ export default class MessageComposer extends React.Component {
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_MessageComposer mx_fadable" style={{ opacity: this.props.opacity }}>
|
<div className="mx_MessageComposer">
|
||||||
<div className="mx_MessageComposer_wrapper">
|
<div className="mx_MessageComposer_wrapper">
|
||||||
<div className="mx_MessageComposer_row">
|
<div className="mx_MessageComposer_row">
|
||||||
{ controls }
|
{ controls }
|
||||||
|
@ -410,9 +410,6 @@ MessageComposer.propTypes = {
|
||||||
// callback when a file to upload is chosen
|
// callback when a file to upload is chosen
|
||||||
uploadFile: React.PropTypes.func.isRequired,
|
uploadFile: React.PropTypes.func.isRequired,
|
||||||
|
|
||||||
// opacity for dynamic UI fading effects
|
|
||||||
opacity: React.PropTypes.number,
|
|
||||||
|
|
||||||
// string representing the current room app drawer state
|
// string representing the current room app drawer state
|
||||||
showApps: React.PropTypes.bool,
|
showApps: React.PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
|
@ -95,7 +95,9 @@ module.exports = React.createClass({
|
||||||
return (
|
return (
|
||||||
<div className="mx_PinnedEventsPanel">
|
<div className="mx_PinnedEventsPanel">
|
||||||
<div className="mx_PinnedEventsPanel_body">
|
<div className="mx_PinnedEventsPanel_body">
|
||||||
<AccessibleButton className="mx_PinnedEventsPanel_cancel" onClick={this.props.onCancelClick}><img src="img/cancel.svg" width="18" height="18" /></AccessibleButton>
|
<AccessibleButton className="mx_PinnedEventsPanel_cancel" onClick={this.props.onCancelClick}>
|
||||||
|
<img className="mx_filterFlipColor" src="img/cancel.svg" width="18" height="18" />
|
||||||
|
</AccessibleButton>
|
||||||
<h3 className="mx_PinnedEventsPanel_header">{ _t("Pinned Messages") }</h3>
|
<h3 className="mx_PinnedEventsPanel_header">{ _t("Pinned Messages") }</h3>
|
||||||
{ tiles }
|
{ tiles }
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -25,15 +25,28 @@ import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
function getDisplayAliasForRoom(room) {
|
function getDisplayAliasForRoom(room) {
|
||||||
return room.canonical_alias || (room.aliases ? room.aliases[0] : "");
|
return room.canonicalAlias || (room.aliases ? room.aliases[0] : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
const RoomDetailRow = React.createClass({
|
const RoomDetailRow = React.createClass({
|
||||||
|
propTypes: PropTypes.shape({
|
||||||
|
name: PropTypes.string,
|
||||||
|
topic: PropTypes.string,
|
||||||
|
roomId: PropTypes.string,
|
||||||
|
avatarUrl: PropTypes.string,
|
||||||
|
numJoinedMembers: PropTypes.number,
|
||||||
|
canonicalAlias: PropTypes.string,
|
||||||
|
aliases: PropTypes.arrayOf(PropTypes.string),
|
||||||
|
|
||||||
|
worldReadable: PropTypes.bool,
|
||||||
|
guestCanJoin: PropTypes.bool,
|
||||||
|
}),
|
||||||
|
|
||||||
onClick: function(ev) {
|
onClick: function(ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'view_room',
|
action: 'view_room',
|
||||||
room_id: this.props.room.room_id,
|
room_id: this.props.room.roomId,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -50,10 +63,10 @@ const RoomDetailRow = React.createClass({
|
||||||
const name = room.name || getDisplayAliasForRoom(room) || _t('Unnamed room');
|
const name = room.name || getDisplayAliasForRoom(room) || _t('Unnamed room');
|
||||||
const topic = linkifyString(sanitizeHtml(room.topic || ''));
|
const topic = linkifyString(sanitizeHtml(room.topic || ''));
|
||||||
|
|
||||||
const guestRead = room.world_readable ? (
|
const guestRead = room.worldReadable ? (
|
||||||
<div className="mx_RoomDirectory_perm">{ _t('World readable') }</div>
|
<div className="mx_RoomDirectory_perm">{ _t('World readable') }</div>
|
||||||
) : <div />;
|
) : <div />;
|
||||||
const guestJoin = room.guest_can_join ? (
|
const guestJoin = room.guestCanJoin ? (
|
||||||
<div className="mx_RoomDirectory_perm">{ _t('Guests can join') }</div>
|
<div className="mx_RoomDirectory_perm">{ _t('Guests can join') }</div>
|
||||||
) : <div />;
|
) : <div />;
|
||||||
|
|
||||||
|
@ -62,13 +75,13 @@ const RoomDetailRow = React.createClass({
|
||||||
{ guestJoin }
|
{ guestJoin }
|
||||||
</div>) : <div />;
|
</div>) : <div />;
|
||||||
|
|
||||||
return <tr key={room.room_id} onClick={this.onClick}>
|
return <tr key={room.roomId} onClick={this.onClick}>
|
||||||
<td className="mx_RoomDirectory_roomAvatar">
|
<td className="mx_RoomDirectory_roomAvatar">
|
||||||
<BaseAvatar width={24} height={24} resizeMethod='crop'
|
<BaseAvatar width={24} height={24} resizeMethod='crop'
|
||||||
name={name} idName={name}
|
name={name} idName={name}
|
||||||
url={ContentRepo.getHttpUriForMxc(
|
url={ContentRepo.getHttpUriForMxc(
|
||||||
MatrixClientPeg.get().getHomeserverUrl(),
|
MatrixClientPeg.get().getHomeserverUrl(),
|
||||||
room.avatar_url, 24, 24, "crop")} />
|
room.avatarUrl, 24, 24, "crop")} />
|
||||||
</td>
|
</td>
|
||||||
<td className="mx_RoomDirectory_roomDescription">
|
<td className="mx_RoomDirectory_roomDescription">
|
||||||
<div className="mx_RoomDirectory_name">{ name }</div>
|
<div className="mx_RoomDirectory_name">{ name }</div>
|
||||||
|
@ -79,7 +92,7 @@ const RoomDetailRow = React.createClass({
|
||||||
<div className="mx_RoomDirectory_alias">{ getDisplayAliasForRoom(room) }</div>
|
<div className="mx_RoomDirectory_alias">{ getDisplayAliasForRoom(room) }</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="mx_RoomDirectory_roomMemberCount">
|
<td className="mx_RoomDirectory_roomMemberCount">
|
||||||
{ room.num_joined_members }
|
{ room.numJoinedMembers }
|
||||||
</td>
|
</td>
|
||||||
</tr>;
|
</tr>;
|
||||||
},
|
},
|
||||||
|
@ -92,13 +105,14 @@ export default React.createClass({
|
||||||
rooms: PropTypes.arrayOf(PropTypes.shape({
|
rooms: PropTypes.arrayOf(PropTypes.shape({
|
||||||
name: PropTypes.string,
|
name: PropTypes.string,
|
||||||
topic: PropTypes.string,
|
topic: PropTypes.string,
|
||||||
room_id: PropTypes.string,
|
roomId: PropTypes.string,
|
||||||
num_joined_members: PropTypes.number,
|
avatarUrl: PropTypes.string,
|
||||||
canonical_alias: PropTypes.string,
|
numJoinedMembers: PropTypes.number,
|
||||||
|
canonicalAlias: PropTypes.string,
|
||||||
aliases: PropTypes.arrayOf(PropTypes.string),
|
aliases: PropTypes.arrayOf(PropTypes.string),
|
||||||
|
|
||||||
world_readable: PropTypes.bool,
|
worldReadable: PropTypes.bool,
|
||||||
guest_can_join: PropTypes.bool,
|
guestCanJoin: PropTypes.bool,
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -281,8 +281,11 @@ module.exports = React.createClass({
|
||||||
<input id="avatarInput" type="file" onChange={this.onAvatarSelected} />
|
<input id="avatarInput" type="file" onChange={this.onAvatarSelected} />
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_RoomHeader_avatarPicker_remove" onClick={this.onAvatarRemoveClick}>
|
<div className="mx_RoomHeader_avatarPicker_remove" onClick={this.onAvatarRemoveClick}>
|
||||||
<img src="img/cancel.svg" width="10"
|
<img src="img/cancel.svg"
|
||||||
alt={_t("Remove avatar")} title={_t("Remove avatar")} />
|
className="mx_filterFlipColor"
|
||||||
|
width="10"
|
||||||
|
alt={_t("Remove avatar")}
|
||||||
|
title={_t("Remove avatar")} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -19,7 +19,6 @@ import Promise from 'bluebird';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { _t, _tJsx, _td } from '../../../languageHandler';
|
import { _t, _tJsx, _td } from '../../../languageHandler';
|
||||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||||
import SdkConfig from '../../../SdkConfig';
|
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import Modal from '../../../Modal';
|
import Modal from '../../../Modal';
|
||||||
import ObjectUtils from '../../../ObjectUtils';
|
import ObjectUtils from '../../../ObjectUtils';
|
||||||
|
@ -158,9 +157,9 @@ module.exports = React.createClass({
|
||||||
});
|
});
|
||||||
|
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'ui_opacity',
|
action: 'panel_disable',
|
||||||
sideOpacity: 0.3,
|
sideDisabled: true,
|
||||||
middleOpacity: 0.3,
|
middleDisabled: true,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -171,9 +170,9 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'ui_opacity',
|
action: 'panel_disable',
|
||||||
sideOpacity: 1.0,
|
sideDisabled: false,
|
||||||
middleOpacity: 1.0,
|
middleDisabled: false,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -18,10 +18,13 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import dis from '../../../dispatcher';
|
import dis from '../../../dispatcher';
|
||||||
|
|
||||||
|
import UserSettingsStore from '../../../UserSettingsStore';
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'VideoView',
|
displayName: 'VideoView',
|
||||||
|
|
||||||
|
@ -108,14 +111,18 @@ module.exports = React.createClass({
|
||||||
document.mozFullScreenElement ||
|
document.mozFullScreenElement ||
|
||||||
document.webkitFullscreenElement);
|
document.webkitFullscreenElement);
|
||||||
const maxVideoHeight = fullscreenElement ? null : this.props.maxHeight;
|
const maxVideoHeight = fullscreenElement ? null : this.props.maxHeight;
|
||||||
|
const localVideoFeedClasses = classNames("mx_VideoView_localVideoFeed",
|
||||||
|
{ "mx_VideoView_localVideoFeed_flipped":
|
||||||
|
UserSettingsStore.getSyncedSetting('VideoView.flipVideoHorizontally', false),
|
||||||
|
},
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<div className="mx_VideoView" ref={this.setContainer} onClick={this.props.onClick}>
|
<div className="mx_VideoView" ref={this.setContainer} onClick={this.props.onClick}>
|
||||||
<div className="mx_VideoView_remoteVideoFeed">
|
<div className="mx_VideoView_remoteVideoFeed">
|
||||||
<VideoFeed ref="remote" onResize={this.props.onResize}
|
<VideoFeed ref="remote" onResize={this.props.onResize}
|
||||||
maxHeight={maxVideoHeight} />
|
maxHeight={maxVideoHeight} />
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_VideoView_localVideoFeed">
|
<div className={localVideoFeedClasses}>
|
||||||
<VideoFeed ref="local" />
|
<VideoFeed ref="local" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -43,5 +43,9 @@ export function groupRoomFromApiObject(apiObject) {
|
||||||
roomId: apiObject.room_id,
|
roomId: apiObject.room_id,
|
||||||
canonicalAlias: apiObject.canonical_alias,
|
canonicalAlias: apiObject.canonical_alias,
|
||||||
avatarUrl: apiObject.avatar_url,
|
avatarUrl: apiObject.avatar_url,
|
||||||
|
topic: apiObject.topic,
|
||||||
|
numJoinedMembers: apiObject.num_joined_members,
|
||||||
|
worldReadable: apiObject.world_readable,
|
||||||
|
guestCanJoin: apiObject.guest_can_join,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,8 +54,6 @@
|
||||||
"Room name or alias": "Room name or alias",
|
"Room name or alias": "Room name or alias",
|
||||||
"Add to community": "Add to community",
|
"Add to community": "Add to community",
|
||||||
"Failed to invite the following users to %(groupId)s:": "Failed to invite the following users to %(groupId)s:",
|
"Failed to invite the following users to %(groupId)s:": "Failed to invite the following users to %(groupId)s:",
|
||||||
"Invites sent": "Invites sent",
|
|
||||||
"Your community invitations have been sent.": "Your community invitations have been sent.",
|
|
||||||
"Failed to invite users to community": "Failed to invite users to community",
|
"Failed to invite users to community": "Failed to invite users to community",
|
||||||
"Failed to invite users to %(groupId)s": "Failed to invite users to %(groupId)s",
|
"Failed to invite users to %(groupId)s": "Failed to invite users to %(groupId)s",
|
||||||
"Failed to add the following rooms to %(groupId)s:": "Failed to add the following rooms to %(groupId)s:",
|
"Failed to add the following rooms to %(groupId)s:": "Failed to add the following rooms to %(groupId)s:",
|
||||||
|
@ -154,8 +152,10 @@
|
||||||
"%(widgetName)s widget removed by %(senderName)s": "%(widgetName)s widget removed by %(senderName)s",
|
"%(widgetName)s widget removed by %(senderName)s": "%(widgetName)s widget removed by %(senderName)s",
|
||||||
"Communities": "Communities",
|
"Communities": "Communities",
|
||||||
"Message Pinning": "Message Pinning",
|
"Message Pinning": "Message Pinning",
|
||||||
|
"Mention": "Mention",
|
||||||
"%(displayName)s is typing": "%(displayName)s is typing",
|
"%(displayName)s is typing": "%(displayName)s is typing",
|
||||||
"%(names)s and one other are typing": "%(names)s and one other are typing",
|
"%(names)s and one other are typing": "%(names)s and one other are typing",
|
||||||
|
"%(names)s and %(count)s others are typing|other": "%(names)s and %(count)s others are typing",
|
||||||
"%(names)s and %(lastPerson)s are typing": "%(names)s and %(lastPerson)s are typing",
|
"%(names)s and %(lastPerson)s are typing": "%(names)s and %(lastPerson)s are typing",
|
||||||
"Failure to create room": "Failure to create room",
|
"Failure to create room": "Failure to create room",
|
||||||
"Server may be unavailable, overloaded, or you hit a bug.": "Server may be unavailable, overloaded, or you hit a bug.",
|
"Server may be unavailable, overloaded, or you hit a bug.": "Server may be unavailable, overloaded, or you hit a bug.",
|
||||||
|
@ -240,6 +240,7 @@
|
||||||
"Unignore": "Unignore",
|
"Unignore": "Unignore",
|
||||||
"Ignore": "Ignore",
|
"Ignore": "Ignore",
|
||||||
"Jump to read receipt": "Jump to read receipt",
|
"Jump to read receipt": "Jump to read receipt",
|
||||||
|
"Invite": "Invite",
|
||||||
"User Options": "User Options",
|
"User Options": "User Options",
|
||||||
"Direct chats": "Direct chats",
|
"Direct chats": "Direct chats",
|
||||||
"Unmute": "Unmute",
|
"Unmute": "Unmute",
|
||||||
|
@ -451,6 +452,7 @@
|
||||||
"You are about to be taken to a third-party site so you can authenticate your account for use with %(integrationsUrl)s. Do you wish to continue?": "You are about to be taken to a third-party site so you can authenticate your account for use with %(integrationsUrl)s. Do you wish to continue?",
|
"You are about to be taken to a third-party site so you can authenticate your account for use with %(integrationsUrl)s. Do you wish to continue?": "You are about to be taken to a third-party site so you can authenticate your account for use with %(integrationsUrl)s. Do you wish to continue?",
|
||||||
"Removed or unknown message type": "Removed or unknown message type",
|
"Removed or unknown message type": "Removed or unknown message type",
|
||||||
"Message removed by %(userId)s": "Message removed by %(userId)s",
|
"Message removed by %(userId)s": "Message removed by %(userId)s",
|
||||||
|
"Message removed": "Message removed",
|
||||||
"Robot check is currently unavailable on desktop - please use a <a>web browser</a>": "Robot check is currently unavailable on desktop - please use a <a>web browser</a>",
|
"Robot check is currently unavailable on desktop - please use a <a>web browser</a>": "Robot check is currently unavailable on desktop - please use a <a>web browser</a>",
|
||||||
"This Home Server would like to make sure you are not a robot": "This Home Server would like to make sure you are not a robot",
|
"This Home Server would like to make sure you are not a robot": "This Home Server would like to make sure you are not a robot",
|
||||||
"Sign in with CAS": "Sign in with CAS",
|
"Sign in with CAS": "Sign in with CAS",
|
||||||
|
@ -495,10 +497,13 @@
|
||||||
"Are you sure you want to remove '%(roomName)s' from %(groupId)s?": "Are you sure you want to remove '%(roomName)s' from %(groupId)s?",
|
"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.",
|
"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",
|
"Remove": "Remove",
|
||||||
|
"Remove this room from the community": "Remove this room from the community",
|
||||||
"Unknown Address": "Unknown Address",
|
"Unknown Address": "Unknown Address",
|
||||||
"NOTE: Apps are not end-to-end encrypted": "NOTE: Apps are not end-to-end encrypted",
|
"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:",
|
"Do you want to load widget from URL:": "Do you want to load widget from URL:",
|
||||||
"Allow": "Allow",
|
"Allow": "Allow",
|
||||||
|
"Delete Widget": "Delete Widget",
|
||||||
|
"Deleting a widget removes it for all users in this room. Are you sure you want to delete this widget?": "Deleting a widget removes it for all users in this room. Are you sure you want to delete this widget?",
|
||||||
"Delete widget": "Delete widget",
|
"Delete widget": "Delete widget",
|
||||||
"Revoke widget access": "Revoke widget access",
|
"Revoke widget access": "Revoke widget access",
|
||||||
"Edit": "Edit",
|
"Edit": "Edit",
|
||||||
|
@ -566,6 +571,7 @@
|
||||||
"Custom level": "Custom level",
|
"Custom level": "Custom level",
|
||||||
"Room directory": "Room directory",
|
"Room directory": "Room directory",
|
||||||
"Start chat": "Start chat",
|
"Start chat": "Start chat",
|
||||||
|
"And %(count)s more...|other": "And %(count)s more...",
|
||||||
"ex. @bob:example.com": "ex. @bob:example.com",
|
"ex. @bob:example.com": "ex. @bob:example.com",
|
||||||
"Add User": "Add User",
|
"Add User": "Add User",
|
||||||
"Something went wrong!": "Something went wrong!",
|
"Something went wrong!": "Something went wrong!",
|
||||||
|
@ -584,7 +590,7 @@
|
||||||
"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.",
|
"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.",
|
||||||
"%(actionVerb)s this person?": "%(actionVerb)s this person?",
|
"%(actionVerb)s this person?": "%(actionVerb)s this person?",
|
||||||
"Community IDs may only contain alphanumeric characters": "Community IDs may only contain alphanumeric characters",
|
"Community IDs may only contain alphanumeric characters": "Community IDs may only contain alphanumeric characters",
|
||||||
"Room creation failed": "Room creation failed",
|
"Something went wrong whilst creating your community": "Something went wrong whilst creating your community",
|
||||||
"Create Community": "Create Community",
|
"Create Community": "Create Community",
|
||||||
"Community Name": "Community Name",
|
"Community Name": "Community Name",
|
||||||
"Example": "Example",
|
"Example": "Example",
|
||||||
|
@ -677,17 +683,17 @@
|
||||||
"Leave %(groupName)s?": "Leave %(groupName)s?",
|
"Leave %(groupName)s?": "Leave %(groupName)s?",
|
||||||
"Leave": "Leave",
|
"Leave": "Leave",
|
||||||
"Unable to leave room": "Unable to leave room",
|
"Unable to leave room": "Unable to leave room",
|
||||||
|
"Community Settings": "Community Settings",
|
||||||
"Add rooms to this community": "Add rooms to this community",
|
"Add rooms to this community": "Add rooms to this community",
|
||||||
"Featured Rooms:": "Featured Rooms:",
|
"Featured Rooms:": "Featured Rooms:",
|
||||||
"Featured Users:": "Featured Users:",
|
"Featured Users:": "Featured Users:",
|
||||||
"%(inviter)s has invited you to join this community": "%(inviter)s has invited you to join this community",
|
"%(inviter)s has invited you to join this community": "%(inviter)s has invited you to join this community",
|
||||||
"You are a member of this community": "You are a member of this community",
|
|
||||||
"You are an administrator of this community": "You are an administrator of 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",
|
"Community Member Settings": "Community Member Settings",
|
||||||
"Publish this community on your profile": "Publish this community on your profile",
|
"Publish this community on your profile": "Publish this community on your profile",
|
||||||
"Long Description (HTML)": "Long Description (HTML)",
|
"Long Description (HTML)": "Long Description (HTML)",
|
||||||
"Description": "Description",
|
"Description": "Description",
|
||||||
"Community Settings": "Community Settings",
|
|
||||||
"Community %(groupId)s not found": "Community %(groupId)s not found",
|
"Community %(groupId)s not found": "Community %(groupId)s not found",
|
||||||
"This Home server does not support communities": "This Home server does not support communities",
|
"This Home server does not support communities": "This Home server does not support communities",
|
||||||
"Failed to load %(groupId)s": "Failed to load %(groupId)s",
|
"Failed to load %(groupId)s": "Failed to load %(groupId)s",
|
||||||
|
@ -753,6 +759,7 @@
|
||||||
"Disable Emoji suggestions while typing": "Disable Emoji suggestions while typing",
|
"Disable Emoji suggestions while typing": "Disable Emoji suggestions while typing",
|
||||||
"Hide avatars in user and room mentions": "Hide avatars in user and room mentions",
|
"Hide avatars in user and room mentions": "Hide avatars in user and room mentions",
|
||||||
"Disable big emoji in chat": "Disable big emoji in chat",
|
"Disable big emoji in chat": "Disable big emoji in chat",
|
||||||
|
"Mirror local video feed": "Mirror local video feed",
|
||||||
"Opt out of analytics": "Opt out of analytics",
|
"Opt out of analytics": "Opt out of analytics",
|
||||||
"Disable Peer-to-Peer for 1:1 calls": "Disable Peer-to-Peer for 1:1 calls",
|
"Disable Peer-to-Peer for 1:1 calls": "Disable Peer-to-Peer for 1:1 calls",
|
||||||
"Never send encrypted messages to unverified devices from this device": "Never send encrypted messages to unverified devices from this device",
|
"Never send encrypted messages to unverified devices from this device": "Never send encrypted messages to unverified devices from this device",
|
||||||
|
|
|
@ -56,6 +56,10 @@ class FlairStore extends EventEmitter {
|
||||||
return groupSupport;
|
return groupSupport;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
invalidatePublicisedGroups(userId) {
|
||||||
|
delete this._userGroups[userId];
|
||||||
|
}
|
||||||
|
|
||||||
getPublicisedGroupsCached(matrixClient, userId) {
|
getPublicisedGroupsCached(matrixClient, userId) {
|
||||||
if (this._userGroups[userId]) {
|
if (this._userGroups[userId]) {
|
||||||
return Promise.resolve(this._userGroups[userId]);
|
return Promise.resolve(this._userGroups[userId]);
|
||||||
|
|
|
@ -15,6 +15,8 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import EventEmitter from 'events';
|
import EventEmitter from 'events';
|
||||||
|
import { groupMemberFromApiObject, groupRoomFromApiObject } from '../groups';
|
||||||
|
import FlairStore from './FlairStore';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores the group summary for a room and provides an API to change it and
|
* Stores the group summary for a room and provides an API to change it and
|
||||||
|
@ -27,8 +29,36 @@ export default class GroupStore extends EventEmitter {
|
||||||
this._matrixClient = matrixClient;
|
this._matrixClient = matrixClient;
|
||||||
this._summary = {};
|
this._summary = {};
|
||||||
this._rooms = [];
|
this._rooms = [];
|
||||||
this._fetchSummary();
|
|
||||||
this._fetchRooms();
|
this.on('error', (err) => {
|
||||||
|
console.error(`GroupStore for ${this.groupId} encountered error`, err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_fetchMembers() {
|
||||||
|
this._matrixClient.getGroupUsers(this.groupId).then((result) => {
|
||||||
|
this._members = result.chunk.map((apiMember) => {
|
||||||
|
return groupMemberFromApiObject(apiMember);
|
||||||
|
});
|
||||||
|
this._notifyListeners();
|
||||||
|
}).catch((err) => {
|
||||||
|
console.error("Failed to get group member list: " + err);
|
||||||
|
this.emit('error', err);
|
||||||
|
});
|
||||||
|
|
||||||
|
this._matrixClient.getGroupInvitedUsers(this.groupId).then((result) => {
|
||||||
|
this._invitedMembers = result.chunk.map((apiMember) => {
|
||||||
|
return groupMemberFromApiObject(apiMember);
|
||||||
|
});
|
||||||
|
this._notifyListeners();
|
||||||
|
}).catch((err) => {
|
||||||
|
// Invited users not visible to non-members
|
||||||
|
if (err.httpStatus === 403) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.error("Failed to get group invited member list: " + err);
|
||||||
|
this.emit('error', err);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_fetchSummary() {
|
_fetchSummary() {
|
||||||
|
@ -42,7 +72,9 @@ export default class GroupStore extends EventEmitter {
|
||||||
|
|
||||||
_fetchRooms() {
|
_fetchRooms() {
|
||||||
this._matrixClient.getGroupRooms(this.groupId).then((resp) => {
|
this._matrixClient.getGroupRooms(this.groupId).then((resp) => {
|
||||||
this._rooms = resp.chunk;
|
this._rooms = resp.chunk.map((apiRoom) => {
|
||||||
|
return groupRoomFromApiObject(apiRoom);
|
||||||
|
});
|
||||||
this._notifyListeners();
|
this._notifyListeners();
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
this.emit('error', err);
|
this.emit('error', err);
|
||||||
|
@ -53,6 +85,17 @@ export default class GroupStore extends EventEmitter {
|
||||||
this.emit('update');
|
this.emit('update');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registerListener(fn) {
|
||||||
|
this.on('update', fn);
|
||||||
|
this._fetchSummary();
|
||||||
|
this._fetchRooms();
|
||||||
|
this._fetchMembers();
|
||||||
|
}
|
||||||
|
|
||||||
|
unregisterListener(fn) {
|
||||||
|
this.removeListener('update', fn);
|
||||||
|
}
|
||||||
|
|
||||||
getSummary() {
|
getSummary() {
|
||||||
return this._summary;
|
return this._summary;
|
||||||
}
|
}
|
||||||
|
@ -61,6 +104,14 @@ export default class GroupStore extends EventEmitter {
|
||||||
return this._rooms;
|
return this._rooms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getGroupMembers( ) {
|
||||||
|
return this._members;
|
||||||
|
}
|
||||||
|
|
||||||
|
getGroupInvitedMembers( ) {
|
||||||
|
return this._invitedMembers;
|
||||||
|
}
|
||||||
|
|
||||||
getGroupPublicity() {
|
getGroupPublicity() {
|
||||||
return this._summary.user ? this._summary.user.is_publicised : null;
|
return this._summary.user ? this._summary.user.is_publicised : null;
|
||||||
}
|
}
|
||||||
|
@ -83,6 +134,11 @@ export default class GroupStore extends EventEmitter {
|
||||||
.then(this._fetchRooms.bind(this));
|
.then(this._fetchRooms.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inviteUserToGroup(userId) {
|
||||||
|
return this._matrixClient.inviteUserToGroup(this.groupId, userId)
|
||||||
|
.then(this._fetchMembers.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
addRoomToGroupSummary(roomId, categoryId) {
|
addRoomToGroupSummary(roomId, categoryId) {
|
||||||
return this._matrixClient
|
return this._matrixClient
|
||||||
.addRoomToGroupSummary(this.groupId, roomId, categoryId)
|
.addRoomToGroupSummary(this.groupId, roomId, categoryId)
|
||||||
|
@ -110,6 +166,7 @@ export default class GroupStore extends EventEmitter {
|
||||||
setGroupPublicity(isPublished) {
|
setGroupPublicity(isPublished) {
|
||||||
return this._matrixClient
|
return this._matrixClient
|
||||||
.setGroupPublicity(this.groupId, isPublished)
|
.setGroupPublicity(this.groupId, isPublished)
|
||||||
|
.then(() => { FlairStore.invalidatePublicisedGroups(this._matrixClient.credentials.userId); })
|
||||||
.then(this._fetchSummary.bind(this));
|
.then(this._fetchSummary.bind(this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,8 +33,7 @@ class GroupStoreCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let singletonGroupStoreCache = null;
|
if (global.singletonGroupStoreCache === undefined) {
|
||||||
if (!singletonGroupStoreCache) {
|
global.singletonGroupStoreCache = new GroupStoreCache();
|
||||||
singletonGroupStoreCache = new GroupStoreCache();
|
|
||||||
}
|
}
|
||||||
module.exports = singletonGroupStoreCache;
|
export default global.singletonGroupStoreCache;
|
||||||
|
|
|
@ -72,6 +72,13 @@ class RoomViewStore extends Store {
|
||||||
case 'view_room':
|
case 'view_room':
|
||||||
this._viewRoom(payload);
|
this._viewRoom(payload);
|
||||||
break;
|
break;
|
||||||
|
case 'view_my_groups':
|
||||||
|
case 'view_group':
|
||||||
|
this._setState({
|
||||||
|
roomId: null,
|
||||||
|
roomAlias: null,
|
||||||
|
});
|
||||||
|
break;
|
||||||
case 'view_room_error':
|
case 'view_room_error':
|
||||||
this._viewRoomError(payload);
|
this._viewRoomError(payload);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -18,6 +18,7 @@ limitations under the License.
|
||||||
import MatrixClientPeg from '../MatrixClientPeg';
|
import MatrixClientPeg from '../MatrixClientPeg';
|
||||||
import {getAddressType} from '../UserAddress';
|
import {getAddressType} from '../UserAddress';
|
||||||
import {inviteToRoom} from '../RoomInvite';
|
import {inviteToRoom} from '../RoomInvite';
|
||||||
|
import GroupStoreCache from '../stores/GroupStoreCache';
|
||||||
import Promise from 'bluebird';
|
import Promise from 'bluebird';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -117,7 +118,9 @@ export default class MultiInviter {
|
||||||
|
|
||||||
let doInvite;
|
let doInvite;
|
||||||
if (this.groupId !== null) {
|
if (this.groupId !== null) {
|
||||||
doInvite = MatrixClientPeg.get().inviteUserToGroup(this.groupId, addr);
|
doInvite = GroupStoreCache
|
||||||
|
.getGroupStore(MatrixClientPeg.get(), this.groupId)
|
||||||
|
.inviteUserToGroup(addr);
|
||||||
} else {
|
} else {
|
||||||
doInvite = inviteToRoom(this.roomId, addr);
|
doInvite = inviteToRoom(this.roomId, addr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,6 +88,9 @@ describe('MemberEventListSummary', function() {
|
||||||
sandbox = testUtils.stubClient();
|
sandbox = testUtils.stubClient();
|
||||||
|
|
||||||
languageHandler.setLanguage('en').done(done);
|
languageHandler.setLanguage('en').done(done);
|
||||||
|
languageHandler.setMissingEntryGenerator(function(key) {
|
||||||
|
return key.split('|', 2)[1];
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function() {
|
||||||
|
|
Loading…
Reference in a new issue