famedlysdk/lib/src/Store.dart

642 lines
24 KiB
Dart
Raw Normal View History

2019-06-09 11:57:33 +00:00
/*
* 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
*/
2019-06-09 10:16:48 +00:00
import 'dart:async';
import 'dart:convert';
import 'dart:core';
import 'package:sqflite/sqflite.dart';
2019-06-09 11:37:20 +00:00
import 'package:path/path.dart' as p;
2019-06-09 10:16:48 +00:00
import 'sync/EventUpdate.dart';
2019-06-11 12:10:50 +00:00
import 'sync/UserUpdate.dart';
2019-06-09 10:16:48 +00:00
import 'sync/RoomUpdate.dart';
import 'Client.dart';
import 'User.dart';
import 'Room.dart';
2019-06-11 08:51:45 +00:00
import 'Event.dart';
2019-06-09 10:16:48 +00:00
import 'Connection.dart';
2019-06-09 12:33:25 +00:00
/// Responsible to store all data persistent and to query objects from the
/// database.
2019-06-09 10:16:48 +00:00
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 {
2019-06-09 10:16:48 +00:00
var databasePath = await getDatabasesPath();
String path = p.join(databasePath, "FluffyMatrix.db");
_db = await openDatabase(path, version: 4,
2019-06-09 10:16:48 +00:00
onCreate: (Database db, int version) async {
await createTables(db);
}, onUpgrade: (Database db, int oldVersion, int newVersion) async {
print("Migrate databse from version $oldVersion to $newVersion");
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 Events");
db.rawUpdate("UPDATE Clients SET prev_batch='' WHERE client=?",
[client.clientName]);
createTables(db);
}
});
2019-06-09 10:16:48 +00:00
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"],
2019-06-09 10:16:48 +00:00
);
print("Restore client credentials of ${client.userID}");
} else
client.connection.onLoginStateChanged.add(LoginState.loggedOut);
}
Future<void> createTables(Database db) async {
2019-06-11 06:59:22 +00:00
await db.execute(ClientsScheme);
await db.execute(RoomsScheme);
2019-06-11 09:13:14 +00:00
await db.execute(UserScheme);
2019-06-11 06:59:22 +00:00
await db.execute(EventsScheme);
}
Future<String> queryPrevBatch() async {
List<Map> list = await txn.rawQuery(
"SELECT prev_batch FROM Clients WHERE client=?", [client.clientName]);
2019-06-09 10:16:48 +00:00
return list[0]["prev_batch"];
}
/// Will be automatically called when the client is logged in successfully.
Future<void> storeClient() async {
2019-06-09 10:16:48 +00:00
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]);
2019-06-10 10:03:59 +00:00
await _db.rawDelete("DELETE FROM Rooms");
2019-06-11 09:13:14 +00:00
await _db.rawDelete("DELETE FROM User");
2019-06-09 10:16:48 +00:00
await _db.rawDelete("DELETE FROM Events");
return;
}
Transaction txn;
Future<void> transaction(Future<void> queries()) async {
2019-06-09 10:16:48 +00:00
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]);
}
/// 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(
2019-06-10 10:03:59 +00:00
"INSERT OR IGNORE INTO Rooms " +
"VALUES(?, ?, '', 0, 0, '', '', '', 0, '', '', '', '', '', '', '', '', 0, 50, 50, 0, 50, 50, 0, 50, 100, 50, 50, 50, 100) ",
2019-06-09 10:16:48 +00:00
[roomUpdate.id, roomUpdate.membership]);
// Update the notification counts and the limited timeline boolean
txn.rawUpdate(
"UPDATE Rooms SET highlight_count=?, notification_count=?, membership=? WHERE id=? ",
2019-06-09 10:16:48 +00:00
[
roomUpdate.highlight_count,
roomUpdate.notification_count,
roomUpdate.membership,
roomUpdate.id
]);
// Is the timeline limited? Then all previous messages should be
// removed from the database!
if (roomUpdate.limitedTimeline) {
txn.rawDelete("DELETE FROM Events WHERE chat_id=?", [roomUpdate.id]);
2019-06-10 10:03:59 +00:00
txn.rawUpdate("UPDATE Rooms SET prev_batch=? WHERE id=?",
2019-06-09 10:16:48 +00:00
[roomUpdate.prev_batch, roomUpdate.id]);
}
}
2019-06-11 12:10:50 +00:00
/// Stores an UserUpdate object in the database. Must be called inside of
/// [transaction].
Future<void> storeUserEventUpdate(UserUpdate userUpdate) {
dynamic eventContent = userUpdate.content;
String type = userUpdate.type;
switch (userUpdate.eventType) {
case "m.direct":
if (userUpdate.content["content"] is Map<String, List<String>>) {
Map<String, List<String>> directMap = userUpdate.content["content"];
directMap.forEach((String key, List<String> value) {
if (value.length > 0)
txn.rawUpdate(
"UPDATE Rooms SET direct_chat_matrix_id=? WHERE id=?",
[key, value[0]]);
});
}
break;
}
}
2019-06-09 10:16:48 +00:00
/// Stores an EventUpdate object in the database. Must be called inside of
2019-06-11 12:10:50 +00:00
/// [transaction].
2019-06-09 10:16:48 +00:00
Future<void> storeEventUpdate(EventUpdate eventUpdate) {
dynamic eventContent = eventUpdate.content;
String type = eventUpdate.type;
String chat_id = eventUpdate.roomID;
if (type == "timeline" || type == "history") {
// calculate the status
num status = 2;
// Make unsigned part of the content
if (eventContent["unsigned"] is Map<String, dynamic>)
eventContent["content"]["unsigned"] = eventContent["unsigned"];
// Get the state_key for m.room.member events
String state_key = "";
if (eventContent["state_key"] is String) {
state_key = eventContent["state_key"];
}
// Save the event in the database
txn.rawInsert(
2019-06-09 10:16:48 +00:00
"INSERT OR REPLACE INTO Events VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)", [
eventContent["event_id"],
chat_id,
eventContent["origin_server_ts"],
eventContent["sender"],
state_key,
eventContent["content"]["body"],
eventContent["type"],
json.encode(eventContent["content"]),
status
]);
}
if (type == "history") return null;
switch (eventUpdate.eventType) {
case "m.receipt":
if (eventContent["user"] == client.userID) {
2019-06-10 10:03:59 +00:00
txn.rawUpdate("UPDATE Rooms SET unread=? WHERE id=?",
2019-06-09 10:16:48 +00:00
[eventContent["ts"], chat_id]);
} else {
// Mark all previous received messages as seen
txn.rawUpdate(
"UPDATE Events SET status=3 WHERE origin_server_ts<=? AND chat_id=? AND status=2",
[eventContent["ts"], chat_id]);
}
break;
// This event means, that the name of a room has been changed, so
// it has to be changed in the database.
2019-06-09 10:16:48 +00:00
case "m.room.name":
2019-06-10 10:03:59 +00:00
txn.rawUpdate("UPDATE Rooms SET topic=? WHERE id=?",
2019-06-09 10:16:48 +00:00
[eventContent["content"]["name"], chat_id]);
break;
// This event means, that the topic of a room has been changed, so
// it has to be changed in the database
2019-06-09 10:16:48 +00:00
case "m.room.topic":
2019-06-10 10:03:59 +00:00
txn.rawUpdate("UPDATE Rooms SET description=? WHERE id=?",
2019-06-09 10:16:48 +00:00
[eventContent["content"]["topic"], chat_id]);
break;
// This event means, that the topic of a room has been changed, so
// it has to be changed in the database
2019-06-09 10:16:48 +00:00
case "m.room.history_visibility":
2019-06-10 10:03:59 +00:00
txn.rawUpdate("UPDATE Rooms SET history_visibility=? WHERE id=?",
2019-06-09 10:16:48 +00:00
[eventContent["content"]["history_visibility"], chat_id]);
break;
// This event means, that the topic of a room has been changed, so
// it has to be changed in the database
2019-06-09 10:16:48 +00:00
case "m.room.redaction":
txn.rawDelete(
"DELETE FROM Events WHERE id=?", [eventContent["redacts"]]);
break;
// This event means, that the topic of a room has been changed, so
// it has to be changed in the database
2019-06-09 10:16:48 +00:00
case "m.room.guest_access":
2019-06-10 10:03:59 +00:00
txn.rawUpdate("UPDATE Rooms SET guest_access=? WHERE id=?",
2019-06-09 10:16:48 +00:00
[eventContent["content"]["guest_access"], chat_id]);
break;
// This event means, that the topic of a room has been changed, so
// it has to be changed in the database
2019-06-09 10:16:48 +00:00
case "m.room.join_rules":
2019-06-10 10:03:59 +00:00
txn.rawUpdate("UPDATE Rooms SET join_rules=? WHERE id=?",
2019-06-09 10:16:48 +00:00
[eventContent["content"]["join_rule"], chat_id]);
break;
// This event means, that the avatar of a room has been changed, so
// it has to be changed in the database
2019-06-09 10:16:48 +00:00
case "m.room.avatar":
2019-06-10 10:03:59 +00:00
txn.rawUpdate("UPDATE Rooms SET avatar_url=? WHERE id=?",
2019-06-09 10:16:48 +00:00
[eventContent["content"]["url"], chat_id]);
break;
// This event means, that the aliases of a room has been changed, so
// it has to be changed in the database
2019-06-09 10:16:48 +00:00
case "m.fully_read":
2019-06-10 10:03:59 +00:00
txn.rawUpdate("UPDATE Rooms SET fully_read=? WHERE id=?",
2019-06-09 10:16:48 +00:00
[eventContent["content"]["event_id"], chat_id]);
break;
// This event means, that someone joined the room, has left the room
// or has changed his nickname
2019-06-09 10:16:48 +00:00
case "m.room.member":
String membership = eventContent["content"]["membership"];
String state_key = eventContent["state_key"];
String insertDisplayname = "";
String insertAvatarUrl = "";
if (eventContent["content"]["displayname"] is String) {
insertDisplayname = eventContent["content"]["displayname"];
}
if (eventContent["content"]["avatar_url"] is String) {
insertAvatarUrl = eventContent["content"]["avatar_url"];
}
// Update membership table
txn.rawInsert("INSERT OR IGNORE INTO User VALUES(?,?,?,?,?,0)", [
2019-06-09 10:16:48 +00:00
chat_id,
state_key,
insertDisplayname,
insertAvatarUrl,
membership
]);
2019-06-11 09:13:14 +00:00
String queryStr = "UPDATE User SET membership=?";
2019-06-09 10:16:48 +00:00
List<String> queryArgs = [membership];
if (eventContent["content"]["displayname"] is String) {
queryStr += " , displayname=?";
queryArgs.add(eventContent["content"]["displayname"]);
}
if (eventContent["content"]["avatar_url"] is String) {
queryStr += " , avatar_url=?";
queryArgs.add(eventContent["content"]["avatar_url"]);
}
queryStr += " WHERE matrix_id=? AND chat_id=?";
queryArgs.add(state_key);
queryArgs.add(chat_id);
txn.rawUpdate(queryStr, queryArgs);
break;
// This event changes the permissions of the users and the power levels
2019-06-09 10:16:48 +00:00
case "m.room.power_levels":
2019-06-10 10:03:59 +00:00
String query = "UPDATE Rooms SET ";
2019-06-09 10:16:48 +00:00
if (eventContent["content"]["ban"] is num)
query += ", power_ban=" + eventContent["content"]["ban"].toString();
if (eventContent["content"]["events_default"] is num)
query += ", power_events_default=" +
eventContent["content"]["events_default"].toString();
if (eventContent["content"]["state_default"] is num)
query += ", power_state_default=" +
eventContent["content"]["state_default"].toString();
if (eventContent["content"]["redact"] is num)
query +=
", power_redact=" + eventContent["content"]["redact"].toString();
if (eventContent["content"]["invite"] is num)
query +=
", power_invite=" + eventContent["content"]["invite"].toString();
if (eventContent["content"]["kick"] is num)
query += ", power_kick=" + eventContent["content"]["kick"].toString();
if (eventContent["content"]["user_default"] is num)
query += ", power_user_default=" +
eventContent["content"]["user_default"].toString();
if (eventContent["content"]["events"] is Map<String, dynamic>) {
if (eventContent["content"]["events"]["m.room.avatar"] is num)
query += ", power_event_avatar=" +
eventContent["content"]["events"]["m.room.avatar"].toString();
if (eventContent["content"]["events"]["m.room.history_visibility"]
is num)
2019-06-09 10:16:48 +00:00
query += ", power_event_history_visibility=" +
eventContent["content"]["events"]["m.room.history_visibility"]
.toString();
if (eventContent["content"]["events"]["m.room.canonical_alias"]
is num)
2019-06-09 10:16:48 +00:00
query += ", power_event_canonical_alias=" +
eventContent["content"]["events"]["m.room.canonical_alias"]
.toString();
if (eventContent["content"]["events"]["m.room.aliases"] is num)
query += ", power_event_aliases=" +
eventContent["content"]["events"]["m.room.aliases"].toString();
if (eventContent["content"]["events"]["m.room.name"] is num)
query += ", power_event_name=" +
eventContent["content"]["events"]["m.room.name"].toString();
if (eventContent["content"]["events"]["m.room.power_levels"] is num)
query += ", power_event_power_levels=" +
eventContent["content"]["events"]["m.room.power_levels"]
.toString();
}
2019-06-10 10:03:59 +00:00
if (query != "UPDATE Rooms SET ") {
2019-06-09 10:16:48 +00:00
query = query.replaceFirst(",", "");
txn.rawUpdate(query + " WHERE id=?", [chat_id]);
}
// Set the users power levels:
if (eventContent["content"]["users"] is Map<String, dynamic>) {
eventContent["content"]["users"]
.forEach((String user, dynamic value) async {
num power_level = eventContent["content"]["users"][user];
txn.rawUpdate(
2019-06-11 09:13:14 +00:00
"UPDATE User SET power_level=? WHERE matrix_id=? AND chat_id=?",
2019-06-09 10:16:48 +00:00
[power_level, user, chat_id]);
txn.rawInsert(
2019-06-11 09:13:14 +00:00
"INSERT OR IGNORE INTO User VALUES(?, ?, '', '', ?, ?)",
2019-06-09 10:16:48 +00:00
[chat_id, user, "unknown", power_level]);
});
}
break;
}
}
2019-06-11 09:13:14 +00:00
/// Returns a User object by a given Matrix ID and a Room.
Future<User> getUser({String matrixID, Room room}) async {
2019-06-09 10:16:48 +00:00
List<Map<String, dynamic>> res = await db.rawQuery(
2019-06-11 09:13:14 +00:00
"SELECT * FROM User WHERE matrix_id=? AND chat_id=?",
[matrixID, room.id]);
2019-06-09 10:16:48 +00:00
if (res.length != 1) return null;
2019-06-11 09:13:14 +00:00
return User.fromJson(res[0], room);
2019-06-09 10:16:48 +00:00
}
/// Loads all Users in the database to provide a contact list.
Future<List<User>> loadContacts() async {
List<Map<String, dynamic>> res = await db.rawQuery(
2019-06-11 09:13:14 +00:00
"SELECT * FROM User WHERE matrix_id!=? GROUP BY matrix_id ORDER BY displayname",
2019-06-09 10:16:48 +00:00
[client.userID]);
List<User> userList = [];
for (int i = 0; i < res.length; i++)
userList.add(User.fromJson(res[i], null));
2019-06-09 10:16:48 +00:00
return userList;
}
/// Returns all users of a room by a given [roomID].
2019-06-11 09:13:14 +00:00
Future<List<User>> loadParticipants(Room room) async {
2019-06-09 10:16:48 +00:00
List<Map<String, dynamic>> res = await db.rawQuery(
"SELECT * " +
2019-06-11 09:13:14 +00:00
" FROM User " +
2019-06-09 10:16:48 +00:00
" WHERE chat_id=? " +
" AND membership='join'",
2019-06-11 09:13:14 +00:00
[room.id]);
2019-06-09 10:16:48 +00:00
List<User> participants = [];
for (num i = 0; i < res.length; i++) {
2019-06-11 09:13:14 +00:00
participants.add(User.fromJson(res[i], room));
2019-06-09 10:16:48 +00:00
}
return participants;
}
2019-06-11 08:51:45 +00:00
/// Returns a list of events for the given room and sets all participants.
Future<List<Event>> getEventList(Room room) async {
2019-06-11 08:51:45 +00:00
List<Map<String, dynamic>> eventRes = await db.rawQuery(
"SELECT * " +
2019-06-11 09:13:14 +00:00
" FROM Events events, User user " +
2019-06-11 08:51:45 +00:00
" WHERE events.chat_id=?" +
2019-06-11 09:13:14 +00:00
" AND events.sender=user.matrix_id " +
2019-06-11 08:51:45 +00:00
" GROUP BY events.id " +
" ORDER BY origin_server_ts DESC",
2019-06-11 09:13:14 +00:00
[room.id]);
2019-06-11 08:51:45 +00:00
List<Event> eventList = [];
for (num i = 0; i < eventRes.length; i++)
2019-06-11 09:13:14 +00:00
eventList.add(Event.fromJson(eventRes[i], room));
2019-06-11 08:51:45 +00:00
return eventList;
}
2019-06-09 10:16:48 +00:00
/// Returns all rooms, the client is participating. Excludes left rooms.
Future<List<Room>> getRoomList(
{bool onlyLeft = false,
bool onlyDirect = false,
bool onlyGroups = false}) async {
if (onlyDirect && onlyGroups) return [];
List<Map<String, dynamic>> res = await db.rawQuery("SELECT * " +
" FROM Rooms rooms LEFT JOIN Events events " +
" ON rooms.id=events.chat_id " +
" WHERE rooms.id!='' " +
" AND rooms.membership" +
(onlyLeft ? "=" : "!=") +
"'left' " +
(onlyDirect ? " AND rooms.direct_chat_matrix_id!= '' " : "") +
(onlyGroups ? " AND rooms.direct_chat_matrix_id= '' " : "") +
" GROUP BY rooms.id " +
" ORDER BY origin_server_ts DESC ");
2019-06-09 10:16:48 +00:00
List<Room> roomList = [];
for (num i = 0; i < res.length; i++) {
try {
Room room = await Room.getRoomFromTableRow(res[i], client);
roomList.add(room);
} catch (e) {
print(e.toString());
}
}
return roomList;
}
2019-06-11 08:51:45 +00:00
/// Returns a room without events and participants.
Future<Room> getRoomById(String id) async {
List<Map<String, dynamic>> res =
await db.rawQuery("SELECT * FROM Rooms WHERE id=?", [id]);
2019-06-11 08:51:45 +00:00
if (res.length != 1) return null;
return Room.getRoomFromTableRow(res[0], client);
}
2019-06-09 10:16:48 +00:00
/// Calculates and returns an avatar for a direct chat by a given [roomID].
Future<String> getAvatarFromSingleChat(String roomID) async {
2019-06-09 10:16:48 +00:00
String avatarStr = "";
List<Map<String, dynamic>> res = await db.rawQuery(
2019-06-11 09:13:14 +00:00
"SELECT avatar_url FROM User " +
" WHERE User.chat_id=? " +
" AND (User.membership='join' OR User.membership='invite') " +
" AND User.matrix_id!=? ",
2019-06-09 10:16:48 +00:00
[roomID, client.userID]);
if (res.length == 1) avatarStr = res[0]["avatar_url"];
return avatarStr;
}
/// Calculates a chat name for a groupchat without a name. The chat name will
/// be the name of all users (excluding the user of this client) divided by
/// ','.
Future<String> getChatNameFromMemberNames(String roomID) async {
2019-06-09 10:16:48 +00:00
String displayname = 'Empty chat';
List<Map<String, dynamic>> rs = await db.rawQuery(
2019-06-11 09:13:14 +00:00
"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!=? ",
2019-06-09 10:16:48 +00:00
[roomID, client.userID]);
if (rs.length > 0) {
displayname = "";
for (var i = 0; i < rs.length; i++) {
String username = rs[i]["displayname"];
if (username == "" || username == null) username = rs[i]["matrix_id"];
if (rs[i]["state_key"] != client.userID) displayname += username + ", ";
}
if (displayname == "" || displayname == null)
displayname = 'Empty chat';
else
displayname = displayname.substring(0, displayname.length - 2);
}
return displayname;
}
/// Returns the (first) room ID from the store which is a private chat with
/// the user [userID]. Returns null if there is none.
Future<String> getDirectChatRoomID(String userID) async {
List<Map<String, dynamic>> res = await db.rawQuery(
"SELECT id FROM Rooms WHERE direct_chat_matrix_id=?", [userID]);
if (res.length != 1) return null;
return res[0]["id"];
}
2019-06-11 11:32:14 +00:00
/// Returns the power level of the user for the given [roomID]. Returns null if
/// the room or the own user wasn't found.
Future<int> getPowerLevel(String roomID) async {
List<Map<String, dynamic>> res = await db.rawQuery(
"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"];
}
/// Returns the power levels from all users for the given [roomID].
Future<Map<String, int>> getPowerLevels(String roomID) async {
List<Map<String, dynamic>> res = await db.rawQuery(
"SELECT matrix_id, power_level FROM Users WHERE chat_id=?",
[roomID, client.userID]);
Map<String, int> powerMap = {};
for (int i = 0; i < res.length; i++)
powerMap[res[i]["matrix_id"]] = res[i]["power_level"];
return powerMap;
}
2019-06-09 10:16:48 +00:00
/// The database sheme for the Client class.
2019-06-10 10:03:59 +00:00
static final String ClientsScheme = 'CREATE TABLE IF NOT EXISTS Clients(' +
2019-06-09 10:16:48 +00:00
'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))';
2019-06-10 10:03:59 +00:00
2019-06-09 10:16:48 +00:00
/// The database sheme for the Room class.
2019-06-10 10:03:59 +00:00
static final String RoomsScheme = 'CREATE TABLE IF NOT EXISTS Rooms(' +
2019-06-09 10:16:48 +00:00
'id TEXT PRIMARY KEY, ' +
'membership TEXT, ' +
'topic TEXT, ' +
'highlight_count INTEGER, ' +
'notification_count INTEGER, ' +
'prev_batch TEXT, ' +
'avatar_url TEXT, ' +
'draft TEXT, ' +
'unread INTEGER, ' + // Timestamp of when the user has last read the chat
'fully_read TEXT, ' + // ID of the fully read marker event
'description TEXT, ' +
'canonical_alias TEXT, ' + // The address in the form: #roomname:homeserver.org
2019-06-10 10:03:59 +00:00
'direct_chat_matrix_id TEXT, ' + //If this room is a direct chat, this is the matrix ID of the user
'notification_settings TEXT, ' + // Must be one of [all, mention]
2019-06-09 10:16:48 +00:00
// Security rules
'guest_access TEXT, ' +
'history_visibility TEXT, ' +
'join_rules TEXT, ' +
// Power levels
'power_events_default INTEGER, ' +
'power_state_default INTEGER, ' +
'power_redact INTEGER, ' +
'power_invite INTEGER, ' +
'power_ban INTEGER, ' +
'power_kick INTEGER, ' +
'power_user_default INTEGER, ' +
// Power levels for events
'power_event_avatar INTEGER, ' +
'power_event_history_visibility INTEGER, ' +
'power_event_canonical_alias INTEGER, ' +
'power_event_aliases INTEGER, ' +
'power_event_name INTEGER, ' +
'power_event_power_levels INTEGER, ' +
'UNIQUE(id))';
/// The database sheme for the Event class.
2019-06-10 10:03:59 +00:00
static final String EventsScheme = 'CREATE TABLE IF NOT EXISTS Events(' +
2019-06-09 10:16:48 +00:00
'id TEXT PRIMARY KEY, ' +
'chat_id TEXT, ' +
'origin_server_ts INTEGER, ' +
'sender TEXT, ' +
'state_key TEXT, ' +
'content_body TEXT, ' +
'type TEXT, ' +
'content_json TEXT, ' +
"status INTEGER, " +
'UNIQUE(id))';
/// The database sheme for the User class.
2019-06-11 09:13:14 +00:00
static final String UserScheme = 'CREATE TABLE IF NOT EXISTS User(' +
2019-06-09 10:16:48 +00:00
'chat_id TEXT, ' + // The chat id of this membership
'matrix_id TEXT, ' + // The matrix id of this user
'displayname TEXT, ' +
'avatar_url TEXT, ' +
'membership TEXT, ' + // The status of the membership. Must be one of [join, invite, ban, leave]
'power_level INTEGER, ' + // The power level of this user. Must be in [0,..,100]
'UNIQUE(chat_id, matrix_id))';
}