From 0336d99b9ec680219e956b382e599cbac52b4889 Mon Sep 17 00:00:00 2001
From: Matthew Hodgson <matthew@matrix.org>
Date: Fri, 13 Apr 2018 01:29:24 +0100
Subject: [PATCH] move VectorConferenceHandler over and use
 getUpdateCheckStatusEnum via field rather than export

---
 src/VectorConferenceHandler.js                | 138 ++++++++++++++++++
 .../views/globals/UpdateCheckBar.js           |  20 ++-
 2 files changed, 150 insertions(+), 8 deletions(-)
 create mode 100644 src/VectorConferenceHandler.js

diff --git a/src/VectorConferenceHandler.js b/src/VectorConferenceHandler.js
new file mode 100644
index 0000000000..19081726b2
--- /dev/null
+++ b/src/VectorConferenceHandler.js
@@ -0,0 +1,138 @@
+/*
+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.
+*/
+
+"use strict";
+
+import Promise from 'bluebird';
+var Matrix = require("matrix-js-sdk");
+var Room = Matrix.Room;
+var CallHandler = require('./CallHandler');
+
+// FIXME: this is Riot (Vector) specific code, but will be removed shortly when
+// we switch over to jitsi entirely for video conferencing.
+
+// FIXME: This currently forces Vector to try to hit the matrix.org AS for conferencing.
+// This is bad because it prevents people running their own ASes from being used.
+// This isn't permanent and will be customisable in the future: see the proposal
+// at docs/conferencing.md for more info.
+var USER_PREFIX = "fs_";
+var DOMAIN = "matrix.org";
+
+function ConferenceCall(matrixClient, groupChatRoomId) {
+    this.client = matrixClient;
+    this.groupRoomId = groupChatRoomId;
+    this.confUserId = module.exports.getConferenceUserIdForRoom(this.groupRoomId);
+}
+
+ConferenceCall.prototype.setup = function() {
+    var self = this;
+    return this._joinConferenceUser().then(function() {
+        return self._getConferenceUserRoom();
+    }).then(function(room) {
+        // return a call for *this* room to be placed. We also tack on
+        // confUserId to speed up lookups (else we'd need to loop every room
+        // looking for a 1:1 room with this conf user ID!)
+        var call = Matrix.createNewMatrixCall(self.client, room.roomId);
+        call.confUserId = self.confUserId;
+        call.groupRoomId = self.groupRoomId;
+        return call;
+    });
+};
+
+ConferenceCall.prototype._joinConferenceUser = function() {
+    // Make sure the conference user is in the group chat room
+    var groupRoom = this.client.getRoom(this.groupRoomId);
+    if (!groupRoom) {
+        return Promise.reject("Bad group room ID");
+    }
+    var member = groupRoom.getMember(this.confUserId);
+    if (member && member.membership === "join") {
+        return Promise.resolve();
+    }
+    return this.client.invite(this.groupRoomId, this.confUserId);
+};
+
+ConferenceCall.prototype._getConferenceUserRoom = function() {
+    // Use an existing 1:1 with the conference user; else make one
+    var rooms = this.client.getRooms();
+    var confRoom = null;
+    for (var i = 0; i < rooms.length; i++) {
+        var confUser = rooms[i].getMember(this.confUserId);
+        if (confUser && confUser.membership === "join" &&
+                rooms[i].getJoinedMembers().length === 2) {
+            confRoom = rooms[i];
+            break;
+        }
+    }
+    if (confRoom) {
+        return Promise.resolve(confRoom);
+    }
+    return this.client.createRoom({
+        preset: "private_chat",
+        invite: [this.confUserId]
+    }).then(function(res) {
+        return new Room(res.room_id);
+    });
+};
+
+/**
+ * Check if this user ID is in fact a conference bot.
+ * @param {string} userId The user ID to check.
+ * @return {boolean} True if it is a conference bot.
+ */
+module.exports.isConferenceUser = function(userId) {
+    if (userId.indexOf("@" + USER_PREFIX) !== 0) {
+        return false;
+    }
+    var base64part = userId.split(":")[0].substring(1 + USER_PREFIX.length);
+    if (base64part) {
+        var decoded = new Buffer(base64part, "base64").toString();
+        // ! $STUFF : $STUFF
+        return /^!.+:.+/.test(decoded);
+    }
+    return false;
+};
+
+module.exports.getConferenceUserIdForRoom = function(roomId) {
+    // abuse browserify's core node Buffer support (strip padding ='s)
+    var base64RoomId = new Buffer(roomId).toString("base64").replace(/=/g, "");
+    return "@" + USER_PREFIX + base64RoomId + ":" + DOMAIN;
+};
+
+module.exports.createNewMatrixCall = function(client, roomId) {
+    var confCall = new ConferenceCall(
+        client, roomId
+    );
+    return confCall.setup();
+};
+
+module.exports.getConferenceCallForRoom = function(roomId) {
+    // search for a conference 1:1 call for this group chat room ID
+    var activeCall = CallHandler.getAnyActiveCall();
+    if (activeCall && activeCall.confUserId) {
+        var thisRoomConfUserId = module.exports.getConferenceUserIdForRoom(
+            roomId
+        );
+        if (thisRoomConfUserId === activeCall.confUserId) {
+            return activeCall;
+        }
+    }
+    return null;
+};
+
+module.exports.ConferenceCall = ConferenceCall;
+
+module.exports.slot = 'conference';
diff --git a/src/components/views/globals/UpdateCheckBar.js b/src/components/views/globals/UpdateCheckBar.js
index b169054c5c..4397b3313e 100644
--- a/src/components/views/globals/UpdateCheckBar.js
+++ b/src/components/views/globals/UpdateCheckBar.js
@@ -19,14 +19,8 @@ limitations under the License.
 import React from 'react';
 import { _t } from '../../../languageHandler';
 import PlatformPeg from '../../../PlatformPeg';
-import {updateCheckStatusEnum} from '../../../vector/platform/VectorBasePlatform';
 import AccessibleButton from '../../../components/views/elements/AccessibleButton';
 
-const doneStatuses = [
-    updateCheckStatusEnum.ERROR,
-    updateCheckStatusEnum.NOTAVAILABLE,
-];
-
 export default React.createClass({
     propTypes: {
         status: React.PropTypes.oneOf(Object.values(updateCheckStatusEnum)).isRequired,
@@ -42,6 +36,7 @@ export default React.createClass({
     },
 
     getStatusText: function() {
+        const updateCheckStatusEnum = PlatformPeg.get().getUpdateCheckStatusEnum();
         switch(this.props.status) {
             case updateCheckStatusEnum.ERROR:
                 return _t('Error encountered (%(errorDetail)s).', { errorDetail: this.props.detail });
@@ -52,8 +47,7 @@ export default React.createClass({
             case updateCheckStatusEnum.DOWNLOADING:
                 return _t('Downloading update...');
         }
-    }
-    ,
+    },
 
     hideToolbar: function() {
         PlatformPeg.get().stopUpdateCheck();
@@ -63,6 +57,16 @@ export default React.createClass({
         const message = this.getStatusText();
         const warning = _t('Warning');
 
+        if (!'getUpdateCheckStatusEnum' in PlatformPeg.get()) {
+            return <div></div>;
+        }
+
+        const updateCheckStatusEnum = PlatformPeg.get().getUpdateCheckStatusEnum();
+        const doneStatuses = [
+            updateCheckStatusEnum.ERROR,
+            updateCheckStatusEnum.NOTAVAILABLE,
+        ];
+
         let image;
         if (doneStatuses.includes(this.props.status)) {
             image = <img className="mx_MatrixToolbar_warning" src="img/warning.svg" width="24" height="23" alt={warning}/>;