diff --git a/lib/src/Room.dart b/lib/src/Room.dart index ee37614..630aa49 100644 --- a/lib/src/Room.dart +++ b/lib/src/Room.dart @@ -73,8 +73,12 @@ class Room { return canonicalAlias.substring(1, canonicalAlias.length).split(":")[0]; if (mHeroes.length > 0) { String displayname = ""; - for (int i = 0; i < mHeroes.length; i++) - displayname += User(senderId: mHeroes[i]).calcDisplayname() + ", "; + for (int i = 0; i < mHeroes.length; i++) { + User hero = states[mHeroes[i]] != null + ? states[mHeroes[i]].asUser + : User(stateKey: mHeroes[i]); + displayname += hero.calcDisplayname() + ", "; + } return displayname.substring(0, displayname.length - 2); } return "Empty chat"; @@ -88,9 +92,9 @@ class Room { /// The avatar of the room if set by a participant. MxContent get avatar { if (states["m.room.avatar"] != null) - return MxContent(states["m.room.avatar"].content["avatar_url"]); + return MxContent(states["m.room.avatar"].content["url"]); if (mHeroes.length == 1 && states[mHeroes[0]] != null) - return (states[mHeroes[0]] as User).avatarUrl; + return states[mHeroes[0]].asUser.avatarUrl; return MxContent(""); } @@ -307,10 +311,10 @@ class Room { return res; } - /// Call the Matrix API to unban a banned user from this room. + /// Set the power level of the user with the [userID] to the value [power]. Future setPower(String userID, int power) async { + if (states["m.room.power_levels"] == null) return null; Map powerMap = states["m.room.power_levels"].content["users"]; - if (powerMap == null) return null; powerMap[userID] = power; dynamic res = await client.connection.jsonRequest( @@ -378,7 +382,7 @@ class Room { /// Sets this room as a direct chat for this user. Future addToDirectChat(String userID) async { - Map> directChats = client.directChats; + Map directChats = client.directChats; if (directChats.containsKey(userID)) if (!directChats[userID].contains(id)) directChats[userID].add(id); else @@ -413,6 +417,8 @@ class Room { Future>> roomAccountData}) async { Room newRoom = Room( id: row["id"], + membership: Membership.values + .firstWhere((e) => e.toString() == 'Membership.' + row["membership"]), notificationCount: row["notification_count"], highlightCount: row["highlight_count"], notificationSettings: row["notification_settings"], @@ -421,6 +427,8 @@ class Room { mJoinedMemberCount: row["joined_member_count"], mHeroes: row["heroes"]?.split(",") ?? [], client: matrix, + states: {}, + roomAccountData: {}, ); Map newStates = {}; @@ -463,10 +471,12 @@ class Room { /// Load all events for a given room from the store. This includes all /// senders of those events, who will be added to the participants list. Future> loadEvents() async { - return await client.store.getEventList(this); + if (client.store != null) return await client.store.getEventList(this); + return []; } /// Load all participants for a given room from the store. + @deprecated Future> loadParticipants() async { return await client.store.loadParticipants(this); } @@ -490,14 +500,14 @@ class Room { } Future getUserByMXID(String mxID) async { - if (states[mxID] != null) return states[mxID] as User; + if (states[mxID] != null) return states[mxID].asUser; final dynamic resp = await client.connection.jsonRequest( type: HTTPType.GET, action: "/client/r0/rooms/$id/state/m.room.member/$mxID"); if (resp is ErrorResponse) return null; // Somehow we miss the mxid in the response and only get the content of the event. resp["matrix_id"] = mxID; - return State.fromJson(resp, this) as User; + return State.fromJson(resp, this).asUser; } /// Searches for the event in the store. If it isn't found, try to request it @@ -514,18 +524,21 @@ class Room { } /// Returns the user's own power level. - int get ownPowerLevel { + int getPowerLevelByUserId(String userId) { int powerLevel = 0; State powerLevelState = states["m.room.power_levels"]; if (powerLevelState == null) return powerLevel; if (powerLevelState.content["users_default"] is int) powerLevel = powerLevelState.content["users_default"]; - if (powerLevelState.content["users"] is Map && - powerLevelState.content["users"][client.userID] != null) - powerLevel = powerLevelState.content["users"][client.userID]; + if (powerLevelState.content["users"] is Map && + powerLevelState.content["users"][userId] != null) + powerLevel = powerLevelState.content["users"][userId]; return powerLevel; } + /// Returns the user's own power level. + int get ownPowerLevel => getPowerLevelByUserId(client.userID); + /// Returns the power levels from all users for this room or null if not given. Map get powerLevels { State powerLevelState = states["m.room.power_levels"]; diff --git a/lib/src/Store.dart b/lib/src/Store.dart index 5a2bb9b..870e0f0 100644 --- a/lib/src/Store.dart +++ b/lib/src/Store.dart @@ -300,7 +300,7 @@ class Store { "SELECT * FROM States WHERE state_key=? AND room_id=?", [matrixID, room.id]); if (res.length != 1) return null; - return State.fromJson(res[0], room) as User; + return State.fromJson(res[0], room).asUser; } /// Loads all Users in the database to provide a contact list @@ -311,8 +311,7 @@ class Store { [client.userID, exceptRoomID]); List userList = []; for (int i = 0; i < res.length; i++) - userList - .add(State.fromJson(res[i], Room(id: "", client: client)) as User); + userList.add(State.fromJson(res[i], Room(id: "", client: client)).asUser); return userList; } @@ -328,7 +327,7 @@ class Store { List participants = []; for (num i = 0; i < res.length; i++) { - participants.add(State.fromJson(res[i], room) as User); + participants.add(State.fromJson(res[i], room).asUser); } return participants; diff --git a/lib/src/User.dart b/lib/src/User.dart index 835a982..e664bd0 100644 --- a/lib/src/User.dart +++ b/lib/src/User.dart @@ -60,7 +60,7 @@ class User extends State { String get id => stateKey; /// The displayname of the user if the user has set one. - String get displayName => content["displayname"]; + String get displayName => content != null ? content["displayname"] : null; /// The membership status of the user. One of: /// join @@ -75,12 +75,14 @@ class User extends State { }); /// The avatar if the user has one. - MxContent avatarUrl; + MxContent get avatarUrl => content != null && content["avatar_url"] is String + ? MxContent(content["avatar_url"]) + : MxContent(""); /// Returns the displayname or the local part of the Matrix ID if the user /// has no displayname. String calcDisplayname() => (displayName == null || displayName.isEmpty) - ? id.replaceFirst("@", "").split(":")[0] + ? stateKey.replaceFirst("@", "").split(":")[0] : displayName; /// Call the Matrix API to kick this user from this room. diff --git a/test/FakeMatrixApi.dart b/test/FakeMatrixApi.dart index f63f445..98a2ee5 100644 --- a/test/FakeMatrixApi.dart +++ b/test/FakeMatrixApi.dart @@ -64,6 +64,20 @@ class FakeMatrixApi extends MockClient { static final Map> api = { "GET": { + "/client/r0/rooms/!localpart:server.abc/state/m.room.member/@getme:example.com": + (var req) => { + "content": { + "membership": "join", + "displayname": "You got me", + }, + "type": "m.room.member", + "event_id": "143273582443PhrSn:example.org", + "room_id": "!localpart:server.abc", + "sender": "@getme:example.com", + "state_key": "@getme:example.com", + "origin_server_ts": 1432735824653, + "unsigned": {"age": 1234} + }, "/client/r0/rooms/!localpart:server.abc/event/1234": (var req) => { "content": { "body": "This is an example text message", @@ -503,12 +517,30 @@ class FakeMatrixApi extends MockClient { "room_id": "!1234:fakeServer.notExisting", }, "/client/r0/rooms/!localpart:server.abc/read_markers": (var reqI) => {}, + "/client/r0/rooms/!localpart:server.abc/kick": (var reqI) => {}, + "/client/r0/rooms/!localpart:server.abc/ban": (var reqI) => {}, + "/client/r0/rooms/!localpart:server.abc/unban": (var reqI) => {}, + "/client/r0/rooms/!localpart:server.abc/invite": (var reqI) => {}, }, "PUT": { "/client/r0/rooms/!1234:example.com/send/m.room.message/1234": (var reqI) => { "event_id": "42", }, + "/client/r0/rooms/!localpart:server.abc/state/m.room.name": (var reqI) => + { + "event_id": "42", + }, + "/client/r0/rooms/!localpart:server.abc/state/m.room.topic": (var reqI) => + { + "event_id": "42", + }, + "/client/r0/rooms/!localpart:server.abc/state/m.room.power_levels": + (var reqI) => { + "event_id": "42", + }, + "/client/r0/user/@test:fakeServer.notExisting/account_data/m.direct": + (var reqI) => {}, }, "DELETE": { "/unknown/token": (var req) => {"errcode": "M_UNKNOWN_TOKEN"}, diff --git a/test/Room_test.dart b/test/Room_test.dart index 897d119..066b575 100644 --- a/test/Room_test.dart +++ b/test/Room_test.dart @@ -24,7 +24,10 @@ import 'package:famedlysdk/src/Client.dart'; import 'package:famedlysdk/src/Event.dart'; import 'package:famedlysdk/src/Room.dart'; +import 'package:famedlysdk/src/State.dart'; +import 'package:famedlysdk/src/Timeline.dart'; import 'package:famedlysdk/src/User.dart'; +import 'package:famedlysdk/src/utils/ChatTime.dart'; import 'package:flutter_test/flutter_test.dart'; import 'FakeMatrixApi.dart'; @@ -50,24 +53,9 @@ void main() { test("Create from json", () async { final String id = "!localpart:server.abc"; - final String name = "My Room"; final Membership membership = Membership.join; - final String topic = "This is my own room"; - final int unread = DateTime.now().millisecondsSinceEpoch; final int notificationCount = 2; final int highlightCount = 1; - final String fullyRead = "fjh82jdjifd:server.abc"; - final String notificationSettings = "all"; - final String guestAccess = "forbidden"; - final String canonicalAlias = "#testroom:example.com"; - final String historyVisibility = "invite"; - final String joinRules = "invite"; - final int now = DateTime.now().millisecondsSinceEpoch; - final String msgtype = "m.text"; - final String body = "Hello World"; - final String formatted_body = "Hello World"; - final String contentJson = - '{"msgtype":"$msgtype","body":"$body","formatted_body":"$formatted_body"}'; final List heroes = [ "@alice:matrix.org", "@bob:example.com", @@ -77,35 +65,10 @@ void main() { Map jsonObj = { "id": id, "membership": membership.toString().split('.').last, - "topic": name, - "description": topic, "avatar_url": "", "notification_count": notificationCount, "highlight_count": highlightCount, - "unread": unread, - "fully_read": fullyRead, - "notification_settings": notificationSettings, - "direct_chat_matrix_id": "", - "draft": "", "prev_batch": "", - "guest_access": guestAccess, - "history_visibility": historyVisibility, - "join_rules": joinRules, - "canonical_alias": canonicalAlias, - "power_events_default": 0, - "power_state_default": 0, - "power_redact": 0, - "power_invite": 0, - "power_ban": 0, - "power_kick": 0, - "power_user_default": 0, - "power_event_avatar": 0, - "power_event_history_visibility": 0, - "power_event_canonical_alias": 0, - "power_event_aliases": 0, - "power_event_name": 0, - "power_event_power_levels": 0, - "content_json": contentJson, "joined_member_count": notificationCount, "invited_member_count": notificationCount, "heroes": heroes.join(","), @@ -115,33 +78,69 @@ void main() { expect(room.id, id); expect(room.membership, membership); - expect(room.name, name); - expect(room.displayname, name); - expect(room.topic, topic); - expect(room.avatar.mxc, ""); expect(room.notificationCount, notificationCount); expect(room.highlightCount, highlightCount); - expect(room.unread.toTimeStamp(), unread); - expect(room.fullyRead, fullyRead); - expect(room.notificationSettings, notificationSettings); - expect(room.directChatMatrixID, ""); - expect(room.canonicalAlias, canonicalAlias); - expect(room.prev_batch, ""); - expect(room.lastMessage, body); - expect(room.timeCreated.toTimeStamp() >= now, true); - room.powerLevels.forEach((String key, int value) { - expect(value, 0); - }); expect(room.mJoinedMemberCount, notificationCount); expect(room.mInvitedMemberCount, notificationCount); expect(room.mHeroes, heroes); - - jsonObj["topic"] = ""; - room = await Room.getRoomFromTableRow(jsonObj, matrix); - expect(room.displayname, "testroom"); - jsonObj["canonical_alias"] = ""; - room = await Room.getRoomFromTableRow(jsonObj, matrix); expect(room.displayname, "alice, bob, charley"); + + room.states["m.room.canonical_alias"] = State( + senderId: "@test:example.com", + typeKey: "m.room.canonical_alias", + roomId: room.id, + room: room, + eventId: "123", + content: {"alias": "#testalias:example.com"}, + stateKey: ""); + expect(room.displayname, "testalias"); + expect(room.canonicalAlias, "#testalias:example.com"); + + room.states["m.room.name"] = State( + senderId: "@test:example.com", + typeKey: "m.room.name", + roomId: room.id, + room: room, + eventId: "123", + content: {"name": "testname"}, + stateKey: ""); + expect(room.displayname, "testname"); + + expect(room.topic, ""); + room.states["m.room.topic"] = State( + senderId: "@test:example.com", + typeKey: "m.room.topic", + roomId: room.id, + room: room, + eventId: "123", + content: {"topic": "testtopic"}, + stateKey: ""); + expect(room.topic, "testtopic"); + + expect(room.avatar.mxc, ""); + room.states["m.room.avatar"] = State( + senderId: "@test:example.com", + typeKey: "m.room.avatar", + roomId: room.id, + room: room, + eventId: "123", + content: {"url": "mxc://testurl"}, + stateKey: ""); + expect(room.avatar.mxc, "mxc://testurl"); + + expect(room.lastEvent, null); + room.states["m.room.message"] = State( + senderId: "@test:example.com", + typeKey: "m.room.message", + roomId: room.id, + room: room, + eventId: "12345", + time: ChatTime.now(), + content: {"msgtype": "m.text", "body": "test"}, + stateKey: ""); + expect(room.lastEvent.eventId, "12345"); + expect(room.lastMessage, "test"); + expect(room.timeCreated, room.lastEvent.time); }); test("sendReadReceipt", () async { @@ -165,5 +164,86 @@ void main() { final Event event = await room.getEventById("1234"); expect(event.eventId, "143273582443PhrSn:example.org"); }); + + test("setName", () async { + final dynamic resp = await room.setName("Testname"); + expect(resp["event_id"], "42"); + }); + + test("setDescription", () async { + final dynamic resp = await room.setDescription("Testname"); + expect(resp["event_id"], "42"); + }); + + test("kick", () async { + final dynamic resp = await room.kick("Testname"); + expect(resp, {}); + }); + + test("ban", () async { + final dynamic resp = await room.ban("Testname"); + expect(resp, {}); + }); + + test("unban", () async { + final dynamic resp = await room.unban("Testname"); + expect(resp, {}); + }); + + test("PowerLevels", () async { + room.states["m.room.power_levels"] = State( + senderId: "@test:example.com", + typeKey: "m.room.power_levels", + roomId: room.id, + room: room, + eventId: "123", + content: { + "ban": 50, + "events": {"m.room.name": 100, "m.room.power_levels": 100}, + "events_default": 0, + "invite": 50, + "kick": 50, + "notifications": {"room": 20}, + "redact": 50, + "state_default": 50, + "users": {"@test:fakeServer.notExisting": 100}, + "users_default": 10 + }, + stateKey: ""); + expect(room.ownPowerLevel, 100); + expect(room.getPowerLevelByUserId(matrix.userID), room.ownPowerLevel); + expect(room.getPowerLevelByUserId("@nouser:example.com"), 10); + expect(room.powerLevels, + room.states["m.room.power_levels"].content["users"]); + final dynamic resp = + await room.setPower("@test:fakeServer.notExisting", 90); + expect(resp["event_id"], "42"); + }); + + test("invite", () async { + final dynamic resp = await room.invite("Testname"); + expect(resp, {}); + }); + + test("addToDirectChat", () async { + final dynamic resp = await room.addToDirectChat("Testname"); + expect(resp, {}); + }); + }); + + test("getTimeline", () async { + final Timeline timeline = await room.getTimeline(); + expect(timeline.events, []); + }); + + test("loadEvents", () async { + final List events = await room.loadEvents(); + expect(events, []); + }); + + test("getUserByMXID", () async { + final User user = await room.getUserByMXID("@getme:example.com"); + expect(user.stateKey, "@getme:example.com"); + expect(user.calcDisplayname(), "You got me"); }); }