diff --git a/src/CallHandler.js b/src/CallHandler.js index 4c68718709..c2ee05b22c 100644 --- a/src/CallHandler.js +++ b/src/CallHandler.js @@ -276,6 +276,7 @@ function _onAction(payload) { ).done(function(call) { placeCall(call); }, function(err) { + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); Modal.createDialog(ErrorDialog, { title: "Failed to set up conference call", description: "Conference call failed: " + err, diff --git a/src/MatrixTools.js b/src/MatrixTools.js deleted file mode 100644 index 3bc7f28e20..0000000000 --- a/src/MatrixTools.js +++ /dev/null @@ -1,58 +0,0 @@ -/* -Copyright 2015, 2016 OpenMarket Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -var CallHandler = require('./CallHandler'); - -module.exports = { - /** - * Given a room object, return the alias we should use for it, - * if any. This could be the canonical alias if one exists, otherwise - * an alias selected arbitrarily but deterministically from the list - * of aliases. Otherwise return null; - */ - getDisplayAliasForRoom: function(room) { - return room.getCanonicalAlias() || room.getAliases()[0]; - }, - - isDirectMessageRoom: function(room, me, ConferenceHandler, hideConferenceChans) { - if (me.membership == "join" || me.membership === "ban" || - (me.membership === "leave" && me.events.member.getSender() !== me.events.member.getStateKey())) - { - // Used to split rooms via tags - var tagNames = Object.keys(room.tags); - // Used for 1:1 direct chats - var joinedMembers = room.getJoinedMembers(); - - // Show 1:1 chats in seperate "Direct Messages" section as long as they haven't - // been moved to a different tag section - if (joinedMembers.length === 2 && !tagNames.length) { - var otherMember = joinedMembers.filter(function(m) { - return m.userId !== me.userId - })[0]; - - if (ConferenceHandler && ConferenceHandler.isConferenceUser(otherMember.userId)) { - // console.log("Hiding conference 1:1 room %s", room.roomId); - if (!hideConferenceChans) { - return true; - } - } else { - return true; - } - } - } - return false; - }, -} - diff --git a/src/Rooms.js b/src/Rooms.js new file mode 100644 index 0000000000..7f4564b439 --- /dev/null +++ b/src/Rooms.js @@ -0,0 +1,77 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + + +/** + * Given a room object, return the alias we should use for it, + * if any. This could be the canonical alias if one exists, otherwise + * an alias selected arbitrarily but deterministically from the list + * of aliases. Otherwise return null; + */ +export function getDisplayAliasForRoom(room) { + return room.getCanonicalAlias() || room.getAliases()[0]; +} + +/** + * If the room contains only two members including the logged-in user, + * return the other one. Otherwise, return null. + */ +export function getOnlyOtherMember(room, me) { + const joinedMembers = room.getJoinedMembers(); + + if (joinedMembers.length === 2) { + return joinedMembers.filter(function(m) { + return m.userId !== me.userId + })[0]; + } + + return null; +} + +export function isConfCallRoom(room, me, conferenceHandler) { + if (!conferenceHandler) return false; + + if (me.membership != "join") { + return false; + } + + const otherMember = getOnlyOtherMember(room, me); + if (otherMember === null) { + return false; + } + + if (conferenceHandler.isConferenceUser(otherMember.userId)) { + return true; + } +} + +export function looksLikeDirectMessageRoom(room, me) { + if (me.membership == "join" || me.membership === "ban" || + (me.membership === "leave" && me.events.member.getSender() !== me.events.member.getStateKey())) + { + // Used to split rooms via tags + const tagNames = Object.keys(room.tags); + // Used for 1:1 direct chats + const joinedMembers = room.getJoinedMembers(); + + // Show 1:1 chats in seperate "Direct Messages" section as long as they haven't + // been moved to a different tag section + if (joinedMembers.length === 2 && !tagNames.length) { + return true; + } + } + return false; +} diff --git a/src/SlashCommands.js b/src/SlashCommands.js index 759a95c8ff..be007496dd 100644 --- a/src/SlashCommands.js +++ b/src/SlashCommands.js @@ -15,7 +15,6 @@ limitations under the License. */ var MatrixClientPeg = require("./MatrixClientPeg"); -var MatrixTools = require("./MatrixTools"); var dis = require("./dispatcher"); var Tinter = require("./Tinter"); diff --git a/src/autocomplete/RoomProvider.js b/src/autocomplete/RoomProvider.js index 39cf1179d7..ac7f1b418a 100644 --- a/src/autocomplete/RoomProvider.js +++ b/src/autocomplete/RoomProvider.js @@ -4,7 +4,7 @@ import Q from 'q'; import MatrixClientPeg from '../MatrixClientPeg'; import Fuse from 'fuse.js'; import {PillCompletion} from './Components'; -import {getDisplayAliasForRoom} from '../MatrixTools'; +import {getDisplayAliasForRoom} from '../Rooms'; import sdk from '../index'; const ROOM_REGEX = /(?=#)([^\s]*)/g; diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 48431a956b..c83da2b8f0 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -36,7 +36,7 @@ var PostRegistration = require("./login/PostRegistration"); var Modal = require("../../Modal"); var Tinter = require("../../Tinter"); var sdk = require('../../index'); -var MatrixTools = require('../../MatrixTools'); +var Rooms = require('../../Rooms'); var linkifyMatrix = require("../../linkify-matrix"); var KeyCode = require('../../KeyCode'); var Lifecycle = require('../../Lifecycle'); @@ -482,7 +482,7 @@ module.exports = React.createClass({ var presentedId = room_info.room_alias || room_info.room_id; var room = MatrixClientPeg.get().getRoom(room_info.room_id); if (room) { - var theAlias = MatrixTools.getDisplayAliasForRoom(room); + var theAlias = Rooms.getDisplayAliasForRoom(room); if (theAlias) presentedId = theAlias; // No need to do this given RoomView triggers it itself... @@ -602,7 +602,7 @@ module.exports = React.createClass({ var presentedId = self.state.currentRoomId; var room = MatrixClientPeg.get().getRoom(self.state.currentRoomId); if (room) { - var theAlias = MatrixTools.getDisplayAliasForRoom(room); + var theAlias = Rooms.getDisplayAliasForRoom(room); if (theAlias) presentedId = theAlias; } diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 3d0bcf7445..041493d420 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -36,7 +36,6 @@ var dis = require("../../dispatcher"); var Tinter = require("../../Tinter"); var rate_limited_func = require('../../ratelimitedfunc'); var ObjectUtils = require('../../ObjectUtils'); -var MatrixTools = require('../../MatrixTools'); import UserProvider from '../../autocomplete/UserProvider'; diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index 91a32f51ac..8b439fcfd7 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -25,7 +25,8 @@ var Unread = require('../../../Unread'); var dis = require("../../../dispatcher"); var sdk = require('../../../index'); var rate_limited_func = require('../../../ratelimitedfunc'); -var MatrixTools = require('../../../MatrixTools'); +var Rooms = require('../../../Rooms'); +var DMRoomMap = require('../../../utils/DMRoomMap'); var HIDE_CONFERENCE_CHANS = true; @@ -207,8 +208,10 @@ module.exports = React.createClass({ s.lists["m.lowpriority"] = []; s.lists["im.vector.fake.archived"] = []; + const dmRoomMap = new DMRoomMap(MatrixClientPeg.get()); + MatrixClientPeg.get().getRooms().forEach(function(room) { - var me = room.getMember(MatrixClientPeg.get().credentials.userId); + const me = room.getMember(MatrixClientPeg.get().credentials.userId); if (!me) return; // console.log("room = " + room.name + ", me.membership = " + me.membership + @@ -219,7 +222,10 @@ module.exports = React.createClass({ if (me.membership == "invite") { s.lists["im.vector.fake.invite"].push(room); } - else if (MatrixTools.isDirectMessageRoom(room, me, self.props.ConferenceHandler, HIDE_CONFERENCE_CHANS)) { + else if (HIDE_CONFERENCE_CHANS && Rooms.isConfCallRoom(room, me, self.props.ConferenceHandler)) { + // skip past this room & don't put it in any lists + } + else if (dmRoomMap.getUserIdForRoomId(room.roomId)) { // "Direct Message" rooms s.lists["im.vector.fake.direct"].push(room); } @@ -248,6 +254,38 @@ module.exports = React.createClass({ } }); + if (s.lists["im.vector.fake.direct"].length == 0 && MatrixClientPeg.get().getAccountData('m.direct') === undefined) { + // scan through the 'recents' list for any rooms which look like DM rooms + // and make them DM rooms + const oldRecents = s.lists["im.vector.fake.recent"]; + s.lists["im.vector.fake.recent"] = []; + + for (const room of oldRecents) { + const me = room.getMember(MatrixClientPeg.get().credentials.userId); + + if (me && Rooms.looksLikeDirectMessageRoom(room, me)) { + s.lists["im.vector.fake.direct"].push(room); + } else { + s.lists["im.vector.fake.recent"].push(room); + } + } + + // save these new guessed DM rooms into the account data + const newMDirectEvent = {}; + for (const room of s.lists["im.vector.fake.direct"]) { + const me = room.getMember(MatrixClientPeg.get().credentials.userId); + const otherPerson = Rooms.getOnlyOtherMember(room, me); + if (!otherPerson) continue; + + const roomList = newMDirectEvent[otherPerson.userId] || []; + roomList.push(room.roomId); + newMDirectEvent[otherPerson.userId] = roomList; + } + + // if this fails, fine, we'll just do the same thing next time we get the room lists + MatrixClientPeg.get().setAccountData('m.direct', newMDirectEvent).done(); + } + //console.log("calculated new roomLists; im.vector.fake.recent = " + s.lists["im.vector.fake.recent"]); // we actually apply the sorting to this when receiving the prop in RoomSubLists. diff --git a/src/utils/DMRoomMap.js b/src/utils/DMRoomMap.js new file mode 100644 index 0000000000..d92ae87e64 --- /dev/null +++ b/src/utils/DMRoomMap.js @@ -0,0 +1,46 @@ +/* +Copyright 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/** + * Class that takes a Matrix Client and flips the m.direct map + * so the operation of mapping a room ID to which user it's a DM + * with can be performed efficiently. + */ +export default class DMRoomMap { + constructor(matrixClient) { + const mDirectEvent = matrixClient.getAccountData('m.direct'); + if (!mDirectEvent) { + this.userToRooms = {}; + this.roomToUser = {}; + } else { + this.userToRooms = mDirectEvent.getContent(); + this.roomToUser = {}; + for (const user of Object.keys(this.userToRooms)) { + for (const roomId of this.userToRooms[user]) { + this.roomToUser[roomId] = user; + } + } + } + } + + getDMRoomsForUserId(userId) { + return this.userToRooms[userId]; + } + + getUserIdForRoomId(roomId) { + return this.roomToUser[roomId]; + } +}