From 92d8b78bc001594f73d761a5dfc052e1d6d0c449 Mon Sep 17 00:00:00 2001 From: Martin Giger Date: Sun, 31 Oct 2021 12:48:28 +0200 Subject: [PATCH] Bug 1738442 - Mark Matrix users as voiced if they have elevated powers to send messages. r=clokep Differential Revision: https://phabricator.services.mozilla.com/D129938 --HG-- extra : amend_source : 3c6eb1ad4fbb1fb2252e9b42aabd7270c5c0cdae --- chat/protocols/matrix/matrix.jsm | 30 +++- chat/protocols/matrix/matrixCommands.jsm | 2 +- chat/protocols/matrix/matrixPowerLevels.jsm | 40 +++++ .../matrix/test/test_matrixPowerLevels.js | 139 +++++++++++++++++- 4 files changed, 200 insertions(+), 11 deletions(-) diff --git a/chat/protocols/matrix/matrix.jsm b/chat/protocols/matrix/matrix.jsm index 052077d3d5..3590f4d6ea 100644 --- a/chat/protocols/matrix/matrix.jsm +++ b/chat/protocols/matrix/matrix.jsm @@ -241,7 +241,23 @@ MatrixParticipant.prototype = { }, get voiced() { - //TODO this should require the power level specified in m.room.power_levels for m.room.message. + // If the default power level doesn't let you send messages, set voiced if + // the user can send messages + const room = this._account?._client?.getRoom(this._roomMember.roomId); + if (room) { + const powerLevels = room.currentState + .getStateEvents(EventType.RoomPowerLevels, "") + ?.getContent(); + const defaultLevel = MatrixPowerLevels.getUserDefaultLevel(powerLevels); + const messageLevel = MatrixPowerLevels.getEventLevel( + powerLevels, + EventType.RoomMessage + ); + if (defaultLevel < messageLevel) { + return room.currentState.maySendMessage(this._id); + } + } + // Else use a synthetic power level for the voiced flag return this._roomMember.powerLevelNorm >= MatrixPowerLevels.voice; }, get moderator() { @@ -885,7 +901,7 @@ MatrixRoom.prototype = { * field when the value changes. */ updateConvIcon() { - const avatarUrl = this.room.getAvatarUrl( + const avatarUrl = this.room?.getAvatarUrl( this._account._client.getHomeserverUrl(), USER_ICON_SIZE, USER_ICON_SIZE, @@ -1800,6 +1816,16 @@ MatrixAccount.prototype = { } }); + this._client.on("RoomState.members", (event, state, member) => { + if (this.roomList.has(state.roomId)) { + const conversation = this.roomList.get(state.roomId); + if (conversation.isChat) { + const participant = conversation._participants.get(member.userId); + conversation.notifyObservers(participant, "chat-buddy-update"); + } + } + }); + this._client.on("Session.logged_out", error => { this.prefs.clearUserPref("accessToken"); // https://spec.matrix.org/unstable/client-server-api/#soft-logout diff --git a/chat/protocols/matrix/matrixCommands.jsm b/chat/protocols/matrix/matrixCommands.jsm index 6c3917871a..07ac1fa0f4 100644 --- a/chat/protocols/matrix/matrixCommands.jsm +++ b/chat/protocols/matrix/matrixCommands.jsm @@ -164,7 +164,7 @@ function publishRoomDetails(account, conv) { system: true, }); - const defaultLevel = powerLevel.users_default; + const defaultLevel = MatrixPowerLevels.getUserDefaultLevel(powerLevel); for (let [key, value] of Object.entries(powerLevel)) { if (key == "users") { continue; diff --git a/chat/protocols/matrix/matrixPowerLevels.jsm b/chat/protocols/matrix/matrixPowerLevels.jsm index 801587b5a5..e5bc555868 100644 --- a/chat/protocols/matrix/matrixPowerLevels.jsm +++ b/chat/protocols/matrix/matrixPowerLevels.jsm @@ -40,4 +40,44 @@ var MatrixPowerLevels = { } return _("powerLevel.detailed", levelName, powerLevel); }, + /** + * @param {object} powerLevels - m.room.power_levels event contents. + * @param {string} key - Power level key to get. + * @returns {number} The power level if given in the event, else 0. + */ + _getDefaultLevel(powerLevels, key) { + const fullKey = `${key}_default`; + if (Number.isSafeInteger(powerLevels?.[fullKey])) { + return powerLevels[fullKey]; + } + return 0; + }, + /** + * @param {object} powerLevels - m.room.power_levels event contents. + * @returns {number} The default power level of users in the room. + */ + getUserDefaultLevel(powerLevels) { + return this._getDefaultLevel(powerLevels, "users"); + }, + /** + * + * @param {object} powerLevels - m.room.power_levels event contents. + * @returns {number} The default power level required to send events in the + * room. + */ + getEventDefaultLevel(powerLevels) { + return this._getDefaultLevel(powerLevels, "events"); + }, + /** + * + * @param {object} powerLevels - m.room.power_levels event contents. + * @param {string} event - Event ID to get the required power level for. + * @returns {number} The power level required to send this event in the room. + */ + getEventLevel(powerLevels, event) { + if (Number.isSafeInteger(powerLevels?.events?.[event])) { + return powerLevels.events[event]; + } + return this.getEventDefaultLevel(powerLevels); + }, }; diff --git a/chat/protocols/matrix/test/test_matrixPowerLevels.js b/chat/protocols/matrix/test/test_matrixPowerLevels.js index 983b10b306..b6ceb74ccd 100644 --- a/chat/protocols/matrix/test/test_matrixPowerLevels.js +++ b/chat/protocols/matrix/test/test_matrixPowerLevels.js @@ -4,14 +4,10 @@ var { MatrixPowerLevels } = ChromeUtils.import( "resource:///modules/matrixPowerLevels.jsm" ); +var { EventType } = ChromeUtils.import("resource:///modules/matrix-sdk.jsm"); var { l10nHelper } = ChromeUtils.import("resource:///modules/imXPCOMUtils.jsm"); var _ = l10nHelper("chrome://chat/locale/matrix.properties"); -function run_test() { - add_test(testToText); - run_next_test(); -} - const TO_TEXT_FIXTURES = [ { level: MatrixPowerLevels.user, @@ -66,8 +62,79 @@ const TO_TEXT_FIXTURES = [ name: "Custom power level 25", }, ]; +const GET_EVENT_LEVEL_FIXTURES = [ + { + powerLevels: undefined, + expected: 0, + }, + { + powerLevels: {}, + expected: 0, + }, + { + powerLevels: { + events_default: 10, + }, + expected: 10, + }, + { + powerLevels: { + events_default: Infinity, + }, + expected: 0, + }, + { + powerLevels: { + events_default: "foo", + }, + expected: 0, + }, + { + powerLevels: { + events_default: 0, + events: {}, + }, + expected: 0, + }, + { + powerLevels: { + events_default: 0, + events: { + [EventType.RoomMessage]: 0, + }, + }, + expected: 0, + }, + { + powerLevels: { + events_default: 0, + events: { + [EventType.RoomMessage]: Infinity, + }, + }, + expected: 0, + }, + { + powerLevels: { + events_default: 0, + events: { + [EventType.RoomMessage]: "foo", + }, + }, + expected: 0, + }, + { + powerLevels: { + events_default: 0, + events: { + [EventType.RoomMessage]: 10, + }, + }, + expected: 10, + }, +]; -function testToText() { +add_task(async function testToText() { for (const fixture of TO_TEXT_FIXTURES) { const result = MatrixPowerLevels.toText( fixture.level, @@ -75,6 +142,62 @@ function testToText() { ); equal(result, fixture.result); } +}); - run_next_test(); -} +add_task(async function testGetUserDefaultLevel() { + equal(MatrixPowerLevels.getUserDefaultLevel(), 0); + equal(MatrixPowerLevels.getUserDefaultLevel({}), 0); + equal( + MatrixPowerLevels.getUserDefaultLevel({ + users_default: 10, + }), + 10 + ); + equal( + MatrixPowerLevels.getUserDefaultLevel({ + users_default: Infinity, + }), + 0 + ); + equal( + MatrixPowerLevels.getUserDefaultLevel({ + users_default: "foo", + }), + 0 + ); +}); + +add_task(async function testGetEventDefaultLevel() { + equal(MatrixPowerLevels.getEventDefaultLevel(), 0); + equal(MatrixPowerLevels.getEventDefaultLevel({}), 0); + equal( + MatrixPowerLevels.getEventDefaultLevel({ + events_default: 10, + }), + 10 + ); + equal( + MatrixPowerLevels.getEventDefaultLevel({ + events_default: Infinity, + }), + 0 + ); + equal( + MatrixPowerLevels.getEventDefaultLevel({ + events_default: "foo", + }), + 0 + ); +}); + +add_task(async function testGetEventLevel() { + for (const eventLevelTest of GET_EVENT_LEVEL_FIXTURES) { + equal( + MatrixPowerLevels.getEventLevel( + eventLevelTest.powerLevels, + EventType.RoomMessage + ), + eventLevelTest.expected + ); + } +});