diff --git a/lib/famedlysdk.dart b/lib/famedlysdk.dart index a365d6d..8288e51 100644 --- a/lib/famedlysdk.dart +++ b/lib/famedlysdk.dart @@ -32,6 +32,7 @@ export 'package:famedlysdk/src/sync/UserUpdate.dart'; export 'package:famedlysdk/src/utils/ChatTime.dart'; export 'package:famedlysdk/src/utils/MatrixFile.dart'; export 'package:famedlysdk/src/utils/MxContent.dart'; +export 'package:famedlysdk/src/utils/StatesMap.dart'; export 'package:famedlysdk/src/AccountData.dart'; export 'package:famedlysdk/src/Client.dart'; export 'package:famedlysdk/src/Connection.dart'; diff --git a/lib/src/Room.dart b/lib/src/Room.dart index 40338b1..bd27bf2 100644 --- a/lib/src/Room.dart +++ b/lib/src/Room.dart @@ -37,6 +37,7 @@ import 'package:mime_type/mime_type.dart'; import './User.dart'; import 'Connection.dart'; import 'Timeline.dart'; +import 'utils/StatesMap.dart'; typedef onRoomUpdate = void Function(); @@ -67,8 +68,7 @@ class Room { /// The number of users with membership of invite. int mInvitedMemberCount; - /// Key-Value store for room states. - Map states = {}; + StatesMap states = StatesMap(); /// Key-Value store for ephemerals. Map ephemerals = {}; @@ -155,18 +155,16 @@ class Room { Event get lastEvent { ChatTime lastTime = ChatTime(0); - Event lastEvent = null; - for (final entry in states.entries) { - final RoomState state = entry.value; - if ((state.time != null && state.time > lastTime) || - state.typeKey == "m.room.message") { - lastTime = state.time; - lastEvent = state.timelineEvent; - if (state.typeKey == "m.room.message") { - break; + Event lastEvent = states["m.room.message"]?.timelineEvent; + if (lastEvent == null) + states.forEach((final String key, final entry) { + if (!entry.containsKey("")) return; + final RoomState state = entry[""]; + if (state.time != null && state.time > lastTime) { + lastTime = state.time; + lastEvent = state.timelineEvent; } - } - } + }); return lastEvent; } @@ -194,7 +192,6 @@ class Room { this.mHeroes = const [], this.mInvitedMemberCount = 0, this.mJoinedMemberCount = 0, - this.states = const {}, this.roomAccountData = const {}, }); @@ -624,7 +621,6 @@ class Room { mJoinedMemberCount: row["joined_member_count"], mHeroes: row["heroes"]?.split(",") ?? [], client: matrix, - states: {}, roomAccountData: {}, ); @@ -635,7 +631,8 @@ class Room { RoomState newState = RoomState.fromJson(rawStates[i], newRoom); newStates[newState.key] = newState; } - newRoom.states = newStates; + for (var entry in newStates.entries) + newRoom.states[entry.key] = entry.value; } Map newRoomAccountData = {}; @@ -682,9 +679,13 @@ class Room { /// case. List getParticipants() { List userList = []; - for (var entry in states.entries) - if (entry.value.type == EventTypes.RoomMember) - userList.add(entry.value.asUser); + if (states["m.room.member"] is Map) { + print('Check members: ${states["m.room.member"].length}'); + for (var entry in states["m.room.member"].entries) { + RoomState state = entry.value; + if (state.type == EventTypes.RoomMember) userList.add(state.asUser); + } + } return userList; } diff --git a/lib/src/RoomList.dart b/lib/src/RoomList.dart index 9882eb6..d04f869 100644 --- a/lib/src/RoomList.dart +++ b/lib/src/RoomList.dart @@ -130,7 +130,6 @@ class RoomList { mHeroes: chatUpdate.summary?.mHeroes, mJoinedMemberCount: chatUpdate.summary?.mJoinedMemberCount, mInvitedMemberCount: chatUpdate.summary?.mInvitedMemberCount, - states: {}, roomAccountData: {}, client: client, ); diff --git a/lib/src/utils/StatesMap.dart b/lib/src/utils/StatesMap.dart new file mode 100644 index 0000000..cfc57c6 --- /dev/null +++ b/lib/src/utils/StatesMap.dart @@ -0,0 +1,36 @@ +import 'package:famedlysdk/famedlysdk.dart'; + +/// Matrix room states are addressed by a tuple of the [type] and an +/// optional [stateKey]. +class StatesMap { + Map> states = {}; + + /// Returns either the [RoomState] or a map of state_keys to [RoomState] objects. + /// If you just enter a MatrixID, it will try to return the corresponding m.room.member event. + dynamic operator [](String key) { + if (key.startsWith("@") && key.contains(":")) { + if (!states.containsKey("m.room.member")) states["m.room.member"] = {}; + return states["m.room.member"][key]; + } + if (!states.containsKey(key)) states[key] = {}; + if (states[key][""] is RoomState) + return states[key][""]; + else if (states[key].length == 0) + return null; + else + return states[key]; + } + + void operator []=(String key, RoomState val) { + if (key.startsWith("@") && key.contains(":")) { + if (!states.containsKey("m.room.member")) states["m.room.member"] = {}; + states["m.room.member"][key] = val; + } + if (!states.containsKey(key)) states[key] = {}; + states[key][val.stateKey ?? ""] = val; + } + + bool containsKey(String key) => states.containsKey(key); + + void forEach(f) => states.forEach(f); +} diff --git a/test/StatesMap_test.dart b/test/StatesMap_test.dart new file mode 100644 index 0000000..77e8cdb --- /dev/null +++ b/test/StatesMap_test.dart @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2019 Zender & Kurtz GbR. + * + * Authors: + * Christian Pauly + * Marcel Radzio + * + * This file is part of famedlysdk. + * + * famedlysdk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * famedlysdk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with famedlysdk. If not, see . + */ + +import 'package:famedlysdk/famedlysdk.dart'; +import 'package:test/test.dart'; +import 'package:famedlysdk/src/utils/StatesMap.dart'; + +void main() { + /// All Tests related to the ChatTime + group("StateKeys", () { + test("Operator overload", () async { + StatesMap states = StatesMap(); + states["m.room.name"] = RoomState( + eventId: "1", + content: {"name": "test"}, + typeKey: "m.room.name", + stateKey: "", + roomId: "!test:test.test", + senderId: "@alice:test.test"); + + states["@alice:test.test"] = RoomState( + eventId: "2", + content: {"membership": "join"}, + typeKey: "m.room.name", + stateKey: "@alice:test.test", + roomId: "!test:test.test", + senderId: "@alice:test.test"); + + states["m.room.member"]["@bob:test.test"] = RoomState( + eventId: "3", + content: {"membership": "join"}, + typeKey: "m.room.name", + stateKey: "@bob:test.test", + roomId: "!test:test.test", + senderId: "@bob:test.test"); + + states["com.test.custom"] = RoomState( + eventId: "4", + content: {"custom": "stuff"}, + typeKey: "com.test.custom", + stateKey: "customStateKey", + roomId: "!test:test.test", + senderId: "@bob:test.test"); + + expect(states["m.room.name"].eventId, "1"); + expect(states["@alice:test.test"].eventId, "2"); + expect(states["m.room.member"]["@alice:test.test"].eventId, "2"); + expect(states["@bob:test.test"].eventId, "3"); + expect(states["m.room.member"]["@bob:test.test"].eventId, "3"); + expect(states["m.room.member"].length, 2); + expect(states["com.test.custom"]["customStateKey"].eventId, "4"); + }); + }); +} diff --git a/test/Timeline_test.dart b/test/Timeline_test.dart index b9ce645..29c4b53 100644 --- a/test/Timeline_test.dart +++ b/test/Timeline_test.dart @@ -43,11 +43,7 @@ void main() { client.homeserver = "https://fakeServer.notExisting"; Room room = Room( - id: roomID, - client: client, - prev_batch: "1234", - states: {}, - roomAccountData: {}); + id: roomID, client: client, prev_batch: "1234", roomAccountData: {}); Timeline timeline = Timeline( room: room, events: [],