Merge branch 'develop' into departify

This commit is contained in:
Stefan Parviainen 2017-10-27 19:23:13 +02:00
commit 95b2392104
17 changed files with 202 additions and 60 deletions

View file

@ -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)) {
// Start a new DM chat const rooms = _getDirectMessageRooms(addrTexts[0]);
createRoom({dmUserId: addrTexts[0]}).catch((err) => { if (rooms.length > 0) {
console.error(err.stack); // A Direct Message room already exists for this user, so select a
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); // room from a list that is similar to the one in MemberInfo panel
Modal.createTrackedDialog('Failed to invite user', '', ErrorDialog, { const ChatCreateOrReuseDialog = sdk.getComponent(
title: _t("Failed to invite user"), "views.dialogs.ChatCreateOrReuseDialog",
description: ((err && err.message) ? err.message : _t("Operation failed")), );
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
createRoom({dmUserId: addrTexts[0]}).catch((err) => {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Failed to invite user', '', ErrorDialog, {
title: _t("Failed to invite user"),
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;
}

View file

@ -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,
@ -964,13 +963,15 @@ export default React.createClass({
</AccessibleButton>, </AccessibleButton>,
); );
} else { } else {
rightButtons.push( if (summary.user && summary.user.membership === 'join') {
<AccessibleButton className="mx_GroupHeader_button" rightButtons.push(
onClick={this._onEditClick} title={_t("Community Settings")} key="_editButton" <AccessibleButton className="mx_GroupHeader_button"
> onClick={this._onEditClick} title={_t("Community Settings")} key="_editButton"
<TintableSvg src="img/icons-settings-room.svg" width="16" height="16" /> >
</AccessibleButton>, <TintableSvg src="img/icons-settings-room.svg" width="16" height="16" />
); </AccessibleButton>,
);
}
if (this.props.collapsedRhs) { if (this.props.collapsedRhs) {
rightButtons.push( rightButtons.push(
<AccessibleButton className="mx_GroupHeader_button" <AccessibleButton className="mx_GroupHeader_button"

View file

@ -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',
@ -1328,8 +1332,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}

View file

@ -302,7 +302,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,

View file

@ -174,7 +174,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 }

View file

@ -50,12 +50,9 @@ export default withMatrixClient(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._fetchMembers(); this._fetchMembers();
}); });
this._groupStore.on('error', (err) => {
console.error(err);
});
}, },
_fetchMembers: function() { _fetchMembers: function() {

View file

@ -47,16 +47,14 @@ 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() {

View file

@ -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>
); );

View file

@ -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;

View file

@ -133,8 +133,9 @@ 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"
onClick={this.props.onCancelClick} /> src="img/cancel.svg" width="18" height="18"
onClick={this.props.onCancelClick} />
</div> </div>
); );
}, },

View file

@ -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();
@ -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) {
@ -186,6 +189,10 @@ 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();
@ -615,6 +622,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
@ -639,22 +648,58 @@ 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>
); );
@ -760,9 +805,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 =
@ -790,9 +832,29 @@ module.exports = withMatrixClient(React.createClass({
presenceCurrentlyActive = this.props.member.user.currentlyActive; 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">
@ -808,16 +870,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() }

View file

@ -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>

View file

@ -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>
); );

View file

@ -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>

View file

@ -152,6 +152,7 @@
"%(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 %(count)s others are typing|other": "%(names)s and %(count)s others are typing", "%(names)s and %(count)s others are typing|other": "%(names)s and %(count)s others are typing",
"%(names)s and %(count)s others are typing|one": "%(names)s and one other is typing", "%(names)s and %(count)s others are typing|one": "%(names)s and one other is typing",
@ -200,8 +201,6 @@
"Authentication": "Authentication", "Authentication": "Authentication",
"Failed to delete device": "Failed to delete device", "Failed to delete device": "Failed to delete device",
"Delete": "Delete", "Delete": "Delete",
"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?",
"Disable Notifications": "Disable Notifications", "Disable Notifications": "Disable Notifications",
"Enable Notifications": "Enable Notifications", "Enable Notifications": "Enable Notifications",
"Cannot add any more widgets": "Cannot add any more widgets", "Cannot add any more widgets": "Cannot add any more widgets",
@ -245,6 +244,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",
@ -456,6 +456,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",
@ -500,10 +501,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",
@ -688,8 +692,8 @@
"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)",
@ -759,6 +763,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",

View file

@ -29,9 +29,10 @@ 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) => {
this._fetchMembers(); console.error(`GroupStore for ${this.groupId} encountered error`, err);
});
} }
_fetchMembers() { _fetchMembers() {
@ -51,6 +52,10 @@ export default class GroupStore extends EventEmitter {
}); });
this._notifyListeners(); this._notifyListeners();
}).catch((err) => { }).catch((err) => {
// Invited users not visible to non-members
if (err.httpStatus === 403) {
return;
}
console.error("Failed to get group invited member list: " + err); console.error("Failed to get group invited member list: " + err);
this.emit('error', err); this.emit('error', err);
}); });
@ -80,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;
} }

View file

@ -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() {