[Store] Make database private
This commit is contained in:
parent
ea83718682
commit
0a46ec9551
22
README.md
22
README.md
|
@ -19,22 +19,36 @@ The API is documented here: [famedly.gitlab.io/famedlysdk](https://famedly.gitla
|
||||||
ref: 77be6102f6cbb2e01adc28f9caa3aa583f914235
|
ref: 77be6102f6cbb2e01adc28f9caa3aa583f914235
|
||||||
```
|
```
|
||||||
|
|
||||||
|
(Optional) Import the store
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
famedlysdk_store_sqflite:
|
||||||
|
git:
|
||||||
|
url: https://gitlab.com/famedly/libraries/famedlysdk-store.git
|
||||||
|
ref: 17fbbed1ea9b04ca041e9479d4e74bb4d2c78d55
|
||||||
|
```
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:famedlysdk/famedlysdk.dart';
|
import 'package:famedlysdk/famedlysdk.dart';
|
||||||
|
import 'package:famedlysdk-store/ famedlysdk_store_sqflite.dart'; // Optional
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Access the MatrixState object by calling Matrix.of with your current BuildContext:
|
2. Create a new client:
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
Client matrix = Client("famedly talk");
|
Client matrix = Client("HappyChat");
|
||||||
|
```
|
||||||
|
|
||||||
|
Or with store:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
Client matrix = Client("HappyChat", store: Store(this));
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Connect to a Matrix Homeserver and listen to the streams:
|
3. Connect to a Matrix Homeserver and listen to the streams:
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
|
|
||||||
matrix.connection.onLoginStateChanged.stream.listen((bool loginState){
|
matrix.connection.onLoginStateChanged.stream.listen((bool loginState){
|
||||||
print("LoginState: ${loginState.toString()}");
|
print("LoginState: ${loginState.toString()}");
|
||||||
});
|
});
|
||||||
|
|
|
@ -30,6 +30,7 @@ export 'package:famedlysdk/src/sync/RoomUpdate.dart';
|
||||||
export 'package:famedlysdk/src/sync/EventUpdate.dart';
|
export 'package:famedlysdk/src/sync/EventUpdate.dart';
|
||||||
export 'package:famedlysdk/src/sync/UserUpdate.dart';
|
export 'package:famedlysdk/src/sync/UserUpdate.dart';
|
||||||
export 'package:famedlysdk/src/utils/ChatTime.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/MxContent.dart';
|
||||||
export 'package:famedlysdk/src/AccountData.dart';
|
export 'package:famedlysdk/src/AccountData.dart';
|
||||||
export 'package:famedlysdk/src/Client.dart';
|
export 'package:famedlysdk/src/Client.dart';
|
||||||
|
@ -40,6 +41,6 @@ export 'package:famedlysdk/src/Room.dart';
|
||||||
export 'package:famedlysdk/src/RoomAccountData.dart';
|
export 'package:famedlysdk/src/RoomAccountData.dart';
|
||||||
export 'package:famedlysdk/src/RoomList.dart';
|
export 'package:famedlysdk/src/RoomList.dart';
|
||||||
export 'package:famedlysdk/src/RoomState.dart';
|
export 'package:famedlysdk/src/RoomState.dart';
|
||||||
export 'package:famedlysdk/src/Store.dart';
|
export 'package:famedlysdk/src/StoreAPI.dart';
|
||||||
export 'package:famedlysdk/src/Timeline.dart';
|
export 'package:famedlysdk/src/Timeline.dart';
|
||||||
export 'package:famedlysdk/src/User.dart';
|
export 'package:famedlysdk/src/User.dart';
|
||||||
|
|
|
@ -23,16 +23,17 @@
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:core';
|
import 'dart:core';
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:famedlysdk/src/AccountData.dart';
|
import 'package:famedlysdk/src/AccountData.dart';
|
||||||
import 'package:famedlysdk/src/Presence.dart';
|
import 'package:famedlysdk/src/Presence.dart';
|
||||||
|
import 'package:famedlysdk/src/StoreAPI.dart';
|
||||||
import 'package:famedlysdk/src/sync/UserUpdate.dart';
|
import 'package:famedlysdk/src/sync/UserUpdate.dart';
|
||||||
|
import 'package:famedlysdk/src/utils/MatrixFile.dart';
|
||||||
|
|
||||||
import 'Connection.dart';
|
import 'Connection.dart';
|
||||||
import 'Room.dart';
|
import 'Room.dart';
|
||||||
import 'RoomList.dart';
|
import 'RoomList.dart';
|
||||||
import 'Store.dart';
|
//import 'Store.dart';
|
||||||
import 'User.dart';
|
import 'User.dart';
|
||||||
import 'requests/SetPushersRequest.dart';
|
import 'requests/SetPushersRequest.dart';
|
||||||
import 'responses/ErrorResponse.dart';
|
import 'responses/ErrorResponse.dart';
|
||||||
|
@ -49,12 +50,12 @@ class Client {
|
||||||
Connection connection;
|
Connection connection;
|
||||||
|
|
||||||
/// Optional persistent store for all data.
|
/// Optional persistent store for all data.
|
||||||
Store store;
|
StoreAPI store;
|
||||||
|
|
||||||
Client(this.clientName, {this.debug = false}) {
|
Client(this.clientName, {this.debug = false, this.store}) {
|
||||||
connection = Connection(this);
|
connection = Connection(this);
|
||||||
|
|
||||||
if (this.clientName != "testclient") store = Store(this);
|
if (this.clientName != "testclient") store = null; //Store(this);
|
||||||
connection.onLoginStateChanged.stream.listen((loginState) {
|
connection.onLoginStateChanged.stream.listen((loginState) {
|
||||||
print("LoginState: ${loginState.toString()}");
|
print("LoginState: ${loginState.toString()}");
|
||||||
});
|
});
|
||||||
|
@ -333,7 +334,7 @@ class Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Uploads a new user avatar for this user. Returns ErrorResponse if something went wrong.
|
/// Uploads a new user avatar for this user. Returns ErrorResponse if something went wrong.
|
||||||
Future<dynamic> setAvatar(File file) async {
|
Future<dynamic> setAvatar(MatrixFile file) async {
|
||||||
final uploadResp = await connection.upload(file);
|
final uploadResp = await connection.upload(file);
|
||||||
if (uploadResp is ErrorResponse) return uploadResp;
|
if (uploadResp is ErrorResponse) return uploadResp;
|
||||||
final setAvatarResp = await connection.jsonRequest(
|
final setAvatarResp = await connection.jsonRequest(
|
||||||
|
|
|
@ -24,10 +24,10 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:core';
|
import 'dart:core';
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:famedlysdk/src/Room.dart';
|
import 'package:famedlysdk/src/Room.dart';
|
||||||
import 'package:famedlysdk/src/RoomList.dart';
|
import 'package:famedlysdk/src/RoomList.dart';
|
||||||
|
import 'package:famedlysdk/src/utils/MatrixFile.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:mime_type/mime_type.dart';
|
import 'package:mime_type/mime_type.dart';
|
||||||
|
@ -295,7 +295,7 @@ class Connection {
|
||||||
|
|
||||||
/// Uploads a file with the name [fileName] as base64 encoded to the server
|
/// Uploads a file with the name [fileName] as base64 encoded to the server
|
||||||
/// and returns the mxc url as a string or an [ErrorResponse].
|
/// and returns the mxc url as a string or an [ErrorResponse].
|
||||||
Future<dynamic> upload(File file) async {
|
Future<dynamic> upload(MatrixFile file) async {
|
||||||
List<int> fileBytes;
|
List<int> fileBytes;
|
||||||
if (client.homeserver != "https://fakeServer.notExisting")
|
if (client.homeserver != "https://fakeServer.notExisting")
|
||||||
fileBytes = await file.readAsBytes();
|
fileBytes = await file.readAsBytes();
|
||||||
|
|
|
@ -102,8 +102,7 @@ class Event extends RoomState {
|
||||||
Future<bool> remove() async {
|
Future<bool> remove() async {
|
||||||
if (status < 1) {
|
if (status < 1) {
|
||||||
if (room.client.store != null)
|
if (room.client.store != null)
|
||||||
await room.client.store.db
|
await room.client.store.removeEvent(eventId);
|
||||||
.rawDelete("DELETE FROM Events WHERE event_id=?", [eventId]);
|
|
||||||
|
|
||||||
room.client.connection.onEvent.add(EventUpdate(
|
room.client.connection.onEvent.add(EventUpdate(
|
||||||
roomID: room.id,
|
roomID: room.id,
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
* along with famedlysdk. If not, see <http://www.gnu.org/licenses/>.
|
* along with famedlysdk. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import 'dart:io';
|
|
||||||
import 'package:famedlysdk/src/Client.dart';
|
import 'package:famedlysdk/src/Client.dart';
|
||||||
import 'package:famedlysdk/src/Event.dart';
|
import 'package:famedlysdk/src/Event.dart';
|
||||||
import 'package:famedlysdk/src/RoomAccountData.dart';
|
import 'package:famedlysdk/src/RoomAccountData.dart';
|
||||||
|
@ -29,8 +28,9 @@ import 'package:famedlysdk/src/RoomState.dart';
|
||||||
import 'package:famedlysdk/src/responses/ErrorResponse.dart';
|
import 'package:famedlysdk/src/responses/ErrorResponse.dart';
|
||||||
import 'package:famedlysdk/src/sync/EventUpdate.dart';
|
import 'package:famedlysdk/src/sync/EventUpdate.dart';
|
||||||
import 'package:famedlysdk/src/utils/ChatTime.dart';
|
import 'package:famedlysdk/src/utils/ChatTime.dart';
|
||||||
|
import 'package:famedlysdk/src/utils/MatrixFile.dart';
|
||||||
import 'package:famedlysdk/src/utils/MxContent.dart';
|
import 'package:famedlysdk/src/utils/MxContent.dart';
|
||||||
import 'package:image/image.dart';
|
//import 'package:image/image.dart';
|
||||||
import 'package:mime_type/mime_type.dart';
|
import 'package:mime_type/mime_type.dart';
|
||||||
|
|
||||||
import './User.dart';
|
import './User.dart';
|
||||||
|
@ -250,19 +250,19 @@ class Room {
|
||||||
Future<String> sendTextEvent(String message, {String txid = null}) =>
|
Future<String> sendTextEvent(String message, {String txid = null}) =>
|
||||||
sendEvent({"msgtype": "m.text", "body": message}, txid: txid);
|
sendEvent({"msgtype": "m.text", "body": message}, txid: txid);
|
||||||
|
|
||||||
Future<String> sendFileEvent(File file, String msgType,
|
Future<String> sendFileEvent(MatrixFile file, String msgType,
|
||||||
{String txid = null}) async {
|
{String txid = null}) async {
|
||||||
|
String fileName = file.path.split("/").last;
|
||||||
// Try to get the size of the file
|
// Try to get the size of the file
|
||||||
int size;
|
int size;
|
||||||
try {
|
try {
|
||||||
size = (await file.readAsBytes()).length;
|
size = file.bytes.length;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print("[UPLOAD] Could not get size. Reason: ${e.toString()}");
|
print("[UPLOAD] Could not get size. Reason: ${e.toString()}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upload file
|
// Upload file
|
||||||
String fileName = file.path.split("/").last;
|
String mimeType = mime(file.path);
|
||||||
String mimeType = mime(fileName);
|
|
||||||
final dynamic uploadResp = await client.connection.upload(file);
|
final dynamic uploadResp = await client.connection.upload(file);
|
||||||
if (uploadResp is ErrorResponse) return null;
|
if (uploadResp is ErrorResponse) return null;
|
||||||
|
|
||||||
|
@ -284,12 +284,9 @@ class Room {
|
||||||
return await sendEvent(content, txid: txid);
|
return await sendEvent(content, txid: txid);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> sendImageEvent(File file, {String txid = null}) async {
|
Future<String> sendImageEvent(MatrixFile file,
|
||||||
String path = file.path;
|
{String txid = null, int width, int height}) async {
|
||||||
String fileName = path.split("/").last;
|
String fileName = file.path.split("/").last;
|
||||||
|
|
||||||
Image image = await decodeImage(await file.readAsBytes());
|
|
||||||
|
|
||||||
final dynamic uploadResp = await client.connection.upload(file);
|
final dynamic uploadResp = await client.connection.upload(file);
|
||||||
if (uploadResp is ErrorResponse) return null;
|
if (uploadResp is ErrorResponse) return null;
|
||||||
Map<String, dynamic> content = {
|
Map<String, dynamic> content = {
|
||||||
|
@ -297,10 +294,10 @@ class Room {
|
||||||
"body": fileName,
|
"body": fileName,
|
||||||
"url": uploadResp,
|
"url": uploadResp,
|
||||||
"info": {
|
"info": {
|
||||||
"size": image.getBytes().length,
|
"size": file.bytes.length,
|
||||||
"mimetype": mime(file.path),
|
"mimetype": mime(fileName),
|
||||||
"w": image.width,
|
"w": width,
|
||||||
"h": image.height,
|
"h": height,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return await sendEvent(content, txid: txid);
|
return await sendEvent(content, txid: txid);
|
||||||
|
@ -719,7 +716,7 @@ class Room {
|
||||||
|
|
||||||
/// Uploads a new user avatar for this room. Returns ErrorResponse if something went wrong
|
/// Uploads a new user avatar for this room. Returns ErrorResponse if something went wrong
|
||||||
/// and the event ID otherwise.
|
/// and the event ID otherwise.
|
||||||
Future<dynamic> setAvatar(File file) async {
|
Future<dynamic> setAvatar(MatrixFile file) async {
|
||||||
final uploadResp = await client.connection.upload(file);
|
final uploadResp = await client.connection.upload(file);
|
||||||
if (uploadResp is ErrorResponse) return uploadResp;
|
if (uploadResp is ErrorResponse) return uploadResp;
|
||||||
final setAvatarResp = await client.connection.jsonRequest(
|
final setAvatarResp = await client.connection.jsonRequest(
|
||||||
|
|
|
@ -1,533 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2019 Zender & Kurtz GbR.
|
|
||||||
*
|
|
||||||
* Authors:
|
|
||||||
* Christian Pauly <krille@famedly.com>
|
|
||||||
* Marcel Radzio <mtrnord@famedly.com>
|
|
||||||
*
|
|
||||||
* 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 <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:convert';
|
|
||||||
import 'dart:core';
|
|
||||||
|
|
||||||
import 'package:famedlysdk/src/AccountData.dart';
|
|
||||||
import 'package:famedlysdk/src/Presence.dart';
|
|
||||||
import 'package:famedlysdk/src/RoomState.dart';
|
|
||||||
import 'package:path/path.dart' as p;
|
|
||||||
import 'package:sqflite/sqflite.dart';
|
|
||||||
|
|
||||||
import 'Client.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.
|
|
||||||
class Store {
|
|
||||||
final Client client;
|
|
||||||
|
|
||||||
Store(this.client) {
|
|
||||||
_init();
|
|
||||||
}
|
|
||||||
|
|
||||||
Database _db;
|
|
||||||
|
|
||||||
/// SQLite database for all persistent data. It is recommended to extend this
|
|
||||||
/// SDK instead of writing direct queries to the database.
|
|
||||||
Database get db => _db;
|
|
||||||
|
|
||||||
_init() async {
|
|
||||||
var databasePath = await getDatabasesPath();
|
|
||||||
String path = p.join(databasePath, "FluffyMatrix.db");
|
|
||||||
_db = await openDatabase(path, version: 14,
|
|
||||||
onCreate: (Database db, int version) async {
|
|
||||||
await createTables(db);
|
|
||||||
}, onUpgrade: (Database db, int oldVersion, int newVersion) async {
|
|
||||||
if (client.debug)
|
|
||||||
print(
|
|
||||||
"[Store] Migrate databse from version $oldVersion to $newVersion");
|
|
||||||
if (oldVersion != newVersion) {
|
|
||||||
await schemes.forEach((String name, String scheme) async {
|
|
||||||
if (name != "Clients") await db.execute("DROP TABLE IF EXISTS $name");
|
|
||||||
});
|
|
||||||
await createTables(db);
|
|
||||||
await db.rawUpdate("UPDATE Clients SET prev_batch='' WHERE client=?",
|
|
||||||
[client.clientName]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
await _db.rawUpdate("UPDATE Events SET status=-1 WHERE status=0");
|
|
||||||
|
|
||||||
List<Map> list = await _db
|
|
||||||
.rawQuery("SELECT * FROM Clients WHERE client=?", [client.clientName]);
|
|
||||||
if (list.length == 1) {
|
|
||||||
var clientList = list[0];
|
|
||||||
client.connection.connect(
|
|
||||||
newToken: clientList["token"],
|
|
||||||
newHomeserver: clientList["homeserver"],
|
|
||||||
newUserID: clientList["matrix_id"],
|
|
||||||
newDeviceID: clientList["device_id"],
|
|
||||||
newDeviceName: clientList["device_name"],
|
|
||||||
newLazyLoadMembers: clientList["lazy_load_members"] == 1,
|
|
||||||
newMatrixVersions: clientList["matrix_versions"].toString().split(","),
|
|
||||||
newPrevBatch: clientList["prev_batch"].toString().isEmpty
|
|
||||||
? null
|
|
||||||
: clientList["prev_batch"],
|
|
||||||
);
|
|
||||||
if (client.debug)
|
|
||||||
print("[Store] Restore client credentials of ${client.userID}");
|
|
||||||
} else
|
|
||||||
client.connection.onLoginStateChanged.add(LoginState.loggedOut);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> createTables(Database db) async {
|
|
||||||
await schemes.forEach((String name, String scheme) async {
|
|
||||||
await db.execute(scheme);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<String> queryPrevBatch() async {
|
|
||||||
List<Map> list = await txn.rawQuery(
|
|
||||||
"SELECT prev_batch FROM Clients WHERE client=?", [client.clientName]);
|
|
||||||
return list[0]["prev_batch"];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Will be automatically called when the client is logged in successfully.
|
|
||||||
Future<void> storeClient() async {
|
|
||||||
await _db
|
|
||||||
.rawInsert('INSERT OR IGNORE INTO Clients VALUES(?,?,?,?,?,?,?,?,?)', [
|
|
||||||
client.clientName,
|
|
||||||
client.accessToken,
|
|
||||||
client.homeserver,
|
|
||||||
client.userID,
|
|
||||||
client.deviceID,
|
|
||||||
client.deviceName,
|
|
||||||
client.prevBatch,
|
|
||||||
client.matrixVersions.join(","),
|
|
||||||
client.lazyLoadMembers,
|
|
||||||
]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clears all tables from the database.
|
|
||||||
Future<void> clear() async {
|
|
||||||
await _db
|
|
||||||
.rawDelete("DELETE FROM Clients WHERE client=?", [client.clientName]);
|
|
||||||
await schemes.forEach((String name, String scheme) async {
|
|
||||||
if (name != "Clients") await db.rawDelete("DELETE FROM $name");
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Transaction txn;
|
|
||||||
|
|
||||||
Future<void> transaction(Future<void> queries()) async {
|
|
||||||
return client.store.db.transaction((txnObj) async {
|
|
||||||
txn = txnObj;
|
|
||||||
await queries();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Will be automatically called on every synchronisation. Must be called inside of
|
|
||||||
// /// [transaction].
|
|
||||||
Future<void> storePrevBatch(dynamic sync) {
|
|
||||||
txn.rawUpdate("UPDATE Clients SET prev_batch=? WHERE client=?",
|
|
||||||
[client.prevBatch, client.clientName]);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> storeRoomPrevBatch(Room room) async {
|
|
||||||
await _db.rawUpdate("UPDATE Rooms SET prev_batch=? WHERE room_id=?",
|
|
||||||
[room.prev_batch, room.id]);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stores a RoomUpdate object in the database. Must be called inside of
|
|
||||||
/// [transaction].
|
|
||||||
Future<void> storeRoomUpdate(RoomUpdate roomUpdate) {
|
|
||||||
// Insert the chat into the database if not exists
|
|
||||||
txn.rawInsert(
|
|
||||||
"INSERT OR IGNORE INTO Rooms " + "VALUES(?, ?, 0, 0, '', 0, 0, '') ",
|
|
||||||
[roomUpdate.id, roomUpdate.membership.toString().split('.').last]);
|
|
||||||
|
|
||||||
// Update the notification counts and the limited timeline boolean and the summary
|
|
||||||
String updateQuery =
|
|
||||||
"UPDATE Rooms SET highlight_count=?, notification_count=?, membership=?";
|
|
||||||
List<dynamic> updateArgs = [
|
|
||||||
roomUpdate.highlight_count,
|
|
||||||
roomUpdate.notification_count,
|
|
||||||
roomUpdate.membership.toString().split('.').last
|
|
||||||
];
|
|
||||||
if (roomUpdate.summary?.mJoinedMemberCount != null) {
|
|
||||||
updateQuery += ", joined_member_count=?";
|
|
||||||
updateArgs.add(roomUpdate.summary.mJoinedMemberCount);
|
|
||||||
}
|
|
||||||
if (roomUpdate.summary?.mInvitedMemberCount != null) {
|
|
||||||
updateQuery += ", invited_member_count=?";
|
|
||||||
updateArgs.add(roomUpdate.summary.mInvitedMemberCount);
|
|
||||||
}
|
|
||||||
if (roomUpdate.summary?.mHeroes != null) {
|
|
||||||
updateQuery += ", heroes=?";
|
|
||||||
updateArgs.add(roomUpdate.summary.mHeroes.join(","));
|
|
||||||
}
|
|
||||||
updateQuery += " WHERE room_id=?";
|
|
||||||
updateArgs.add(roomUpdate.id);
|
|
||||||
txn.rawUpdate(updateQuery, updateArgs);
|
|
||||||
|
|
||||||
// Is the timeline limited? Then all previous messages should be
|
|
||||||
// removed from the database!
|
|
||||||
if (roomUpdate.limitedTimeline) {
|
|
||||||
txn.rawDelete("DELETE FROM Events WHERE room_id=?", [roomUpdate.id]);
|
|
||||||
txn.rawUpdate("UPDATE Rooms SET prev_batch=? WHERE room_id=?",
|
|
||||||
[roomUpdate.prev_batch, roomUpdate.id]);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stores an UserUpdate object in the database. Must be called inside of
|
|
||||||
/// [transaction].
|
|
||||||
Future<void> storeUserEventUpdate(UserUpdate userUpdate) {
|
|
||||||
if (userUpdate.type == "account_data")
|
|
||||||
txn.rawInsert("INSERT OR REPLACE INTO AccountData VALUES(?, ?)", [
|
|
||||||
userUpdate.eventType,
|
|
||||||
json.encode(userUpdate.content["content"]),
|
|
||||||
]);
|
|
||||||
else if (userUpdate.type == "presence")
|
|
||||||
txn.rawInsert("INSERT OR REPLACE INTO Presences VALUES(?, ?, ?)", [
|
|
||||||
userUpdate.eventType,
|
|
||||||
userUpdate.content["sender"],
|
|
||||||
json.encode(userUpdate.content["content"]),
|
|
||||||
]);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stores an EventUpdate object in the database. Must be called inside of
|
|
||||||
/// [transaction].
|
|
||||||
Future<void> storeEventUpdate(EventUpdate eventUpdate) {
|
|
||||||
Map<String, dynamic> eventContent = eventUpdate.content;
|
|
||||||
String type = eventUpdate.type;
|
|
||||||
String chat_id = eventUpdate.roomID;
|
|
||||||
|
|
||||||
// Get the state_key for m.room.member events
|
|
||||||
String state_key = "";
|
|
||||||
if (eventContent["state_key"] is String) {
|
|
||||||
state_key = eventContent["state_key"];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type == "timeline" || type == "history") {
|
|
||||||
// calculate the status
|
|
||||||
num status = 2;
|
|
||||||
if (eventContent["status"] is num) status = eventContent["status"];
|
|
||||||
|
|
||||||
// Save the event in the database
|
|
||||||
if ((status == 1 || status == -1) &&
|
|
||||||
eventContent["unsigned"] is Map<String, dynamic> &&
|
|
||||||
eventContent["unsigned"]["transaction_id"] is String)
|
|
||||||
txn.rawUpdate(
|
|
||||||
"UPDATE Events SET status=?, event_id=? WHERE event_id=?", [
|
|
||||||
status,
|
|
||||||
eventContent["event_id"],
|
|
||||||
eventContent["unsigned"]["transaction_id"]
|
|
||||||
]);
|
|
||||||
else
|
|
||||||
txn.rawInsert(
|
|
||||||
"INSERT OR REPLACE INTO Events VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
||||||
[
|
|
||||||
eventContent["event_id"],
|
|
||||||
chat_id,
|
|
||||||
eventContent["origin_server_ts"],
|
|
||||||
eventContent["sender"],
|
|
||||||
eventContent["type"],
|
|
||||||
json.encode(eventContent["unsigned"] ?? ""),
|
|
||||||
json.encode(eventContent["content"]),
|
|
||||||
json.encode(eventContent["prevContent"]),
|
|
||||||
eventContent["state_key"],
|
|
||||||
status
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Is there a transaction id? Then delete the event with this id.
|
|
||||||
if (status != -1 &&
|
|
||||||
eventUpdate.content.containsKey("unsigned") &&
|
|
||||||
eventUpdate.content["unsigned"]["transaction_id"] is String)
|
|
||||||
txn.rawDelete("DELETE FROM Events WHERE event_id=?",
|
|
||||||
[eventUpdate.content["unsigned"]["transaction_id"]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type == "history") return null;
|
|
||||||
|
|
||||||
if (eventUpdate.content["event_id"] != null) {
|
|
||||||
txn.rawInsert(
|
|
||||||
"INSERT OR REPLACE INTO RoomStates VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
||||||
[
|
|
||||||
eventContent["event_id"],
|
|
||||||
chat_id,
|
|
||||||
eventContent["origin_server_ts"],
|
|
||||||
eventContent["sender"],
|
|
||||||
state_key,
|
|
||||||
json.encode(eventContent["unsigned"] ?? ""),
|
|
||||||
json.encode(eventContent["prev_content"] ?? ""),
|
|
||||||
eventContent["type"],
|
|
||||||
json.encode(eventContent["content"]),
|
|
||||||
]);
|
|
||||||
} else
|
|
||||||
txn.rawInsert("INSERT OR REPLACE INTO RoomAccountData VALUES(?, ?, ?)", [
|
|
||||||
eventContent["type"],
|
|
||||||
chat_id,
|
|
||||||
json.encode(eventContent["content"]),
|
|
||||||
]);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a User object by a given Matrix ID and a Room.
|
|
||||||
Future<User> getUser({String matrixID, Room room}) async {
|
|
||||||
List<Map<String, dynamic>> res = await db.rawQuery(
|
|
||||||
"SELECT * FROM RoomStates WHERE state_key=? AND room_id=?",
|
|
||||||
[matrixID, room.id]);
|
|
||||||
if (res.length != 1) return null;
|
|
||||||
return RoomState.fromJson(res[0], room).asUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Loads all Users in the database to provide a contact list
|
|
||||||
/// except users who are in the Room with the ID [exceptRoomID].
|
|
||||||
Future<List<User>> loadContacts({String exceptRoomID = ""}) async {
|
|
||||||
List<Map<String, dynamic>> res = await db.rawQuery(
|
|
||||||
"SELECT * FROM RoomStates WHERE state_key LIKE '@%:%' AND state_key!=? AND room_id!=? GROUP BY state_key ORDER BY state_key",
|
|
||||||
[client.userID, exceptRoomID]);
|
|
||||||
List<User> userList = [];
|
|
||||||
for (int i = 0; i < res.length; i++)
|
|
||||||
userList
|
|
||||||
.add(RoomState.fromJson(res[i], Room(id: "", client: client)).asUser);
|
|
||||||
return userList;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns all users of a room by a given [roomID].
|
|
||||||
Future<List<User>> loadParticipants(Room room) async {
|
|
||||||
List<Map<String, dynamic>> res = await db.rawQuery(
|
|
||||||
"SELECT * " +
|
|
||||||
" FROM RoomStates " +
|
|
||||||
" WHERE room_id=? " +
|
|
||||||
" AND type='m.room.member'",
|
|
||||||
[room.id]);
|
|
||||||
|
|
||||||
List<User> participants = [];
|
|
||||||
|
|
||||||
for (num i = 0; i < res.length; i++) {
|
|
||||||
participants.add(RoomState.fromJson(res[i], room).asUser);
|
|
||||||
}
|
|
||||||
|
|
||||||
return participants;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a list of events for the given room and sets all participants.
|
|
||||||
Future<List<Event>> getEventList(Room room) async {
|
|
||||||
List<Map<String, dynamic>> eventRes = await db.rawQuery(
|
|
||||||
"SELECT * " +
|
|
||||||
" FROM Events " +
|
|
||||||
" WHERE room_id=?" +
|
|
||||||
" GROUP BY event_id " +
|
|
||||||
" ORDER BY origin_server_ts DESC",
|
|
||||||
[room.id]);
|
|
||||||
|
|
||||||
List<Event> eventList = [];
|
|
||||||
|
|
||||||
for (num i = 0; i < eventRes.length; i++)
|
|
||||||
eventList.add(Event.fromJson(eventRes[i], room));
|
|
||||||
|
|
||||||
return eventList;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns all rooms, the client is participating. Excludes left rooms.
|
|
||||||
Future<List<Room>> getRoomList({bool onlyLeft = false}) async {
|
|
||||||
List<Map<String, dynamic>> res = await db.rawQuery("SELECT * " +
|
|
||||||
" FROM Rooms" +
|
|
||||||
" WHERE membership" +
|
|
||||||
(onlyLeft ? "=" : "!=") +
|
|
||||||
"'leave' " +
|
|
||||||
" GROUP BY room_id ");
|
|
||||||
List<Room> roomList = [];
|
|
||||||
for (num i = 0; i < res.length; i++) {
|
|
||||||
Room room = await Room.getRoomFromTableRow(res[i], client,
|
|
||||||
states: getStatesFromRoomId(res[i]["room_id"]));
|
|
||||||
roomList.add(room);
|
|
||||||
}
|
|
||||||
return roomList;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a room without events and participants.
|
|
||||||
@deprecated
|
|
||||||
Future<Room> getRoomById(String id) async {
|
|
||||||
List<Map<String, dynamic>> res =
|
|
||||||
await db.rawQuery("SELECT * FROM Rooms WHERE room_id=?", [id]);
|
|
||||||
if (res.length != 1) return null;
|
|
||||||
return Room.getRoomFromTableRow(res[0], client,
|
|
||||||
states: getStatesFromRoomId(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<Map<String, dynamic>>> getStatesFromRoomId(String id) async {
|
|
||||||
return db.rawQuery("SELECT * FROM RoomStates WHERE room_id=?", [id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> forgetRoom(String roomID) async {
|
|
||||||
await db.rawDelete("DELETE FROM Rooms WHERE room_id=?", [roomID]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Searches for the event in the store.
|
|
||||||
Future<Event> getEventById(String eventID, Room room) async {
|
|
||||||
List<Map<String, dynamic>> res = await db.rawQuery(
|
|
||||||
"SELECT * FROM Events WHERE event_id=? AND room_id=?",
|
|
||||||
[eventID, room.id]);
|
|
||||||
if (res.length == 0) return null;
|
|
||||||
return Event.fromJson(res[0], room);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Map<String, AccountData>> getAccountData() async {
|
|
||||||
Map<String, AccountData> newAccountData = {};
|
|
||||||
List<Map<String, dynamic>> rawAccountData =
|
|
||||||
await db.rawQuery("SELECT * FROM AccountData");
|
|
||||||
for (int i = 0; i < rawAccountData.length; i++)
|
|
||||||
newAccountData[rawAccountData[i]["type"]] =
|
|
||||||
AccountData.fromJson(rawAccountData[i]);
|
|
||||||
return newAccountData;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Map<String, Presence>> getPresences() async {
|
|
||||||
Map<String, Presence> newPresences = {};
|
|
||||||
List<Map<String, dynamic>> rawPresences =
|
|
||||||
await db.rawQuery("SELECT * FROM Presences");
|
|
||||||
for (int i = 0; i < rawPresences.length; i++)
|
|
||||||
newPresences[rawPresences[i]["type"]] =
|
|
||||||
Presence.fromJson(rawPresences[i]);
|
|
||||||
return newPresences;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future forgetNotification(String roomID) async {
|
|
||||||
assert(roomID != "");
|
|
||||||
await db
|
|
||||||
.rawDelete("DELETE FROM NotificationsCache WHERE chat_id=?", [roomID]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future addNotification(String roomID, String event_id, int uniqueID) async {
|
|
||||||
assert(roomID != "");
|
|
||||||
assert(event_id != "");
|
|
||||||
assert(uniqueID != "");
|
|
||||||
await db.rawInsert(
|
|
||||||
"INSERT INTO NotificationsCache(id, chat_id, event_id) VALUES (?, ?, ?)",
|
|
||||||
[uniqueID, roomID, event_id]);
|
|
||||||
// Make sure we got the same unique ID everywhere
|
|
||||||
await db.rawUpdate("UPDATE NotificationsCache SET id=? WHERE chat_id=?",
|
|
||||||
[uniqueID, roomID]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<Map<String, dynamic>>> getNotificationByRoom(
|
|
||||||
String room_id) async {
|
|
||||||
assert(room_id != "");
|
|
||||||
List<Map<String, dynamic>> res = await db.rawQuery(
|
|
||||||
"SELECT * FROM NotificationsCache WHERE chat_id=?", [room_id]);
|
|
||||||
if (res.length == 0) return null;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static final Map<String, String> schemes = {
|
|
||||||
/// The database scheme for the Client class.
|
|
||||||
"Clients": 'CREATE TABLE IF NOT EXISTS Clients(' +
|
|
||||||
'client TEXT PRIMARY KEY, ' +
|
|
||||||
'token TEXT, ' +
|
|
||||||
'homeserver TEXT, ' +
|
|
||||||
'matrix_id TEXT, ' +
|
|
||||||
'device_id TEXT, ' +
|
|
||||||
'device_name TEXT, ' +
|
|
||||||
'prev_batch TEXT, ' +
|
|
||||||
'matrix_versions TEXT, ' +
|
|
||||||
'lazy_load_members INTEGER, ' +
|
|
||||||
'UNIQUE(client))',
|
|
||||||
|
|
||||||
/// The database scheme for the Room class.
|
|
||||||
'Rooms': 'CREATE TABLE IF NOT EXISTS Rooms(' +
|
|
||||||
'room_id TEXT PRIMARY KEY, ' +
|
|
||||||
'membership TEXT, ' +
|
|
||||||
'highlight_count INTEGER, ' +
|
|
||||||
'notification_count INTEGER, ' +
|
|
||||||
'prev_batch TEXT, ' +
|
|
||||||
'joined_member_count INTEGER, ' +
|
|
||||||
'invited_member_count INTEGER, ' +
|
|
||||||
'heroes TEXT, ' +
|
|
||||||
'UNIQUE(room_id))',
|
|
||||||
|
|
||||||
/// The database scheme for the TimelineEvent class.
|
|
||||||
'Events': 'CREATE TABLE IF NOT EXISTS Events(' +
|
|
||||||
'event_id TEXT PRIMARY KEY, ' +
|
|
||||||
'room_id TEXT, ' +
|
|
||||||
'origin_server_ts INTEGER, ' +
|
|
||||||
'sender TEXT, ' +
|
|
||||||
'type TEXT, ' +
|
|
||||||
'unsigned TEXT, ' +
|
|
||||||
'content TEXT, ' +
|
|
||||||
'prev_content TEXT, ' +
|
|
||||||
'state_key TEXT, ' +
|
|
||||||
"status INTEGER, " +
|
|
||||||
'UNIQUE(event_id))',
|
|
||||||
|
|
||||||
/// The database scheme for room states.
|
|
||||||
'RoomStates': 'CREATE TABLE IF NOT EXISTS RoomStates(' +
|
|
||||||
'event_id TEXT PRIMARY KEY, ' +
|
|
||||||
'room_id TEXT, ' +
|
|
||||||
'origin_server_ts INTEGER, ' +
|
|
||||||
'sender TEXT, ' +
|
|
||||||
'state_key TEXT, ' +
|
|
||||||
'unsigned TEXT, ' +
|
|
||||||
'prev_content TEXT, ' +
|
|
||||||
'type TEXT, ' +
|
|
||||||
'content TEXT, ' +
|
|
||||||
'UNIQUE(room_id,state_key,type))',
|
|
||||||
|
|
||||||
/// The database scheme for room states.
|
|
||||||
'AccountData': 'CREATE TABLE IF NOT EXISTS AccountData(' +
|
|
||||||
'type TEXT PRIMARY KEY, ' +
|
|
||||||
'content TEXT, ' +
|
|
||||||
'UNIQUE(type))',
|
|
||||||
|
|
||||||
/// The database scheme for room states.
|
|
||||||
'RoomAccountData': 'CREATE TABLE IF NOT EXISTS RoomAccountData(' +
|
|
||||||
'type TEXT PRIMARY KEY, ' +
|
|
||||||
'room_id TEXT, ' +
|
|
||||||
'content TEXT, ' +
|
|
||||||
'UNIQUE(type,room_id))',
|
|
||||||
|
|
||||||
/// The database scheme for room states.
|
|
||||||
'Presences': 'CREATE TABLE IF NOT EXISTS Presences(' +
|
|
||||||
'type TEXT PRIMARY KEY, ' +
|
|
||||||
'sender TEXT, ' +
|
|
||||||
'content TEXT, ' +
|
|
||||||
'UNIQUE(sender))',
|
|
||||||
|
|
||||||
/// The database scheme for the NotificationsCache class.
|
|
||||||
"NotificationsCache": 'CREATE TABLE IF NOT EXISTS NotificationsCache(' +
|
|
||||||
'id int, ' +
|
|
||||||
'chat_id TEXT, ' + // The chat id
|
|
||||||
'event_id TEXT, ' + // The matrix id of the Event
|
|
||||||
'UNIQUE(event_id))',
|
|
||||||
};
|
|
||||||
}
|
|
109
lib/src/StoreAPI.dart
Normal file
109
lib/src/StoreAPI.dart
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Zender & Kurtz GbR.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Christian Pauly <krille@famedly.com>
|
||||||
|
* Marcel Radzio <mtrnord@famedly.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:core';
|
||||||
|
import 'package:famedlysdk/src/AccountData.dart';
|
||||||
|
import 'package:famedlysdk/src/Presence.dart';
|
||||||
|
import 'Client.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.
|
||||||
|
abstract class StoreAPI {
|
||||||
|
Client client;
|
||||||
|
|
||||||
|
Future<String> queryPrevBatch();
|
||||||
|
|
||||||
|
/// Will be automatically called when the client is logged in successfully.
|
||||||
|
Future<void> storeClient();
|
||||||
|
|
||||||
|
/// Clears all tables from the database.
|
||||||
|
Future<void> clear();
|
||||||
|
|
||||||
|
var txn;
|
||||||
|
|
||||||
|
Future<void> transaction(Future<void> queries());
|
||||||
|
|
||||||
|
/// Will be automatically called on every synchronisation. Must be called inside of
|
||||||
|
// /// [transaction].
|
||||||
|
Future<void> storePrevBatch(dynamic sync);
|
||||||
|
|
||||||
|
Future<void> storeRoomPrevBatch(Room room);
|
||||||
|
|
||||||
|
/// Stores a RoomUpdate object in the database. Must be called inside of
|
||||||
|
/// [transaction].
|
||||||
|
Future<void> storeRoomUpdate(RoomUpdate roomUpdate);
|
||||||
|
|
||||||
|
/// Stores an UserUpdate object in the database. Must be called inside of
|
||||||
|
/// [transaction].
|
||||||
|
Future<void> storeUserEventUpdate(UserUpdate userUpdate);
|
||||||
|
|
||||||
|
/// Stores an EventUpdate object in the database. Must be called inside of
|
||||||
|
/// [transaction].
|
||||||
|
Future<void> storeEventUpdate(EventUpdate eventUpdate);
|
||||||
|
|
||||||
|
/// Returns a User object by a given Matrix ID and a Room.
|
||||||
|
Future<User> getUser({String matrixID, Room room});
|
||||||
|
|
||||||
|
/// Loads all Users in the database to provide a contact list
|
||||||
|
/// except users who are in the Room with the ID [exceptRoomID].
|
||||||
|
Future<List<User>> loadContacts({String exceptRoomID = ""});
|
||||||
|
|
||||||
|
/// Returns all users of a room by a given [roomID].
|
||||||
|
Future<List<User>> loadParticipants(Room room);
|
||||||
|
|
||||||
|
/// Returns a list of events for the given room and sets all participants.
|
||||||
|
Future<List<Event>> getEventList(Room room);
|
||||||
|
|
||||||
|
/// Returns all rooms, the client is participating. Excludes left rooms.
|
||||||
|
Future<List<Room>> getRoomList({bool onlyLeft = false});
|
||||||
|
|
||||||
|
/// Returns a room without events and participants.
|
||||||
|
@deprecated
|
||||||
|
Future<Room> getRoomById(String id);
|
||||||
|
|
||||||
|
Future<List<Map<String, dynamic>>> getStatesFromRoomId(String id);
|
||||||
|
|
||||||
|
Future<void> forgetRoom(String roomID);
|
||||||
|
|
||||||
|
/// Searches for the event in the store.
|
||||||
|
Future<Event> getEventById(String eventID, Room room);
|
||||||
|
|
||||||
|
Future<Map<String, AccountData>> getAccountData();
|
||||||
|
|
||||||
|
Future<Map<String, Presence>> getPresences();
|
||||||
|
|
||||||
|
Future removeEvent(String eventId);
|
||||||
|
|
||||||
|
Future forgetNotification(String roomID);
|
||||||
|
|
||||||
|
Future addNotification(String roomID, String event_id, int uniqueID);
|
||||||
|
|
||||||
|
Future<List<Map<String, dynamic>>> getNotificationByRoom(String room_id);
|
||||||
|
}
|
8
lib/src/utils/MatrixFile.dart
Normal file
8
lib/src/utils/MatrixFile.dart
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
class MatrixFile {
|
||||||
|
List<int> bytes;
|
||||||
|
String path;
|
||||||
|
|
||||||
|
MatrixFile({this.bytes, this.path});
|
||||||
|
|
||||||
|
Future<List<int>> readAsBytes() async => bytes;
|
||||||
|
}
|
23
pubspec.lock
23
pubspec.lock
|
@ -126,7 +126,7 @@ packages:
|
||||||
name: crypto
|
name: crypto
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.6"
|
version: "2.1.3"
|
||||||
csslib:
|
csslib:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -208,7 +208,7 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.3"
|
version: "3.1.3"
|
||||||
image:
|
image:
|
||||||
dependency: "direct main"
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image
|
name: image
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
|
@ -220,7 +220,7 @@ packages:
|
||||||
name: intl
|
name: intl
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.15.8"
|
version: "0.16.0"
|
||||||
io:
|
io:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -306,7 +306,7 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.10"
|
version: "1.0.10"
|
||||||
path:
|
path:
|
||||||
dependency: "direct main"
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path
|
name: path
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
|
@ -387,13 +387,6 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.5"
|
version: "1.5.5"
|
||||||
sqflite:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: sqflite
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.1.6+4"
|
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -422,13 +415,6 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
version: "1.0.5"
|
||||||
synchronized:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: synchronized
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.0"
|
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -494,4 +480,3 @@ packages:
|
||||||
version: "2.1.16"
|
version: "2.1.16"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.4.0 <3.0.0"
|
dart: ">=2.4.0 <3.0.0"
|
||||||
flutter: ">=1.2.1 <2.0.0"
|
|
||||||
|
|
48
pubspec.yaml
48
pubspec.yaml
|
@ -11,17 +11,9 @@ dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
||||||
# Database
|
|
||||||
sqflite: ^1.1.6+4
|
|
||||||
path: ^1.6.2
|
|
||||||
|
|
||||||
# Connection
|
|
||||||
http: ^0.12.0+2
|
http: ^0.12.0+2
|
||||||
mime_type: ^0.2.4
|
mime_type: ^0.2.4
|
||||||
image: ^2.1.4
|
intl: ^0.16.0
|
||||||
|
|
||||||
# Time formatting
|
|
||||||
intl: ^0.15.8
|
|
||||||
|
|
||||||
json_annotation: ^2.4.0
|
json_annotation: ^2.4.0
|
||||||
|
|
||||||
|
@ -33,40 +25,4 @@ dev_dependencies:
|
||||||
json_serializable: ^3.0.0
|
json_serializable: ^3.0.0
|
||||||
|
|
||||||
pedantic: ^1.5.0 # DO NOT UPDATE AS THIS WOULD CAUSE FLUTTER TO FAIL
|
pedantic: ^1.5.0 # DO NOT UPDATE AS THIS WOULD CAUSE FLUTTER TO FAIL
|
||||||
|
flutter:
|
||||||
# For information on the generic Dart part of this file, see the
|
|
||||||
# following page: https://www.dartlang.org/tools/pub/pubspec
|
|
||||||
|
|
||||||
# The following section is specific to Flutter.
|
|
||||||
flutter:
|
|
||||||
|
|
||||||
# To add assets to your package, add an assets section, like this:
|
|
||||||
# assets:
|
|
||||||
# - images/a_dot_burr.jpeg
|
|
||||||
# - images/a_dot_ham.jpeg
|
|
||||||
#
|
|
||||||
# For details regarding assets in packages, see
|
|
||||||
# https://flutter.dev/assets-and-images/#from-packages
|
|
||||||
#
|
|
||||||
# An image asset can refer to one or more resolution-specific "variants", see
|
|
||||||
# https://flutter.dev/assets-and-images/#resolution-aware.
|
|
||||||
|
|
||||||
# To add custom fonts to your package, add a fonts section here,
|
|
||||||
# in this "flutter" section. Each entry in this list should have a
|
|
||||||
# "family" key with the font family name, and a "fonts" key with a
|
|
||||||
# list giving the asset and other descriptors for the font. For
|
|
||||||
# example:
|
|
||||||
# fonts:
|
|
||||||
# - family: Schyler
|
|
||||||
# fonts:
|
|
||||||
# - asset: fonts/Schyler-Regular.ttf
|
|
||||||
# - asset: fonts/Schyler-Italic.ttf
|
|
||||||
# style: italic
|
|
||||||
# - family: Trajan Pro
|
|
||||||
# fonts:
|
|
||||||
# - asset: fonts/TrajanPro.ttf
|
|
||||||
# - asset: fonts/TrajanPro_Bold.ttf
|
|
||||||
# weight: 700
|
|
||||||
#
|
|
||||||
# For details regarding fonts in packages, see
|
|
||||||
# https://flutter.dev/custom-fonts/#from-packages
|
|
|
@ -22,7 +22,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:famedlysdk/src/AccountData.dart';
|
import 'package:famedlysdk/src/AccountData.dart';
|
||||||
import 'package:famedlysdk/src/Client.dart';
|
import 'package:famedlysdk/src/Client.dart';
|
||||||
|
@ -35,6 +34,7 @@ import 'package:famedlysdk/src/responses/PushrulesResponse.dart';
|
||||||
import 'package:famedlysdk/src/sync/EventUpdate.dart';
|
import 'package:famedlysdk/src/sync/EventUpdate.dart';
|
||||||
import 'package:famedlysdk/src/sync/RoomUpdate.dart';
|
import 'package:famedlysdk/src/sync/RoomUpdate.dart';
|
||||||
import 'package:famedlysdk/src/sync/UserUpdate.dart';
|
import 'package:famedlysdk/src/sync/UserUpdate.dart';
|
||||||
|
import 'package:famedlysdk/src/utils/MatrixFile.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
import 'FakeMatrixApi.dart';
|
import 'FakeMatrixApi.dart';
|
||||||
|
@ -272,13 +272,15 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('upload', () async {
|
test('upload', () async {
|
||||||
final File testFile = File.fromUri(Uri.parse("fake/path/file.jpeg"));
|
final MatrixFile testFile =
|
||||||
|
MatrixFile(bytes: [], path: "/root/file.jpeg");
|
||||||
final dynamic resp = await matrix.connection.upload(testFile);
|
final dynamic resp = await matrix.connection.upload(testFile);
|
||||||
expect(resp, "mxc://example.com/AQwafuaFswefuhsfAFAgsw");
|
expect(resp, "mxc://example.com/AQwafuaFswefuhsfAFAgsw");
|
||||||
});
|
});
|
||||||
|
|
||||||
test('setAvatar', () async {
|
test('setAvatar', () async {
|
||||||
final File testFile = File.fromUri(Uri.parse("fake/path/file.jpeg"));
|
final MatrixFile testFile =
|
||||||
|
MatrixFile(bytes: [], path: "/root/file.jpeg");
|
||||||
final dynamic resp = await matrix.setAvatar(testFile);
|
final dynamic resp = await matrix.setAvatar(testFile);
|
||||||
expect(resp, null);
|
expect(resp, null);
|
||||||
});
|
});
|
||||||
|
|
|
@ -21,8 +21,6 @@
|
||||||
* along with famedlysdk. If not, see <http://www.gnu.org/licenses/>.
|
* along with famedlysdk. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:famedlysdk/src/Client.dart';
|
import 'package:famedlysdk/src/Client.dart';
|
||||||
import 'package:famedlysdk/src/Event.dart';
|
import 'package:famedlysdk/src/Event.dart';
|
||||||
import 'package:famedlysdk/src/Room.dart';
|
import 'package:famedlysdk/src/Room.dart';
|
||||||
|
@ -30,6 +28,7 @@ import 'package:famedlysdk/src/RoomState.dart';
|
||||||
import 'package:famedlysdk/src/Timeline.dart';
|
import 'package:famedlysdk/src/Timeline.dart';
|
||||||
import 'package:famedlysdk/src/User.dart';
|
import 'package:famedlysdk/src/User.dart';
|
||||||
import 'package:famedlysdk/src/utils/ChatTime.dart';
|
import 'package:famedlysdk/src/utils/ChatTime.dart';
|
||||||
|
import 'package:famedlysdk/src/utils/MatrixFile.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
import 'FakeMatrixApi.dart';
|
import 'FakeMatrixApi.dart';
|
||||||
|
@ -259,7 +258,8 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('setAvatar', () async {
|
test('setAvatar', () async {
|
||||||
final File testFile = File.fromUri(Uri.parse("fake/path/file.jpeg"));
|
final MatrixFile testFile =
|
||||||
|
MatrixFile(bytes: [], path: "/root/file.jpeg");
|
||||||
final dynamic resp = await room.setAvatar(testFile);
|
final dynamic resp = await room.setAvatar(testFile);
|
||||||
expect(resp, "YUwRidLecu:example.com");
|
expect(resp, "YUwRidLecu:example.com");
|
||||||
});
|
});
|
||||||
|
@ -286,7 +286,8 @@ void main() {
|
||||||
});*/
|
});*/
|
||||||
|
|
||||||
test('sendFileEvent', () async {
|
test('sendFileEvent', () async {
|
||||||
final File testFile = File.fromUri(Uri.parse("fake/path/file.jpeg"));
|
final MatrixFile testFile =
|
||||||
|
MatrixFile(bytes: [], path: "/root/file.jpeg");
|
||||||
final dynamic resp =
|
final dynamic resp =
|
||||||
await room.sendFileEvent(testFile, "m.file", txid: "testtxid");
|
await room.sendFileEvent(testFile, "m.file", txid: "testtxid");
|
||||||
expect(resp, "42");
|
expect(resp, "42");
|
||||||
|
|
Loading…
Reference in a new issue