[Archive] enhance lazyload archive

This commit is contained in:
Christian Pauly 2019-11-29 16:19:32 +00:00
parent c38f041a5a
commit 888155fbc7
5 changed files with 222 additions and 208 deletions

View file

@ -98,9 +98,6 @@ class Client {
/// A list of all rooms the user is participating or invited. /// A list of all rooms the user is participating or invited.
RoomList roomList; RoomList roomList;
/// A list of all rooms the user is not participating anymore.
RoomList archive;
/// Key/Value store of account data. /// Key/Value store of account data.
Map<String, AccountData> accountData = {}; Map<String, AccountData> accountData = {};
@ -262,27 +259,33 @@ class Client {
/// Creates a new [RoomList] object. /// Creates a new [RoomList] object.
RoomList getRoomList( RoomList getRoomList(
{bool onlyLeft = false, {onRoomListUpdateCallback onUpdate,
onRoomListUpdateCallback onUpdate,
onRoomListInsertCallback onInsert, onRoomListInsertCallback onInsert,
onRoomListRemoveCallback onRemove}) { onRoomListRemoveCallback onRemove}) {
List<Room> rooms = onlyLeft ? archive.rooms : roomList.rooms; List<Room> rooms = roomList.rooms;
return RoomList( return RoomList(
client: this, client: this,
onlyLeft: onlyLeft, onlyLeft: false,
onUpdate: onUpdate, onUpdate: onUpdate,
onInsert: onInsert, onInsert: onInsert,
onRemove: onRemove, onRemove: onRemove,
rooms: rooms); rooms: rooms);
} }
/// Searches in the roomList and in the archive for a room with the given [id]. Future<RoomList> get archive async {
Room getRoomById(String id) { RoomList archiveList = RoomList(client: this, rooms: [], onlyLeft: true);
Room room = roomList.getRoomById(id); String syncFilters =
if (room == null) room = archive.getRoomById(id); '{"room":{"include_leave":true,"timeline":{"limit":1}}}';
return room; String action = "/client/r0/sync?filter=$syncFilters&timeout=0";
final syncResp =
await connection.jsonRequest(type: HTTPType.GET, action: action);
if (!(syncResp is ErrorResponse)) await connection.handleSync(syncResp);
return archiveList;
} }
/// Searches in the roomList and in the archive for a room with the given [id].
Room getRoomById(String id) => roomList.getRoomById(id);
Future<dynamic> joinRoomById(String id) async { Future<dynamic> joinRoomById(String id) async {
return await connection.jsonRequest( return await connection.jsonRequest(
type: HTTPType.POST, action: "/client/r0/join/$id"); type: HTTPType.POST, action: "/client/r0/join/$id");

View file

@ -49,9 +49,6 @@ class Connection {
static String syncFilters = '{"room":{"state":{"lazy_load_members":true}}}'; static String syncFilters = '{"room":{"state":{"lazy_load_members":true}}}';
static String firstSyncFilters =
'{"room":{"include_leave":true,"state":{"lazy_load_members":true}}}';
/// Handles the connection to the Matrix Homeserver. You can change this to a /// Handles the connection to the Matrix Homeserver. You can change this to a
/// MockClient for testing. /// MockClient for testing.
http.Client httpClient = http.Client(); http.Client httpClient = http.Client();
@ -145,11 +142,9 @@ class Connection {
client.prevBatch = newPrevBatch; client.prevBatch = newPrevBatch;
List<Room> rooms = []; List<Room> rooms = [];
List<Room> archivedRooms = [];
if (client.store != null) { if (client.store != null) {
client.store.storeClient(); client.store.storeClient();
rooms = await client.store.getRoomList(onlyLeft: false); rooms = await client.store.getRoomList(onlyLeft: false);
archivedRooms = await client.store.getRoomList(onlyLeft: true);
client.accountData = await client.store.getAccountData(); client.accountData = await client.store.getAccountData();
client.presences = await client.store.getPresences(); client.presences = await client.store.getPresences();
} }
@ -162,14 +157,6 @@ class Connection {
onRemove: null, onRemove: null,
rooms: rooms); rooms: rooms);
client.archive = RoomList(
client: client,
onlyLeft: true,
onUpdate: null,
onInsert: null,
onRemove: null,
rooms: archivedRooms);
_userEventSub ??= onUserEvent.stream.listen(client.handleUserUpdate); _userEventSub ??= onUserEvent.stream.listen(client.handleUserUpdate);
onLoginStateChanged.add(LoginState.logged); onLoginStateChanged.add(LoginState.logged);
@ -309,10 +296,9 @@ class Connection {
Future<void> _sync() async { Future<void> _sync() async {
if (client.isLogged() == false) return; if (client.isLogged() == false) return;
String action = "/client/r0/sync?filter=$firstSyncFilters"; String action = "/client/r0/sync?filter=$syncFilters";
if (client.prevBatch != null) { if (client.prevBatch != null) {
action = "/client/r0/sync?filter=$syncFilters";
action += "&timeout=30000"; action += "&timeout=30000";
action += "&since=${client.prevBatch}"; action += "&since=${client.prevBatch}";
} }
@ -327,12 +313,12 @@ class Connection {
try { try {
if (client.store != null) if (client.store != null)
await client.store.transaction(() { await client.store.transaction(() {
_handleSync(syncResp); handleSync(syncResp);
client.store.storePrevBatch(syncResp); client.store.storePrevBatch(syncResp);
return; return;
}); });
else else
await _handleSync(syncResp); await handleSync(syncResp);
if (client.prevBatch == null) client.connection.onFirstSync.add(true); if (client.prevBatch == null) client.connection.onFirstSync.add(true);
client.prevBatch = syncResp["next_batch"]; client.prevBatch = syncResp["next_batch"];
} catch (e) { } catch (e) {
@ -344,7 +330,7 @@ class Connection {
if (hash == _syncRequest.hashCode) _sync(); if (hash == _syncRequest.hashCode) _sync();
} }
void _handleSync(dynamic sync) { void handleSync(dynamic sync) {
if (sync["rooms"] is Map<String, dynamic>) { if (sync["rooms"] is Map<String, dynamic>) {
if (sync["rooms"]["join"] is Map<String, dynamic>) if (sync["rooms"]["join"] is Map<String, dynamic>)
_handleRooms(sync["rooms"]["join"], Membership.join); _handleRooms(sync["rooms"]["join"], Membership.join);

View file

@ -203,11 +203,25 @@ class Room {
!canonicalAlias.isEmpty && !canonicalAlias.isEmpty &&
canonicalAlias.length > 3) canonicalAlias.length > 3)
return canonicalAlias.substring(1, canonicalAlias.length).split(":")[0]; return canonicalAlias.substring(1, canonicalAlias.length).split(":")[0];
if (mHeroes != null && mHeroes.length > 0 && mHeroes.any((h) => h.isNotEmpty)) { List<String> heroes = [];
if (mHeroes != null &&
mHeroes.length > 0 &&
mHeroes.any((h) => h.isNotEmpty)) {
heroes = mHeroes;
} else {
if (states["m.room.member"] is Map<String, dynamic>) {
for (var entry in states["m.room.member"].entries) {
RoomState state = entry.value;
if (state.type == EventTypes.RoomMember &&
state.stateKey != client?.userID) heroes.add(state.stateKey);
}
}
}
if (heroes.length > 0) {
String displayname = ""; String displayname = "";
for (int i = 0; i < mHeroes.length; i++) { for (int i = 0; i < heroes.length; i++) {
if (mHeroes[i].isEmpty) continue; if (heroes[i].isEmpty) continue;
displayname += User(mHeroes[i]).calcDisplayname() + ", "; displayname += User(heroes[i]).calcDisplayname() + ", ";
} }
return displayname.substring(0, displayname.length - 2); return displayname.substring(0, displayname.length - 2);
} }
@ -679,7 +693,6 @@ class Room {
List<User> getParticipants() { List<User> getParticipants() {
List<User> userList = []; List<User> userList = [];
if (states["m.room.member"] is Map<String, dynamic>) { if (states["m.room.member"] is Map<String, dynamic>) {
print('Check members: ${states["m.room.member"].length}');
for (var entry in states["m.room.member"].entries) { for (var entry in states["m.room.member"].entries) {
RoomState state = entry.value; RoomState state = entry.value;
if (state.type == EventTypes.RoomMember) userList.add(state.asUser); if (state.type == EventTypes.RoomMember) userList.add(state.asUser);

View file

@ -27,6 +27,7 @@ import 'package:famedlysdk/src/AccountData.dart';
import 'package:famedlysdk/src/Client.dart'; import 'package:famedlysdk/src/Client.dart';
import 'package:famedlysdk/src/Connection.dart'; import 'package:famedlysdk/src/Connection.dart';
import 'package:famedlysdk/src/Presence.dart'; import 'package:famedlysdk/src/Presence.dart';
import 'package:famedlysdk/src/RoomList.dart';
import 'package:famedlysdk/src/User.dart'; import 'package:famedlysdk/src/User.dart';
import 'package:famedlysdk/src/requests/SetPushersRequest.dart'; import 'package:famedlysdk/src/requests/SetPushersRequest.dart';
import 'package:famedlysdk/src/responses/ErrorResponse.dart'; import 'package:famedlysdk/src/responses/ErrorResponse.dart';
@ -207,7 +208,7 @@ void main() {
List<RoomUpdate> roomUpdateList = await roomUpdateListFuture; List<RoomUpdate> roomUpdateList = await roomUpdateListFuture;
expect(roomUpdateList.length, 3); expect(roomUpdateList.length, 2);
expect(roomUpdateList[0].id == "!726s6s6q:example.com", true); expect(roomUpdateList[0].id == "!726s6s6q:example.com", true);
expect(roomUpdateList[0].membership == Membership.join, true); expect(roomUpdateList[0].membership == Membership.join, true);
@ -222,13 +223,6 @@ void main() {
expect(roomUpdateList[1].limitedTimeline == false, true); expect(roomUpdateList[1].limitedTimeline == false, true);
expect(roomUpdateList[1].notification_count == 0, true); expect(roomUpdateList[1].notification_count == 0, true);
expect(roomUpdateList[1].highlight_count == 0, true); expect(roomUpdateList[1].highlight_count == 0, true);
expect(roomUpdateList[2].id == "!5345234234:example.com", true);
expect(roomUpdateList[2].membership == Membership.leave, true);
expect(roomUpdateList[2].prev_batch == "", true);
expect(roomUpdateList[2].limitedTimeline == false, true);
expect(roomUpdateList[2].notification_count == 0, true);
expect(roomUpdateList[2].highlight_count == 0, true);
}); });
test('Event Update Test', () async { test('Event Update Test', () async {
@ -364,6 +358,15 @@ void main() {
expect(resp["room_id"], roomID); expect(resp["room_id"], roomID);
}); });
test('get archive', () async {
RoomList archive = await matrix.archive;
await new Future.delayed(new Duration(milliseconds: 50));
expect(archive.rooms.length, 1);
expect(archive.rooms[0].id, "!5345234234:example.com");
expect(archive.rooms[0].membership, Membership.leave);
});
test('Logout when token is unknown', () async { test('Logout when token is unknown', () async {
Future<LoginState> loginStateFuture = Future<LoginState> loginStateFuture =
matrix.connection.onLoginStateChanged.stream.first; matrix.connection.onLoginStateChanged.stream.first;

View file

@ -62,6 +62,176 @@ class FakeMatrixApi extends MockClient {
return Response(json.encode(res), 100); return Response(json.encode(res), 100);
}); });
static Map<String, dynamic> syncResponse = {
"next_batch": Random().nextDouble().toString(),
"presence": {
"events": [
{
"sender": "@alice:example.com",
"type": "m.presence",
"content": {"presence": "online"}
}
]
},
"account_data": {
"events": [
{
"type": "org.example.custom.config",
"content": {"custom_config_key": "custom_config_value"}
},
{
"content": {
"@bob:example.com": [
"!726s6s6q:example.com",
"!hgfedcba:example.com"
]
},
"type": "m.direct"
},
]
},
"to_device": {
"events": [
{
"sender": "@alice:example.com",
"type": "m.new_device",
"content": {
"device_id": "XYZABCDE",
"rooms": ["!726s6s6q:example.com"]
}
}
]
},
"rooms": {
"join": {
"!726s6s6q:example.com": {
"unread_notifications": {
"highlight_count": 2,
"notification_count": 2,
},
"state": {
"events": [
{
"sender": "@alice:example.com",
"type": "m.room.member",
"state_key": "@alice:example.com",
"content": {"membership": "join"},
"origin_server_ts": 1417731086795,
"event_id": "66697273743031:example.com"
},
{
"sender": "@alice:example.com",
"type": "m.room.canonical_alias",
"content": {
"alias": "#famedlyContactDiscovery:fakeServer.notExisting"
},
"state_key": "",
"origin_server_ts": 1417731086796,
"event_id": "66697273743032:example.com"
}
]
},
"timeline": {
"events": [
{
"sender": "@bob:example.com",
"type": "m.room.member",
"state_key": "@bob:example.com",
"content": {"membership": "join"},
"prev_content": {"membership": "invite"},
"origin_server_ts": 1417731086795,
"event_id": "7365636s6r6432:example.com"
},
{
"sender": "@alice:example.com",
"type": "m.room.message",
"txn_id": "1234",
"content": {"body": "I am a fish", "msgtype": "m.text"},
"origin_server_ts": 1417731086797,
"event_id": "74686972643033:example.com"
}
],
"limited": true,
"prev_batch": "t34-23535_0_0"
},
"ephemeral": {
"events": [
{
"type": "m.typing",
"content": {
"user_ids": ["@alice:example.com"]
}
},
{
"content": {
"7365636s6r6432:example.com": {
"m.read": {
"@alice:example.com": {"ts": 1436451550453}
}
}
},
"room_id": "!726s6s6q:example.com",
"type": "m.receipt"
}
]
},
"account_data": {
"events": [
{
"type": "m.tag",
"content": {
"tags": {
"work": {"order": 1}
}
}
},
{
"type": "org.example.custom.room.config",
"content": {"custom_config_key": "custom_config_value"}
}
]
}
}
},
"invite": {
"!696r7674:example.com": {
"invite_state": {
"events": [
{
"sender": "@alice:example.com",
"type": "m.room.name",
"state_key": "",
"content": {"name": "My Room Name"}
},
{
"sender": "@alice:example.com",
"type": "m.room.member",
"state_key": "@bob:example.com",
"content": {"membership": "invite"}
}
]
}
}
},
}
};
static Map<String, dynamic> archiveSyncResponse = {
"next_batch": Random().nextDouble().toString(),
"presence": {"events": []},
"account_data": {"events": []},
"to_device": {"events": []},
"rooms": {
"join": {},
"invite": {},
"leave": {
"!5345234234:example.com": {
"timeline": {"events": []}
},
},
}
};
static final Map<String, Map<String, dynamic>> api = { static final Map<String, Map<String, dynamic>> api = {
"GET": { "GET": {
"/client/r0/rooms/!localpart:server.abc/state/m.room.member/@getme:example.com": "/client/r0/rooms/!localpart:server.abc/state/m.room.member/@getme:example.com":
@ -339,171 +509,10 @@ class FakeMatrixApi extends MockClient {
] ]
} }
}, },
"/client/r0/sync?filter=%7B%22room%22:%7B%22include_leave%22:true,%22state%22:%7B%22lazy_load_members%22:true%7D%7D%7D": "/client/r0/sync?filter=%7B%22room%22:%7B%22include_leave%22:true,%22timeline%22:%7B%22limit%22:1%7D%7D%7D&timeout=0":
(var req) => { (var req) => archiveSyncResponse,
"next_batch": Random().nextDouble().toString(), "/client/r0/sync?filter=%7B%22room%22:%7B%22state%22:%7B%22lazy_load_members%22:true%7D%7D%7D":
"presence": { (var req) => syncResponse,
"events": [
{
"sender": "@alice:example.com",
"type": "m.presence",
"content": {"presence": "online"}
}
]
},
"account_data": {
"events": [
{
"type": "org.example.custom.config",
"content": {"custom_config_key": "custom_config_value"}
},
{
"content": {
"@bob:example.com": [
"!726s6s6q:example.com",
"!hgfedcba:example.com"
]
},
"type": "m.direct"
},
]
},
"to_device": {
"events": [
{
"sender": "@alice:example.com",
"type": "m.new_device",
"content": {
"device_id": "XYZABCDE",
"rooms": ["!726s6s6q:example.com"]
}
}
]
},
"rooms": {
"join": {
"!726s6s6q:example.com": {
"unread_notifications": {
"highlight_count": 2,
"notification_count": 2,
},
"state": {
"events": [
{
"sender": "@alice:example.com",
"type": "m.room.member",
"state_key": "@alice:example.com",
"content": {"membership": "join"},
"origin_server_ts": 1417731086795,
"event_id": "66697273743031:example.com"
},
{
"sender": "@alice:example.com",
"type": "m.room.canonical_alias",
"content": {
"alias":
"#famedlyContactDiscovery:fakeServer.notExisting"
},
"state_key": "",
"origin_server_ts": 1417731086796,
"event_id": "66697273743032:example.com"
}
]
},
"timeline": {
"events": [
{
"sender": "@bob:example.com",
"type": "m.room.member",
"state_key": "@bob:example.com",
"content": {"membership": "join"},
"prev_content": {"membership": "invite"},
"origin_server_ts": 1417731086795,
"event_id": "7365636s6r6432:example.com"
},
{
"sender": "@alice:example.com",
"type": "m.room.message",
"txn_id": "1234",
"content": {
"body": "I am a fish",
"msgtype": "m.text"
},
"origin_server_ts": 1417731086797,
"event_id": "74686972643033:example.com"
}
],
"limited": true,
"prev_batch": "t34-23535_0_0"
},
"ephemeral": {
"events": [
{
"type": "m.typing",
"content": {
"user_ids": ["@alice:example.com"]
}
},
{
"content": {
"7365636s6r6432:example.com": {
"m.read": {
"@alice:example.com": {"ts": 1436451550453}
}
}
},
"room_id": "!726s6s6q:example.com",
"type": "m.receipt"
}
]
},
"account_data": {
"events": [
{
"type": "m.tag",
"content": {
"tags": {
"work": {"order": 1}
}
}
},
{
"type": "org.example.custom.room.config",
"content": {
"custom_config_key": "custom_config_value"
}
}
]
}
}
},
"invite": {
"!696r7674:example.com": {
"invite_state": {
"events": [
{
"sender": "@alice:example.com",
"type": "m.room.name",
"state_key": "",
"content": {"name": "My Room Name"}
},
{
"sender": "@alice:example.com",
"type": "m.room.member",
"state_key": "@bob:example.com",
"content": {"membership": "invite"}
}
]
}
}
},
"leave": {
"!5345234234:example.com": {
"timeline": {"events": []}
},
},
}
},
}, },
"POST": { "POST": {
"/client/r0/login": (var req) => { "/client/r0/login": (var req) => {