From e063af9fe738b854d90db77e6298c0cf7ef1d295 Mon Sep 17 00:00:00 2001 From: Marcel Date: Fri, 12 Jul 2019 09:26:07 +0000 Subject: [PATCH] [Store] Rename User table to Users [Connection] Add HTTPtype enum [User] Add Membership enum [Event] Add Reply to the Event types Took 2 hours 0 minutes --- lib/src/Client.dart | 30 ++++++++++------- lib/src/Connection.dart | 34 ++++++++++++------- lib/src/Event.dart | 14 ++++++-- lib/src/Room.dart | 62 +++++++++++++++++++--------------- lib/src/RoomList.dart | 11 ++++--- lib/src/Store.dart | 64 +++++++++++++++++++----------------- lib/src/Timeline.dart | 5 ++- lib/src/User.dart | 31 +++++++++++------ lib/src/sync/RoomUpdate.dart | 4 ++- test/Client_test.dart | 24 +++++++------- test/Event_test.dart | 7 ++-- test/RoomList_test.dart | 15 +++++---- test/Room_test.dart | 11 ++++--- test/User_test.dart | 6 ++-- 14 files changed, 188 insertions(+), 130 deletions(-) diff --git a/lib/src/Client.dart b/lib/src/Client.dart index fa8ee94..2dc0266 100644 --- a/lib/src/Client.dart +++ b/lib/src/Client.dart @@ -23,13 +23,14 @@ import 'dart:async'; import 'dart:core'; -import 'requests/SetPushersRequest.dart'; -import 'responses/ErrorResponse.dart'; + import 'Connection.dart'; -import 'RoomList.dart'; import 'Room.dart'; +import 'RoomList.dart'; import 'Store.dart'; import 'User.dart'; +import 'requests/SetPushersRequest.dart'; +import 'responses/ErrorResponse.dart'; import 'responses/PushrulesResponse.dart'; /// Represents a Matrix client to communicate with a @@ -91,8 +92,8 @@ class Client { Future checkServer(serverUrl) async { homeserver = serverUrl; - final versionResp = - await connection.jsonRequest(type: "GET", action: "/client/versions"); + final versionResp = await connection.jsonRequest( + type: HTTPType.GET, action: "/client/versions"); if (versionResp is ErrorResponse) { connection.onError.add(ErrorResponse(errcode: "NO_RESPONSE", error: "")); return false; @@ -123,8 +124,8 @@ class Client { : false; } - final loginResp = - await connection.jsonRequest(type: "GET", action: "/client/r0/login"); + final loginResp = await connection.jsonRequest( + type: HTTPType.GET, action: "/client/r0/login"); if (loginResp is ErrorResponse) { connection.onError.add(loginResp); return false; @@ -149,7 +150,7 @@ class Client { /// authentication. Returns false if the login was not successful. Future login(String username, String password) async { final loginResp = await connection - .jsonRequest(type: "POST", action: "/client/r0/login", data: { + .jsonRequest(type: HTTPType.POST, action: "/client/r0/login", data: { "type": "m.login.password", "user": username, "identifier": { @@ -186,7 +187,7 @@ class Client { /// including all persistent data from the store. Future logout() async { final dynamic resp = await connection.jsonRequest( - type: "POST", action: "/client/r0/logout/all"); + type: HTTPType.POST, action: "/client/r0/logout/all"); if (resp is ErrorResponse) connection.onError.add(resp); await connection.clear(); @@ -213,6 +214,11 @@ class Client { rooms: rooms); } + Future joinRoomById(String id) async { + return await connection.jsonRequest( + type: HTTPType.POST, action: "/client/r0/join/$id"); + } + /// Creates a new group chat and invites the given Users and returns the new /// created room ID. Future createGroup(List users) async { @@ -220,7 +226,7 @@ class Client { for (int i = 0; i < users.length; i++) inviteIDs.add(users[i].id); final dynamic resp = await connection.jsonRequest( - type: "POST", + type: HTTPType.POST, action: "/client/r0/createRoom", data: {"invite": inviteIDs, "preset": "private_chat"}); @@ -236,7 +242,7 @@ class Client { /// These are needed for notifications on Android Future getPushrules() async { final dynamic resp = await connection.jsonRequest( - type: "GET", + type: HTTPType.GET, action: "/client/r0/pushrules", ); @@ -251,7 +257,7 @@ class Client { /// This endpoint allows the creation, modification and deletion of pushers for this user ID. Future setPushers(SetPushersRequest data) async { final dynamic resp = await connection.jsonRequest( - type: "POST", + type: HTTPType.POST, action: "/client/r0/pushers/set", data: data, ); diff --git a/lib/src/Connection.dart b/lib/src/Connection.dart index bf9e109..1928d44 100644 --- a/lib/src/Connection.dart +++ b/lib/src/Connection.dart @@ -24,13 +24,18 @@ import 'dart:async'; import 'dart:convert'; import 'dart:core'; + import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; + +import 'Client.dart'; +import 'User.dart'; import 'responses/ErrorResponse.dart'; import 'sync/EventUpdate.dart'; -import 'sync/UserUpdate.dart'; import 'sync/RoomUpdate.dart'; -import 'Client.dart'; +import 'sync/UserUpdate.dart'; + +enum HTTPType { GET, POST, PUT, DELETE } /// Represents a Matrix connection to communicate with a /// [Matrix](https://matrix.org) homeserver. @@ -41,10 +46,12 @@ class Connection { WidgetsBinding.instance ?.addObserver(_LifecycleEventHandler(resumeCallBack: () { _sync(); + return; })); } String get _syncFilters => '{"room":{"state":{"lazy_load_members":true}}}'; + String get _firstSyncFilters => '{"room":{"include_leave":true,"state":{"lazy_load_members":true}}}'; @@ -103,7 +110,7 @@ class Connection { /// /// ``` /// final resp = await matrix - /// .jsonRequest(type: "POST", action: "/client/r0/login", data: { + /// .jsonRequest(type: HTTPType.POST, action: "/client/r0/login", data: { /// "type": "m.login.password", /// "user": "test", /// "password": "1234", @@ -164,7 +171,7 @@ class Connection { /// /// ``` /// final resp = await jsonRequest( - /// type: "PUT", + /// type: HTTPType.PUT, /// action: "/r0/rooms/!fjd823j:example.com/send/m.room.message/$txnId", /// data: { /// "msgtype": "m.text", @@ -174,7 +181,7 @@ class Connection { /// ``` /// Future jsonRequest( - {String type, String action, dynamic data = "", int timeout}) async { + {HTTPType type, String action, dynamic data = "", int timeout}) async { if (client.isLogged() == false && client.homeserver == null) throw ("No homeserver specified."); if (timeout == null) timeout = syncTimeoutSec + 5; @@ -188,11 +195,13 @@ class Connection { if (client.isLogged()) headers["Authorization"] = "Bearer ${client.accessToken}"; - if (client.debug) print("[REQUEST $type] Action: $action, Data: $data"); + if (client.debug) + print( + "[REQUEST ${type.toString().split('.').last}] Action: $action, Data: $data"); http.Response resp; try { - switch (type) { + switch (type.toString().split('.').last) { case "GET": resp = await httpClient .get(url, headers: headers) @@ -257,7 +266,7 @@ class Connection { action += "&timeout=30000"; action += "&since=${client.prevBatch}"; } - _syncRequest = jsonRequest(type: "GET", action: action); + _syncRequest = jsonRequest(type: HTTPType.GET, action: action); final int hash = _syncRequest.hashCode; final syncResp = await _syncRequest; if (hash != _syncRequest.hashCode) return; @@ -270,6 +279,7 @@ class Connection { await client.store.transaction(() { _handleSync(syncResp); client.store.storePrevBatch(syncResp); + return; }); else await _handleSync(syncResp); @@ -287,11 +297,11 @@ class Connection { void _handleSync(dynamic sync) { if (sync["rooms"] is Map) { if (sync["rooms"]["join"] is Map) - _handleRooms(sync["rooms"]["join"], "join"); + _handleRooms(sync["rooms"]["join"], Membership.join); if (sync["rooms"]["invite"] is Map) - _handleRooms(sync["rooms"]["invite"], "invite"); + _handleRooms(sync["rooms"]["invite"], Membership.invite); if (sync["rooms"]["leave"] is Map) - _handleRooms(sync["rooms"]["leave"], "leave"); + _handleRooms(sync["rooms"]["leave"], Membership.leave); } if (sync["presence"] is Map && sync["presence"]["events"] is List) { @@ -308,7 +318,7 @@ class Connection { onSync.add(sync); } - void _handleRooms(Map rooms, String membership) { + void _handleRooms(Map rooms, Membership membership) { rooms.forEach((String id, dynamic room) async { // calculate the notification counts, the limitedTimeline and prevbatch num highlight_count = 0; diff --git a/lib/src/Event.dart b/lib/src/Event.dart index cbf7b6b..fd4f8ae 100644 --- a/lib/src/Event.dart +++ b/lib/src/Event.dart @@ -22,11 +22,13 @@ */ import 'dart:convert'; + +import 'package:famedlysdk/src/Client.dart'; import 'package:famedlysdk/src/sync/EventUpdate.dart'; import 'package:famedlysdk/src/utils/ChatTime.dart'; -import 'package:famedlysdk/src/Client.dart'; -import './User.dart'; + import './Room.dart'; +import './User.dart'; /// A single Matrix event, e.g. a message in a chat. class Event { @@ -106,6 +108,9 @@ class Event { case "m.room.message": switch (content["msgtype"] ?? "m.text") { case "m.text": + if (content.containsKey("m.relates_to")) { + return EventTypes.Reply; + } return EventTypes.Text; case "m.notice": return EventTypes.Notice; @@ -135,7 +140,9 @@ class Event { try { content = json.decode(jsonObj["content_json"]); } catch (e) { - print("jsonObj decode of event content failed: ${e.toString()}"); + if (room.client.debug) { + print("jsonObj decode of event content failed: ${e.toString()}"); + } content = {}; } else if (content == null) content = {}; @@ -198,6 +205,7 @@ enum EventTypes { Audio, File, Location, + Reply, RoomAliases, RoomCanonicalAlias, RoomCreate, diff --git a/lib/src/Room.dart b/lib/src/Room.dart index be7291b..36d1ecb 100644 --- a/lib/src/Room.dart +++ b/lib/src/Room.dart @@ -22,12 +22,14 @@ */ import 'package:famedlysdk/src/Client.dart'; -import 'package:famedlysdk/src/utils/ChatTime.dart'; -import 'package:famedlysdk/src/utils/MxContent.dart'; +import 'package:famedlysdk/src/Event.dart'; import 'package:famedlysdk/src/responses/ErrorResponse.dart'; import 'package:famedlysdk/src/sync/EventUpdate.dart'; -import 'package:famedlysdk/src/Event.dart'; +import 'package:famedlysdk/src/utils/ChatTime.dart'; +import 'package:famedlysdk/src/utils/MxContent.dart'; + import './User.dart'; +import 'Connection.dart'; import 'Timeline.dart'; /// Represents a Matrix room. @@ -36,7 +38,7 @@ class Room { final String id; /// Membership status of the user for this room. - String membership; + Membership membership; /// The name of the room if set by a participant. String name; @@ -96,7 +98,7 @@ class Room { Client get matrix => this.client; @Deprecated("Rooms.status is deprecated! Use Rooms.membership instead!") - String get status => this.membership; + String get status => this.membership.toString().split('.').last; Room({ this.id, @@ -140,7 +142,7 @@ class Room { /// Call the Matrix API to change the name of this room. Future setName(String newName) async { dynamic res = await client.connection.jsonRequest( - type: "PUT", + type: HTTPType.PUT, action: "/client/r0/rooms/${id}/send/m.room.name/${new DateTime.now()}", data: {"name": newName}); if (res is ErrorResponse) client.connection.onError.add(res); @@ -150,7 +152,7 @@ class Room { /// Call the Matrix API to change the topic of this room. Future setDescription(String newName) async { dynamic res = await client.connection.jsonRequest( - type: "PUT", + type: HTTPType.PUT, action: "/client/r0/rooms/${id}/send/m.room.topic/${new DateTime.now()}", data: {"topic": newName}); @@ -162,7 +164,7 @@ class Room { Future sendText(String message, {String txid = null}) async { if (txid == null) txid = "txid${DateTime.now().millisecondsSinceEpoch}"; final dynamic res = await client.connection.jsonRequest( - type: "PUT", + type: HTTPType.PUT, action: "/client/r0/rooms/${id}/send/m.room.message/$txid", data: {"msgtype": "m.text", "body": message}); if (res is ErrorResponse) client.connection.onError.add(res); @@ -197,6 +199,7 @@ class Room { client.connection.onEvent.add(eventUpdate); await client.store?.transaction(() { client.store.storeEventUpdate(eventUpdate); + return; }); // Send the text and on success, store and display a *sent* event. @@ -208,6 +211,7 @@ class Room { client.connection.onEvent.add(eventUpdate); await client.store?.transaction(() { client.store.storeEventUpdate(eventUpdate); + return; }); } else { eventUpdate.content["status"] = 1; @@ -215,6 +219,7 @@ class Room { client.connection.onEvent.add(eventUpdate); await client.store?.transaction(() { client.store.storeEventUpdate(eventUpdate); + return; }); return res["event_id"]; } @@ -223,8 +228,8 @@ class Room { /// Call the Matrix API to leave this room. Future leave() async { - dynamic res = await client.connection - .jsonRequest(type: "POST", action: "/client/r0/rooms/${id}/leave"); + dynamic res = await client.connection.jsonRequest( + type: HTTPType.POST, action: "/client/r0/rooms/${id}/leave"); if (res is ErrorResponse) client.connection.onError.add(res); return res; } @@ -232,8 +237,8 @@ class Room { /// Call the Matrix API to forget this room if you already left it. Future forget() async { client.store.forgetRoom(id); - dynamic res = await client.connection - .jsonRequest(type: "POST", action: "/client/r0/rooms/${id}/forget"); + dynamic res = await client.connection.jsonRequest( + type: HTTPType.POST, action: "/client/r0/rooms/${id}/forget"); if (res is ErrorResponse) client.connection.onError.add(res); return res; } @@ -241,7 +246,7 @@ class Room { /// Call the Matrix API to kick a user from this room. Future kick(String userID) async { dynamic res = await client.connection.jsonRequest( - type: "POST", + type: HTTPType.POST, action: "/client/r0/rooms/${id}/kick", data: {"user_id": userID}); if (res is ErrorResponse) client.connection.onError.add(res); @@ -251,7 +256,7 @@ class Room { /// Call the Matrix API to ban a user from this room. Future ban(String userID) async { dynamic res = await client.connection.jsonRequest( - type: "POST", + type: HTTPType.POST, action: "/client/r0/rooms/${id}/ban", data: {"user_id": userID}); if (res is ErrorResponse) client.connection.onError.add(res); @@ -261,7 +266,7 @@ class Room { /// Call the Matrix API to unban a banned user from this room. Future unban(String userID) async { dynamic res = await client.connection.jsonRequest( - type: "POST", + type: HTTPType.POST, action: "/client/r0/rooms/${id}/unban", data: {"user_id": userID}); if (res is ErrorResponse) client.connection.onError.add(res); @@ -274,7 +279,7 @@ class Room { powerMap[userID] = power; dynamic res = await client.connection.jsonRequest( - type: "PUT", + type: HTTPType.PUT, action: "/client/r0/rooms/$id/state/m.room.power_levels/", data: {"users": powerMap}); if (res is ErrorResponse) client.connection.onError.add(res); @@ -284,7 +289,7 @@ class Room { /// Call the Matrix API to invite a user to this room. Future invite(String userID) async { dynamic res = await client.connection.jsonRequest( - type: "POST", + type: HTTPType.POST, action: "/client/r0/rooms/${id}/invite", data: {"user_id": userID}); if (res is ErrorResponse) client.connection.onError.add(res); @@ -294,7 +299,7 @@ class Room { /// Request more previous events from the server. Future requestHistory({int historyCount = 100}) async { final dynamic resp = await client.connection.jsonRequest( - type: "GET", + type: HTTPType.GET, action: "/client/r0/rooms/$id/messages?from=${prev_batch}&dir=b&limit=$historyCount"); @@ -321,6 +326,7 @@ class Room { client.store.txn.rawUpdate( "UPDATE Rooms SET prev_batch=? WHERE id=?", [resp["end"], id]); } + return; }); if (client.store == null) { for (int i = 0; i < history.length; i++) { @@ -347,7 +353,7 @@ class Room { directChats[userID] = [id]; final resp = await client.connection.jsonRequest( - type: "PUT", + type: HTTPType.PUT, action: "/client/r0/user/${client.userID}/account_data/m.direct", data: directChats); return resp; @@ -356,7 +362,7 @@ class Room { /// Sends *m.fully_read* and *m.read* for the given event ID. Future sendReadReceipt(String eventID) async { final dynamic resp = client.connection.jsonRequest( - type: "POST", + type: HTTPType.POST, action: "/client/r0/rooms/$id/read_markers", data: { "m.fully_read": eventID, @@ -379,7 +385,8 @@ class Room { return Room( id: row["id"], name: name, - membership: row["membership"], + membership: Membership.values + .firstWhere((e) => e.toString() == 'Membership.' + row["membership"]), topic: row["description"], avatar: MxContent(avatarUrl), notificationCount: row["notification_count"], @@ -455,18 +462,21 @@ class Room { Future> requestParticipants() async { List participants = []; - dynamic res = await client.connection - .jsonRequest(type: "GET", action: "/client/r0/rooms/${id}/members"); + dynamic res = await client.connection.jsonRequest( + type: HTTPType.GET, action: "/client/r0/rooms/${id}/members"); if (res is ErrorResponse || !(res["chunk"] is List)) return participants; for (num i = 0; i < res["chunk"].length; i++) { User newUser = User(res["chunk"][i]["state_key"], displayName: res["chunk"][i]["content"]["displayname"] ?? "", - membership: res["chunk"][i]["content"]["membership"] ?? "", + membership: Membership.values.firstWhere((e) => + e.toString() == + 'Membership.' + res["chunk"][i]["content"]["membership"] ?? + ""), avatarUrl: MxContent(res["chunk"][i]["content"]["avatar_url"] ?? ""), room: this); - if (newUser.membership != "leave") participants.add(newUser); + if (newUser.membership != Membership.leave) participants.add(newUser); } return participants; @@ -480,7 +490,7 @@ class Room { if (storeEvent != null) return storeEvent; } final dynamic resp = await client.connection.jsonRequest( - type: "GET", action: "/client/r0/rooms/$id/event/$eventID"); + type: HTTPType.GET, action: "/client/r0/rooms/$id/event/$eventID"); if (resp is ErrorResponse) return null; return Event.fromJson(resp, this); } diff --git a/lib/src/RoomList.dart b/lib/src/RoomList.dart index 47bae38..f0a1060 100644 --- a/lib/src/RoomList.dart +++ b/lib/src/RoomList.dart @@ -23,14 +23,15 @@ import 'dart:async'; import 'dart:core'; + import 'Client.dart'; import 'Event.dart'; import 'Room.dart'; import 'User.dart'; -import 'utils/ChatTime.dart'; -import 'utils/MxContent.dart'; import 'sync/EventUpdate.dart'; import 'sync/RoomUpdate.dart'; +import 'utils/ChatTime.dart'; +import 'utils/MxContent.dart'; /// Represents a list of rooms for this client, which will automatically update /// itself and call the [onUpdate], [onInsert] and [onDelete] callbacks. To get @@ -78,11 +79,11 @@ class RoomList { if (rooms[j].id == chatUpdate.id) break; } final bool found = (j < rooms.length - 1 && rooms[j].id == chatUpdate.id); - final bool isLeftRoom = chatUpdate.membership == "leave"; + final bool isLeftRoom = chatUpdate.membership == Membership.leave; // Does the chat already exist in the list rooms? if (!found && ((!onlyLeft && !isLeftRoom) || (onlyLeft && isLeftRoom))) { - num position = chatUpdate.membership == "invite" ? 0 : j; + num position = chatUpdate.membership == Membership.invite ? 0 : j; // Add the new chat to the list Room newRoom = Room( id: chatUpdate.id, @@ -102,7 +103,7 @@ class RoomList { } // Update notification and highlight count else if (found && - chatUpdate.membership != "leave" && + chatUpdate.membership != Membership.leave && (rooms[j].notificationCount != chatUpdate.notification_count || rooms[j].highlightCount != chatUpdate.highlight_count)) { rooms[j].notificationCount = chatUpdate.notification_count; diff --git a/lib/src/Store.dart b/lib/src/Store.dart index 0b04ae4..9b48ff4 100644 --- a/lib/src/Store.dart +++ b/lib/src/Store.dart @@ -24,16 +24,18 @@ import 'dart:async'; import 'dart:convert'; import 'dart:core'; -import 'package:sqflite/sqflite.dart'; + import 'package:path/path.dart' as p; -import 'sync/EventUpdate.dart'; -import 'sync/UserUpdate.dart'; -import 'sync/RoomUpdate.dart'; +import 'package:sqflite/sqflite.dart'; + import 'Client.dart'; -import 'User.dart'; -import 'Room.dart'; -import 'Event.dart'; import 'Connection.dart'; +import 'Event.dart'; +import 'Room.dart'; +import 'User.dart'; +import 'sync/EventUpdate.dart'; +import 'sync/RoomUpdate.dart'; +import 'sync/UserUpdate.dart'; /// Responsible to store all data persistent and to query objects from the /// database. @@ -53,7 +55,7 @@ class Store { _init() async { var databasePath = await getDatabasesPath(); String path = p.join(databasePath, "FluffyMatrix.db"); - _db = await openDatabase(path, version: 4, + _db = await openDatabase(path, version: 5, onCreate: (Database db, int version) async { await createTables(db); }, onUpgrade: (Database db, int oldVersion, int newVersion) async { @@ -61,7 +63,7 @@ class Store { if (oldVersion != newVersion) { await db.execute("DROP TABLE IF EXISTS Rooms"); await db.execute("DROP TABLE IF EXISTS Participants"); - await db.execute("DROP TABLE IF EXISTS User"); + await db.execute("DROP TABLE IF EXISTS Users"); await db.execute("DROP TABLE IF EXISTS Events"); db.rawUpdate("UPDATE Clients SET prev_batch='' WHERE client=?", [client.clientName]); @@ -127,7 +129,7 @@ class Store { await _db .rawDelete("DELETE FROM Clients WHERE client=?", [client.clientName]); await _db.rawDelete("DELETE FROM Rooms"); - await _db.rawDelete("DELETE FROM User"); + await _db.rawDelete("DELETE FROM Users"); await _db.rawDelete("DELETE FROM Events"); return; } @@ -162,7 +164,7 @@ class Store { txn.rawInsert( "INSERT OR IGNORE INTO Rooms " + "VALUES(?, ?, '', 0, 0, '', '', '', 0, '', '', '', '', '', '', '', '', 0, 50, 50, 0, 50, 50, 0, 50, 100, 50, 50, 50, 100) ", - [roomUpdate.id, roomUpdate.membership]); + [roomUpdate.id, roomUpdate.membership.toString().split('.').last]); // Update the notification counts and the limited timeline boolean txn.rawUpdate( @@ -170,7 +172,7 @@ class Store { [ roomUpdate.highlight_count, roomUpdate.notification_count, - roomUpdate.membership, + roomUpdate.membership.toString().split('.').last, roomUpdate.id ]); @@ -329,14 +331,14 @@ class Store { } // Update membership table - txn.rawInsert("INSERT OR IGNORE INTO User VALUES(?,?,?,?,?,0)", [ + txn.rawInsert("INSERT OR IGNORE INTO Users VALUES(?,?,?,?,?,0)", [ chat_id, state_key, insertDisplayname, insertAvatarUrl, membership ]); - String queryStr = "UPDATE User SET membership=?"; + String queryStr = "UPDATE Users SET membership=?"; List queryArgs = [membership]; if (eventContent["content"]["displayname"] is String) { @@ -411,10 +413,10 @@ class Store { .forEach((String user, dynamic value) async { num power_level = eventContent["content"]["users"][user]; txn.rawUpdate( - "UPDATE User SET power_level=? WHERE matrix_id=? AND chat_id=?", + "UPDATE Users SET power_level=? WHERE matrix_id=? AND chat_id=?", [power_level, user, chat_id]); txn.rawInsert( - "INSERT OR IGNORE INTO User VALUES(?, ?, '', '', ?, ?)", + "INSERT OR IGNORE INTO Users VALUES(?, ?, '', '', ?, ?)", [chat_id, user, "unknown", power_level]); }); } @@ -426,7 +428,7 @@ class Store { /// Returns a User object by a given Matrix ID and a Room. Future getUser({String matrixID, Room room}) async { List> res = await db.rawQuery( - "SELECT * FROM User WHERE matrix_id=? AND chat_id=?", + "SELECT * FROM Users WHERE matrix_id=? AND chat_id=?", [matrixID, room.id]); if (res.length != 1) return null; return User.fromJson(res[0], room); @@ -435,7 +437,7 @@ class Store { /// Loads all Users in the database to provide a contact list. Future> loadContacts() async { List> res = await db.rawQuery( - "SELECT * FROM User WHERE matrix_id!=? GROUP BY matrix_id ORDER BY displayname", + "SELECT * FROM Users WHERE matrix_id!=? GROUP BY matrix_id ORDER BY displayname", [client.userID]); List userList = []; for (int i = 0; i < res.length; i++) @@ -447,7 +449,7 @@ class Store { Future> loadParticipants(Room room) async { List> res = await db.rawQuery( "SELECT * " + - " FROM User " + + " FROM Users " + " WHERE chat_id=? " + " AND membership='join'", [room.id]); @@ -464,7 +466,7 @@ class Store { /// Returns a list of events for the given room and sets all participants. Future> getEventList(Room room) async { List> memberRes = await db.rawQuery( - "SELECT * " + " FROM User " + " WHERE User.chat_id=?", [room.id]); + "SELECT * " + " FROM Users " + " WHERE Users.chat_id=?", [room.id]); Map userMap = {}; for (num i = 0; i < memberRes.length; i++) userMap[memberRes[i]["matrix_id"]] = User.fromJson(memberRes[i], room); @@ -528,10 +530,10 @@ class Store { Future getAvatarFromSingleChat(String roomID) async { String avatarStr = ""; List> res = await db.rawQuery( - "SELECT avatar_url FROM User " + - " WHERE User.chat_id=? " + - " AND (User.membership='join' OR User.membership='invite') " + - " AND User.matrix_id!=? ", + "SELECT avatar_url FROM Users " + + " WHERE Users.chat_id=? " + + " AND (Users.membership='join' OR Users.membership='invite') " + + " AND Users.matrix_id!=? ", [roomID, client.userID]); if (res.length == 1) avatarStr = res[0]["avatar_url"]; return avatarStr; @@ -543,10 +545,10 @@ class Store { Future getChatNameFromMemberNames(String roomID) async { String displayname = 'Empty chat'; List> rs = await db.rawQuery( - "SELECT User.displayname, User.matrix_id, User.membership FROM User " + - " WHERE User.chat_id=? " + - " AND (User.membership='join' OR User.membership='invite') " + - " AND User.matrix_id!=? ", + "SELECT Users.displayname, Users.matrix_id, Users.membership FROM Users " + + " WHERE Users.chat_id=? " + + " AND (Users.membership='join' OR Users.membership='invite') " + + " AND Users.matrix_id!=? ", [roomID, client.userID]); if (rs.length > 0) { displayname = ""; @@ -576,7 +578,7 @@ class Store { /// the room or the own user wasn't found. Future getPowerLevel(String roomID) async { List> res = await db.rawQuery( - "SELECT power_level FROM User WHERE matrix_id=? AND chat_id=?", + "SELECT power_level FROM Users WHERE matrix_id=? AND chat_id=?", [roomID, client.userID]); if (res.length != 1) return null; return res[0]["power_level"]; @@ -585,7 +587,7 @@ class Store { /// Returns the power levels from all users for the given [roomID]. Future> getPowerLevels(String roomID) async { List> res = await db.rawQuery( - "SELECT matrix_id, power_level FROM User WHERE chat_id=?", + "SELECT matrix_id, power_level FROM Users WHERE chat_id=?", [roomID, client.userID]); Map powerMap = {}; for (int i = 0; i < res.length; i++) @@ -686,7 +688,7 @@ class Store { 'UNIQUE(id))'; /// The database sheme for the User class. - static final String UserScheme = 'CREATE TABLE IF NOT EXISTS User(' + + static final String UserScheme = 'CREATE TABLE IF NOT EXISTS Users(' + 'chat_id TEXT, ' + // The chat id of this membership 'matrix_id TEXT, ' + // The matrix id of this user 'displayname TEXT, ' + diff --git a/lib/src/Timeline.dart b/lib/src/Timeline.dart index 9933bad..dc519b0 100644 --- a/lib/src/Timeline.dart +++ b/lib/src/Timeline.dart @@ -22,6 +22,7 @@ */ import 'dart:async'; + import 'Event.dart'; import 'Room.dart'; import 'User.dart'; @@ -94,7 +95,9 @@ class Timeline { } sortAndUpdate(); } catch (e) { - print("[WARNING] (_handleEventUpdate) ${e.toString()}"); + if (room.client.debug) { + print("[WARNING] (_handleEventUpdate) ${e.toString()}"); + } } } diff --git a/lib/src/User.dart b/lib/src/User.dart index cfd93c6..907bdea 100644 --- a/lib/src/User.dart +++ b/lib/src/User.dart @@ -21,9 +21,13 @@ * along with famedlysdk. If not, see . */ +import 'package:famedlysdk/src/Room.dart'; import 'package:famedlysdk/src/responses/ErrorResponse.dart'; import 'package:famedlysdk/src/utils/MxContent.dart'; -import 'package:famedlysdk/src/Room.dart'; + +import 'Connection.dart'; + +enum Membership { join, invite, leave, ban } /// Represents a Matrix User which may be a participant in a Matrix Room. class User { @@ -38,7 +42,7 @@ class User { /// invite /// leave /// ban - String membership; + Membership membership; /// The avatar if the user has one. MxContent avatarUrl; @@ -53,7 +57,7 @@ class User { final Room room; @Deprecated("Use membership instead!") - String get status => membership; + String get status => membership.toString().split('.').last; @Deprecated("Use ID instead!") String get mxid => id; @@ -81,7 +85,12 @@ class User { return User(json['matrix_id'] ?? json['sender'], displayName: json['displayname'], avatarUrl: MxContent(json['avatar_url']), - membership: json['membership'], + membership: Membership.values.firstWhere((e) { + if (json["membership"] != null) { + return e.toString() == 'Membership.' + json['membership']; + } + return false; + }, orElse: () => null), powerLevel: json['power_level'], room: room); } @@ -132,12 +141,14 @@ class User { if (roomID != null) return roomID; // Start a new direct chat - final dynamic resp = await room.client.connection - .jsonRequest(type: "POST", action: "/client/r0/createRoom", data: { - "invite": [id], - "is_direct": true, - "preset": "trusted_private_chat" - }); + final dynamic resp = await room.client.connection.jsonRequest( + type: HTTPType.POST, + action: "/client/r0/createRoom", + data: { + "invite": [id], + "is_direct": true, + "preset": "trusted_private_chat" + }); if (resp is ErrorResponse) { room.client.connection.onError.add(resp); diff --git a/lib/src/sync/RoomUpdate.dart b/lib/src/sync/RoomUpdate.dart index 1b1db37..2f8abcb 100644 --- a/lib/src/sync/RoomUpdate.dart +++ b/lib/src/sync/RoomUpdate.dart @@ -21,6 +21,8 @@ * along with famedlysdk. If not, see . */ +import '../User.dart'; + /// Represents a new room or an update for an /// already known room. class RoomUpdate { @@ -28,7 +30,7 @@ class RoomUpdate { final String id; /// The current membership state of the user in this room. - final String membership; + final Membership membership; /// Represents the number of unead notifications. This probably doesn't fit the number /// of unread messages. diff --git a/test/Client_test.dart b/test/Client_test.dart index f2d0d31..64a5104 100644 --- a/test/Client_test.dart +++ b/test/Client_test.dart @@ -21,16 +21,18 @@ * along with famedlysdk. If not, see . */ -import 'package:famedlysdk/src/responses/PushrulesResponse.dart'; -import 'package:flutter_test/flutter_test.dart'; +import 'dart:async'; + import 'package:famedlysdk/src/Client.dart'; import 'package:famedlysdk/src/Connection.dart'; import 'package:famedlysdk/src/User.dart'; +import 'package:famedlysdk/src/responses/ErrorResponse.dart'; +import 'package:famedlysdk/src/responses/PushrulesResponse.dart'; import 'package:famedlysdk/src/sync/EventUpdate.dart'; import 'package:famedlysdk/src/sync/RoomUpdate.dart'; import 'package:famedlysdk/src/sync/UserUpdate.dart'; -import 'package:famedlysdk/src/responses/ErrorResponse.dart'; -import 'dart:async'; +import 'package:flutter_test/flutter_test.dart'; + import 'FakeMatrixApi.dart'; void main() { @@ -70,7 +72,7 @@ void main() { expect(checkError.errcode, "NO_RESPONSE"); final resp = await matrix.connection - .jsonRequest(type: "POST", action: "/client/r0/login", data: { + .jsonRequest(type: HTTPType.POST, action: "/client/r0/login", data: { "type": "m.login.password", "user": "test", "password": "1234", @@ -108,13 +110,13 @@ void main() { test('Try to get ErrorResponse', () async { final resp = await matrix.connection - .jsonRequest(type: "PUT", action: "/non/existing/path"); + .jsonRequest(type: HTTPType.PUT, action: "/non/existing/path"); expect(resp is ErrorResponse, true); }); test('Logout', () async { final dynamic resp = await matrix.connection - .jsonRequest(type: "POST", action: "/client/r0/logout"); + .jsonRequest(type: HTTPType.POST, action: "/client/r0/logout"); expect(resp is ErrorResponse, false); Future loginStateFuture = @@ -143,21 +145,21 @@ void main() { expect(roomUpdateList.length, 3); expect(roomUpdateList[0].id == "!726s6s6q:example.com", true); - expect(roomUpdateList[0].membership == "join", true); + expect(roomUpdateList[0].membership == Membership.join, true); expect(roomUpdateList[0].prev_batch == "t34-23535_0_0", true); expect(roomUpdateList[0].limitedTimeline == true, true); expect(roomUpdateList[0].notification_count == 2, true); expect(roomUpdateList[0].highlight_count == 2, true); expect(roomUpdateList[1].id == "!696r7674:example.com", true); - expect(roomUpdateList[1].membership == "invite", true); + expect(roomUpdateList[1].membership == Membership.invite, true); expect(roomUpdateList[1].prev_batch == "", true); expect(roomUpdateList[1].limitedTimeline == false, true); expect(roomUpdateList[1].notification_count == 0, true); expect(roomUpdateList[1].highlight_count == 0, true); expect(roomUpdateList[2].id == "!5345234234:example.com", true); - expect(roomUpdateList[2].membership == "leave", 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); @@ -250,7 +252,7 @@ void main() { Future loginStateFuture = matrix.connection.onLoginStateChanged.stream.first; await matrix.connection - .jsonRequest(type: "DELETE", action: "/unknown/token"); + .jsonRequest(type: HTTPType.DELETE, action: "/unknown/token"); LoginState state = await loginStateFuture; expect(state, LoginState.loggedOut); diff --git a/test/Event_test.dart b/test/Event_test.dart index dc75040..84ad61c 100644 --- a/test/Event_test.dart +++ b/test/Event_test.dart @@ -21,8 +21,9 @@ * along with famedlysdk. If not, see . */ -import 'package:flutter_test/flutter_test.dart'; import 'package:famedlysdk/src/Event.dart'; +import 'package:famedlysdk/src/User.dart'; +import 'package:flutter_test/flutter_test.dart'; void main() { /// All Tests related to the Event @@ -33,7 +34,7 @@ void main() { final String senderID = "@alice:server.abc"; final String senderDisplayname = "Alice"; final String empty = ""; - final String membership = "join"; + final Membership membership = Membership.join; final String type = "m.room.message"; final String msgtype = "m.text"; final String body = "Hello World"; @@ -47,7 +48,7 @@ void main() { "matrix_id": senderID, "displayname": senderDisplayname, "avatar_url": empty, - "membership": membership, + "membership": membership.toString().split('.').last, "origin_server_ts": timestamp, "state_key": empty, "type": type, diff --git a/test/RoomList_test.dart b/test/RoomList_test.dart index 7ece531..52f4f28 100644 --- a/test/RoomList_test.dart +++ b/test/RoomList_test.dart @@ -21,12 +21,13 @@ * along with famedlysdk. If not, see . */ -import 'package:flutter_test/flutter_test.dart'; import 'package:famedlysdk/src/Client.dart'; import 'package:famedlysdk/src/RoomList.dart'; +import 'package:famedlysdk/src/User.dart'; import 'package:famedlysdk/src/sync/EventUpdate.dart'; import 'package:famedlysdk/src/sync/RoomUpdate.dart'; import 'package:famedlysdk/src/utils/ChatTime.dart'; +import 'package:flutter_test/flutter_test.dart'; void main() { /// All Tests related to the MxContent @@ -59,7 +60,7 @@ void main() { client.connection.onRoomUpdate.add(RoomUpdate( id: roomID, - membership: "join", + membership: Membership.join, notification_count: 2, highlight_count: 1, limitedTimeline: false, @@ -74,7 +75,7 @@ void main() { expect(roomList.rooms.length, 1); expect(roomList.rooms[0].id, roomID); - expect(roomList.rooms[0].membership, "join"); + expect(roomList.rooms[0].membership, Membership.join); expect(roomList.rooms[0].notificationCount, 2); expect(roomList.rooms[0].highlightCount, 1); expect(roomList.rooms[0].prev_batch, "1234"); @@ -104,7 +105,7 @@ void main() { client.connection.onRoomUpdate.add(RoomUpdate( id: "1", - membership: "join", + membership: Membership.join, notification_count: 2, highlight_count: 1, limitedTimeline: false, @@ -112,7 +113,7 @@ void main() { )); client.connection.onRoomUpdate.add(RoomUpdate( id: "2", - membership: "join", + membership: Membership.join, notification_count: 2, highlight_count: 1, limitedTimeline: false, @@ -193,7 +194,7 @@ void main() { client.connection.onRoomUpdate.add(RoomUpdate( id: "1", - membership: "join", + membership: Membership.join, notification_count: 2, highlight_count: 1, limitedTimeline: false, @@ -201,7 +202,7 @@ void main() { )); client.connection.onRoomUpdate.add(RoomUpdate( id: "2", - membership: "leave", + membership: Membership.leave, notification_count: 2, highlight_count: 1, limitedTimeline: false, diff --git a/test/Room_test.dart b/test/Room_test.dart index 02c3049..d50d479 100644 --- a/test/Room_test.dart +++ b/test/Room_test.dart @@ -21,11 +21,12 @@ * along with famedlysdk. If not, see . */ -import 'package:flutter_test/flutter_test.dart'; -import 'package:famedlysdk/src/Room.dart'; import 'package:famedlysdk/src/Client.dart'; import 'package:famedlysdk/src/Event.dart'; +import 'package:famedlysdk/src/Room.dart'; import 'package:famedlysdk/src/User.dart'; +import 'package:flutter_test/flutter_test.dart'; + import 'FakeMatrixApi.dart'; void main() { @@ -50,7 +51,7 @@ void main() { test("Create from json", () async { final String id = "!localpart:server.abc"; final String name = "My Room"; - final String membership = "join"; + final Membership membership = Membership.join; final String topic = "This is my own room"; final int unread = DateTime.now().millisecondsSinceEpoch; final int notificationCount = 2; @@ -69,7 +70,7 @@ void main() { final Map jsonObj = { "id": id, - "membership": membership, + "membership": membership.toString().split('.').last, "topic": name, "description": topic, "avatar_url": "", @@ -137,7 +138,7 @@ void main() { User user = participants[0]; expect(user.id, "@alice:example.org"); expect(user.displayName, "Alice Margatroid"); - expect(user.membership, "join"); + expect(user.membership, Membership.join); expect(user.avatarUrl.mxc, "mxc://example.org/SEsfnsuifSDFSSEF"); expect(user.room.id, "!localpart:server.abc"); }); diff --git a/test/User_test.dart b/test/User_test.dart index e6d0ae7..dcd8b59 100644 --- a/test/User_test.dart +++ b/test/User_test.dart @@ -21,15 +21,15 @@ * along with famedlysdk. If not, see . */ -import 'package:flutter_test/flutter_test.dart'; import 'package:famedlysdk/src/User.dart'; +import 'package:flutter_test/flutter_test.dart'; void main() { /// All Tests related to the Event group("User", () { test("Create from json", () async { final String id = "@alice:server.abc"; - final String membership = "join"; + final Membership membership = Membership.join; final String displayName = "Alice"; final String avatarUrl = ""; final int powerLevel = 50; @@ -38,7 +38,7 @@ void main() { "matrix_id": id, "displayname": displayName, "avatar_url": avatarUrl, - "membership": membership, + "membership": membership.toString().split('.').last, "power_level": powerLevel, };