mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 09:08:40 +00:00
fix #75
This commit is contained in:
parent
f559e643b1
commit
875ebb9eab
33 changed files with 5168 additions and 107 deletions
13
build.yaml
Normal file
13
build.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
targets:
|
||||
$default:
|
||||
sources:
|
||||
- lib/**
|
||||
- "tool/**"
|
||||
- pubspec.yaml
|
||||
- lib/$lib$
|
||||
- $package$
|
||||
builders:
|
||||
drift_dev:
|
||||
options:
|
||||
databases:
|
||||
twonly_database: lib/src/database/twonly_database.dart
|
||||
1
drift_schemas/twonly_database/drift_schema_v1.json
Normal file
1
drift_schemas/twonly_database/drift_schema_v1.json
Normal file
File diff suppressed because one or more lines are too long
1
drift_schemas/twonly_database/drift_schema_v2.json
Normal file
1
drift_schemas/twonly_database/drift_schema_v2.json
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -2,7 +2,7 @@ import 'dart:collection';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/components/verified_shield.dart';
|
||||
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||
import 'package:twonly/src/database/daos/contacts_dao.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/components/flame.dart';
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||
import 'package:twonly/src/database/daos/contacts_dao.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:twonly/src/json_models/userdata.dart';
|
||||
|
||||
|
|
@ -44,9 +44,6 @@ class ContactAvatar extends StatelessWidget {
|
|||
height: proSize,
|
||||
width: proSize,
|
||||
child: Center(
|
||||
// child: Container(
|
||||
// color: Colors.green,
|
||||
// ),
|
||||
child: SvgPicture.string(
|
||||
avatarSvg,
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
|
|
|
|||
|
|
@ -143,7 +143,13 @@ class _MessageSendStateIconState extends State<MessageSendStateIcon> {
|
|||
|
||||
if (message.kind == MessageKind.storedMediaFile) {
|
||||
icon = FaIcon(FontAwesomeIcons.floppyDisk, size: 12, color: color);
|
||||
text = "Stored in gallery";
|
||||
text = context.lang.messageStoredInGalery;
|
||||
}
|
||||
|
||||
if (message.errorWhileSending) {
|
||||
icon =
|
||||
FaIcon(FontAwesomeIcons.circleExclamation, size: 12, color: color);
|
||||
text = "Error while sending";
|
||||
}
|
||||
|
||||
icons.add(icon);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
|
||||
class PermissionHandlerView extends StatefulWidget {
|
||||
|
|
@ -23,12 +24,14 @@ Future<bool> checkPermissions() async {
|
|||
|
||||
class PermissionHandlerViewState extends State<PermissionHandlerView> {
|
||||
Future<Map<Permission, PermissionStatus>> permissionServices() async {
|
||||
// You can request multiple permissions at once.
|
||||
// try {
|
||||
Map<Permission, PermissionStatus> statuses = await [
|
||||
Permission.camera,
|
||||
// Permission.microphone,
|
||||
Permission.notification
|
||||
].request();
|
||||
// } catch (e) {}
|
||||
// You can request multiple permissions at once.
|
||||
|
||||
// if (statuses[Permission.microphone]!.isPermanentlyDenied) {
|
||||
// openAppSettings();
|
||||
|
|
@ -70,10 +73,14 @@ class PermissionHandlerViewState extends State<PermissionHandlerView> {
|
|||
label: Text("Request permissions"),
|
||||
icon: const Icon(Icons.perm_camera_mic),
|
||||
onPressed: () async {
|
||||
try {
|
||||
permissionServices();
|
||||
if (await checkPermissions()) {
|
||||
widget.onSuccess();
|
||||
}
|
||||
} catch (e) {
|
||||
Logger("permissions_view").shout(e);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -164,3 +164,28 @@ class ContactsDao extends DatabaseAccessor<TwonlyDatabase>
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
String getContactDisplayName(Contact user) {
|
||||
if (user.nickName != null) {
|
||||
return user.nickName!;
|
||||
}
|
||||
if (user.displayName != null) {
|
||||
return user.displayName!;
|
||||
}
|
||||
return user.username;
|
||||
}
|
||||
|
||||
int getFlameCounterFromContact(Contact contact) {
|
||||
if (contact.lastMessageSend == null || contact.lastMessageReceived == null) {
|
||||
return 0;
|
||||
}
|
||||
final now = DateTime.now();
|
||||
final startOfToday = DateTime(now.year, now.month, now.day);
|
||||
final twoDaysAgo = startOfToday.subtract(Duration(days: 2));
|
||||
if (contact.lastMessageSend!.isAfter(twoDaysAgo) &&
|
||||
contact.lastMessageReceived!.isAfter(twoDaysAgo)) {
|
||||
return contact.flameCounter + 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
import 'package:drift/drift.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||
import 'package:twonly/src/database/tables/messages_table.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:twonly/src/json_models/message.dart';
|
||||
|
||||
part 'messages_dao.g.dart';
|
||||
|
||||
@DriftAccessor(tables: [Messages])
|
||||
@DriftAccessor(tables: [Messages, Contacts])
|
||||
class MessagesDao extends DatabaseAccessor<TwonlyDatabase>
|
||||
with _$MessagesDaoMixin {
|
||||
// this constructor is required so that the main database can create an instance
|
||||
|
|
@ -26,6 +25,7 @@ class MessagesDao extends DatabaseAccessor<TwonlyDatabase>
|
|||
..where((t) =>
|
||||
t.openedAt.isNull() &
|
||||
t.contactId.equals(contactId) &
|
||||
t.errorWhileSending.equals(false) &
|
||||
t.messageOtherId.isNotNull() &
|
||||
t.kind.equals(MessageKind.media.name))
|
||||
..orderBy([(t) => OrderingTerm.asc(t.sendAt)]))
|
||||
|
|
@ -72,8 +72,17 @@ class MessagesDao extends DatabaseAccessor<TwonlyDatabase>
|
|||
.get();
|
||||
}
|
||||
|
||||
Future<List<Message>> getAllMessagesForRetransmitting() {
|
||||
return (select(messages)..where((t) => t.acknowledgeByServer.equals(false)))
|
||||
Future<List<Message>> getAllMessagesPendingUploadOlderThanAMinute() {
|
||||
return (select(messages)
|
||||
..where(
|
||||
(t) =>
|
||||
t.acknowledgeByServer.equals(false) &
|
||||
t.messageOtherId.isNull() &
|
||||
t.errorWhileSending.equals(false) &
|
||||
t.sendAt.isSmallerThanValue(
|
||||
DateTime.now().subtract(Duration(minutes: 5))) &
|
||||
t.kind.equals(MessageKind.media.name),
|
||||
))
|
||||
.get();
|
||||
}
|
||||
|
||||
|
|
@ -113,8 +122,10 @@ class MessagesDao extends DatabaseAccessor<TwonlyDatabase>
|
|||
|
||||
Future<int?> insertMessage(MessagesCompanion message) async {
|
||||
try {
|
||||
await twonlyDatabase.contactsDao
|
||||
.newMessageExchange(message.contactId.value);
|
||||
await (update(contacts)
|
||||
..where((c) => c.userId.equals(message.contactId.value)))
|
||||
.write(ContactsCompanion(lastMessageExchange: Value(DateTime.now())));
|
||||
|
||||
return await into(messages).insert(message);
|
||||
} catch (e) {
|
||||
Logger("twonlyDatabase").shout("Error while inserting message: $e");
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import 'package:drift/drift.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
|
||||
class Contacts extends Table {
|
||||
IntColumn get userId => integer()();
|
||||
|
|
@ -31,28 +30,3 @@ class Contacts extends Table {
|
|||
@override
|
||||
Set<Column> get primaryKey => {userId};
|
||||
}
|
||||
|
||||
String getContactDisplayName(Contact user) {
|
||||
if (user.nickName != null) {
|
||||
return user.nickName!;
|
||||
}
|
||||
if (user.displayName != null) {
|
||||
return user.displayName!;
|
||||
}
|
||||
return user.username;
|
||||
}
|
||||
|
||||
int getFlameCounterFromContact(Contact contact) {
|
||||
if (contact.lastMessageSend == null || contact.lastMessageReceived == null) {
|
||||
return 0;
|
||||
}
|
||||
final now = DateTime.now();
|
||||
final startOfToday = DateTime(now.year, now.month, now.day);
|
||||
final twoDaysAgo = startOfToday.subtract(Duration(days: 2));
|
||||
if (contact.lastMessageSend!.isAfter(twoDaysAgo) &&
|
||||
contact.lastMessageReceived!.isAfter(twoDaysAgo)) {
|
||||
return contact.flameCounter + 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,17 @@
|
|||
import 'package:drift/drift.dart';
|
||||
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||
import 'package:twonly/src/json_models/message.dart';
|
||||
|
||||
enum MessageKind {
|
||||
textMessage,
|
||||
storedMediaFile,
|
||||
media,
|
||||
contactRequest,
|
||||
profileChange,
|
||||
rejectRequest,
|
||||
acceptRequest,
|
||||
opened,
|
||||
ack
|
||||
}
|
||||
|
||||
enum DownloadState {
|
||||
pending,
|
||||
|
|
@ -25,6 +36,8 @@ class Messages extends Table {
|
|||
BoolColumn get acknowledgeByServer =>
|
||||
boolean().withDefault(Constant(false))();
|
||||
|
||||
BoolColumn get errorWhileSending => boolean().withDefault(Constant(false))();
|
||||
|
||||
TextColumn get kind => textEnum<MessageKind>()();
|
||||
TextColumn get contentJson => text().nullable()();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:drift/drift.dart';
|
||||
import 'package:drift_flutter/drift_flutter.dart';
|
||||
import 'package:drift_flutter/drift_flutter.dart'
|
||||
show driftDatabase, DriftNativeOptions;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:twonly/src/database/daos/contacts_dao.dart';
|
||||
import 'package:twonly/src/database/daos/messages_dao.dart';
|
||||
|
|
@ -9,7 +10,7 @@ import 'package:twonly/src/database/tables/signal_identity_key_store_table.dart'
|
|||
import 'package:twonly/src/database/tables/signal_pre_key_store_table.dart';
|
||||
import 'package:twonly/src/database/tables/signal_sender_key_store_table.dart';
|
||||
import 'package:twonly/src/database/tables/signal_session_store_table.dart';
|
||||
import 'package:twonly/src/json_models/message.dart';
|
||||
import 'package:twonly/src/database/twonly_database.steps.dart';
|
||||
|
||||
part 'twonly_database.g.dart';
|
||||
|
||||
|
|
@ -26,10 +27,15 @@ part 'twonly_database.g.dart';
|
|||
ContactsDao
|
||||
])
|
||||
class TwonlyDatabase extends _$TwonlyDatabase {
|
||||
TwonlyDatabase() : super(_openConnection());
|
||||
TwonlyDatabase([QueryExecutor? e])
|
||||
: super(
|
||||
e ?? _openConnection(),
|
||||
);
|
||||
|
||||
TwonlyDatabase.forTesting(DatabaseConnection super.connection);
|
||||
|
||||
@override
|
||||
int get schemaVersion => 1;
|
||||
int get schemaVersion => 2;
|
||||
|
||||
static QueryExecutor _openConnection() {
|
||||
return driftDatabase(
|
||||
|
|
@ -40,6 +46,15 @@ class TwonlyDatabase extends _$TwonlyDatabase {
|
|||
);
|
||||
}
|
||||
|
||||
@override
|
||||
MigrationStrategy get migration {
|
||||
return MigrationStrategy(
|
||||
onUpgrade: stepByStep(from1To2: (m, schema) async {
|
||||
m.addColumn(schema.messages, schema.messages.errorWhileSending);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
void markUpdated() {
|
||||
notifyUpdates({TableUpdate.onTable(messages, kind: UpdateKind.update)});
|
||||
notifyUpdates({TableUpdate.onTable(contacts, kind: UpdateKind.update)});
|
||||
|
|
|
|||
|
|
@ -904,6 +904,16 @@ class $MessagesTable extends Messages with TableInfo<$MessagesTable, Message> {
|
|||
defaultConstraints: GeneratedColumn.constraintIsAlways(
|
||||
'CHECK ("acknowledge_by_server" IN (0, 1))'),
|
||||
defaultValue: Constant(false));
|
||||
static const VerificationMeta _errorWhileSendingMeta =
|
||||
const VerificationMeta('errorWhileSending');
|
||||
@override
|
||||
late final GeneratedColumn<bool> errorWhileSending = GeneratedColumn<bool>(
|
||||
'error_while_sending', aliasedName, false,
|
||||
type: DriftSqlType.bool,
|
||||
requiredDuringInsert: false,
|
||||
defaultConstraints: GeneratedColumn.constraintIsAlways(
|
||||
'CHECK ("error_while_sending" IN (0, 1))'),
|
||||
defaultValue: Constant(false));
|
||||
@override
|
||||
late final GeneratedColumnWithTypeConverter<MessageKind, String> kind =
|
||||
GeneratedColumn<String>('kind', aliasedName, false,
|
||||
|
|
@ -946,6 +956,7 @@ class $MessagesTable extends Messages with TableInfo<$MessagesTable, Message> {
|
|||
acknowledgeByUser,
|
||||
downloadState,
|
||||
acknowledgeByServer,
|
||||
errorWhileSending,
|
||||
kind,
|
||||
contentJson,
|
||||
openedAt,
|
||||
|
|
@ -1003,6 +1014,12 @@ class $MessagesTable extends Messages with TableInfo<$MessagesTable, Message> {
|
|||
acknowledgeByServer.isAcceptableOrUnknown(
|
||||
data['acknowledge_by_server']!, _acknowledgeByServerMeta));
|
||||
}
|
||||
if (data.containsKey('error_while_sending')) {
|
||||
context.handle(
|
||||
_errorWhileSendingMeta,
|
||||
errorWhileSending.isAcceptableOrUnknown(
|
||||
data['error_while_sending']!, _errorWhileSendingMeta));
|
||||
}
|
||||
if (data.containsKey('content_json')) {
|
||||
context.handle(
|
||||
_contentJsonMeta,
|
||||
|
|
@ -1048,6 +1065,8 @@ class $MessagesTable extends Messages with TableInfo<$MessagesTable, Message> {
|
|||
DriftSqlType.int, data['${effectivePrefix}download_state'])!),
|
||||
acknowledgeByServer: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.bool, data['${effectivePrefix}acknowledge_by_server'])!,
|
||||
errorWhileSending: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.bool, data['${effectivePrefix}error_while_sending'])!,
|
||||
kind: $MessagesTable.$converterkind.fromSql(attachedDatabase.typeMapping
|
||||
.read(DriftSqlType.string, data['${effectivePrefix}kind'])!),
|
||||
contentJson: attachedDatabase.typeMapping
|
||||
|
|
@ -1081,6 +1100,7 @@ class Message extends DataClass implements Insertable<Message> {
|
|||
final bool acknowledgeByUser;
|
||||
final DownloadState downloadState;
|
||||
final bool acknowledgeByServer;
|
||||
final bool errorWhileSending;
|
||||
final MessageKind kind;
|
||||
final String? contentJson;
|
||||
final DateTime? openedAt;
|
||||
|
|
@ -1095,6 +1115,7 @@ class Message extends DataClass implements Insertable<Message> {
|
|||
required this.acknowledgeByUser,
|
||||
required this.downloadState,
|
||||
required this.acknowledgeByServer,
|
||||
required this.errorWhileSending,
|
||||
required this.kind,
|
||||
this.contentJson,
|
||||
this.openedAt,
|
||||
|
|
@ -1121,6 +1142,7 @@ class Message extends DataClass implements Insertable<Message> {
|
|||
$MessagesTable.$converterdownloadState.toSql(downloadState));
|
||||
}
|
||||
map['acknowledge_by_server'] = Variable<bool>(acknowledgeByServer);
|
||||
map['error_while_sending'] = Variable<bool>(errorWhileSending);
|
||||
{
|
||||
map['kind'] = Variable<String>($MessagesTable.$converterkind.toSql(kind));
|
||||
}
|
||||
|
|
@ -1151,6 +1173,7 @@ class Message extends DataClass implements Insertable<Message> {
|
|||
acknowledgeByUser: Value(acknowledgeByUser),
|
||||
downloadState: Value(downloadState),
|
||||
acknowledgeByServer: Value(acknowledgeByServer),
|
||||
errorWhileSending: Value(errorWhileSending),
|
||||
kind: Value(kind),
|
||||
contentJson: contentJson == null && nullToAbsent
|
||||
? const Value.absent()
|
||||
|
|
@ -1179,6 +1202,7 @@ class Message extends DataClass implements Insertable<Message> {
|
|||
.fromJson(serializer.fromJson<int>(json['downloadState'])),
|
||||
acknowledgeByServer:
|
||||
serializer.fromJson<bool>(json['acknowledgeByServer']),
|
||||
errorWhileSending: serializer.fromJson<bool>(json['errorWhileSending']),
|
||||
kind: $MessagesTable.$converterkind
|
||||
.fromJson(serializer.fromJson<String>(json['kind'])),
|
||||
contentJson: serializer.fromJson<String?>(json['contentJson']),
|
||||
|
|
@ -1201,6 +1225,7 @@ class Message extends DataClass implements Insertable<Message> {
|
|||
'downloadState': serializer.toJson<int>(
|
||||
$MessagesTable.$converterdownloadState.toJson(downloadState)),
|
||||
'acknowledgeByServer': serializer.toJson<bool>(acknowledgeByServer),
|
||||
'errorWhileSending': serializer.toJson<bool>(errorWhileSending),
|
||||
'kind':
|
||||
serializer.toJson<String>($MessagesTable.$converterkind.toJson(kind)),
|
||||
'contentJson': serializer.toJson<String?>(contentJson),
|
||||
|
|
@ -1219,6 +1244,7 @@ class Message extends DataClass implements Insertable<Message> {
|
|||
bool? acknowledgeByUser,
|
||||
DownloadState? downloadState,
|
||||
bool? acknowledgeByServer,
|
||||
bool? errorWhileSending,
|
||||
MessageKind? kind,
|
||||
Value<String?> contentJson = const Value.absent(),
|
||||
Value<DateTime?> openedAt = const Value.absent(),
|
||||
|
|
@ -1238,6 +1264,7 @@ class Message extends DataClass implements Insertable<Message> {
|
|||
acknowledgeByUser: acknowledgeByUser ?? this.acknowledgeByUser,
|
||||
downloadState: downloadState ?? this.downloadState,
|
||||
acknowledgeByServer: acknowledgeByServer ?? this.acknowledgeByServer,
|
||||
errorWhileSending: errorWhileSending ?? this.errorWhileSending,
|
||||
kind: kind ?? this.kind,
|
||||
contentJson: contentJson.present ? contentJson.value : this.contentJson,
|
||||
openedAt: openedAt.present ? openedAt.value : this.openedAt,
|
||||
|
|
@ -1266,6 +1293,9 @@ class Message extends DataClass implements Insertable<Message> {
|
|||
acknowledgeByServer: data.acknowledgeByServer.present
|
||||
? data.acknowledgeByServer.value
|
||||
: this.acknowledgeByServer,
|
||||
errorWhileSending: data.errorWhileSending.present
|
||||
? data.errorWhileSending.value
|
||||
: this.errorWhileSending,
|
||||
kind: data.kind.present ? data.kind.value : this.kind,
|
||||
contentJson:
|
||||
data.contentJson.present ? data.contentJson.value : this.contentJson,
|
||||
|
|
@ -1286,6 +1316,7 @@ class Message extends DataClass implements Insertable<Message> {
|
|||
..write('acknowledgeByUser: $acknowledgeByUser, ')
|
||||
..write('downloadState: $downloadState, ')
|
||||
..write('acknowledgeByServer: $acknowledgeByServer, ')
|
||||
..write('errorWhileSending: $errorWhileSending, ')
|
||||
..write('kind: $kind, ')
|
||||
..write('contentJson: $contentJson, ')
|
||||
..write('openedAt: $openedAt, ')
|
||||
|
|
@ -1305,6 +1336,7 @@ class Message extends DataClass implements Insertable<Message> {
|
|||
acknowledgeByUser,
|
||||
downloadState,
|
||||
acknowledgeByServer,
|
||||
errorWhileSending,
|
||||
kind,
|
||||
contentJson,
|
||||
openedAt,
|
||||
|
|
@ -1322,6 +1354,7 @@ class Message extends DataClass implements Insertable<Message> {
|
|||
other.acknowledgeByUser == this.acknowledgeByUser &&
|
||||
other.downloadState == this.downloadState &&
|
||||
other.acknowledgeByServer == this.acknowledgeByServer &&
|
||||
other.errorWhileSending == this.errorWhileSending &&
|
||||
other.kind == this.kind &&
|
||||
other.contentJson == this.contentJson &&
|
||||
other.openedAt == this.openedAt &&
|
||||
|
|
@ -1338,6 +1371,7 @@ class MessagesCompanion extends UpdateCompanion<Message> {
|
|||
final Value<bool> acknowledgeByUser;
|
||||
final Value<DownloadState> downloadState;
|
||||
final Value<bool> acknowledgeByServer;
|
||||
final Value<bool> errorWhileSending;
|
||||
final Value<MessageKind> kind;
|
||||
final Value<String?> contentJson;
|
||||
final Value<DateTime?> openedAt;
|
||||
|
|
@ -1352,6 +1386,7 @@ class MessagesCompanion extends UpdateCompanion<Message> {
|
|||
this.acknowledgeByUser = const Value.absent(),
|
||||
this.downloadState = const Value.absent(),
|
||||
this.acknowledgeByServer = const Value.absent(),
|
||||
this.errorWhileSending = const Value.absent(),
|
||||
this.kind = const Value.absent(),
|
||||
this.contentJson = const Value.absent(),
|
||||
this.openedAt = const Value.absent(),
|
||||
|
|
@ -1367,6 +1402,7 @@ class MessagesCompanion extends UpdateCompanion<Message> {
|
|||
this.acknowledgeByUser = const Value.absent(),
|
||||
this.downloadState = const Value.absent(),
|
||||
this.acknowledgeByServer = const Value.absent(),
|
||||
this.errorWhileSending = const Value.absent(),
|
||||
required MessageKind kind,
|
||||
this.contentJson = const Value.absent(),
|
||||
this.openedAt = const Value.absent(),
|
||||
|
|
@ -1383,6 +1419,7 @@ class MessagesCompanion extends UpdateCompanion<Message> {
|
|||
Expression<bool>? acknowledgeByUser,
|
||||
Expression<int>? downloadState,
|
||||
Expression<bool>? acknowledgeByServer,
|
||||
Expression<bool>? errorWhileSending,
|
||||
Expression<String>? kind,
|
||||
Expression<String>? contentJson,
|
||||
Expression<DateTime>? openedAt,
|
||||
|
|
@ -1401,6 +1438,7 @@ class MessagesCompanion extends UpdateCompanion<Message> {
|
|||
if (downloadState != null) 'download_state': downloadState,
|
||||
if (acknowledgeByServer != null)
|
||||
'acknowledge_by_server': acknowledgeByServer,
|
||||
if (errorWhileSending != null) 'error_while_sending': errorWhileSending,
|
||||
if (kind != null) 'kind': kind,
|
||||
if (contentJson != null) 'content_json': contentJson,
|
||||
if (openedAt != null) 'opened_at': openedAt,
|
||||
|
|
@ -1418,6 +1456,7 @@ class MessagesCompanion extends UpdateCompanion<Message> {
|
|||
Value<bool>? acknowledgeByUser,
|
||||
Value<DownloadState>? downloadState,
|
||||
Value<bool>? acknowledgeByServer,
|
||||
Value<bool>? errorWhileSending,
|
||||
Value<MessageKind>? kind,
|
||||
Value<String?>? contentJson,
|
||||
Value<DateTime?>? openedAt,
|
||||
|
|
@ -1433,6 +1472,7 @@ class MessagesCompanion extends UpdateCompanion<Message> {
|
|||
acknowledgeByUser: acknowledgeByUser ?? this.acknowledgeByUser,
|
||||
downloadState: downloadState ?? this.downloadState,
|
||||
acknowledgeByServer: acknowledgeByServer ?? this.acknowledgeByServer,
|
||||
errorWhileSending: errorWhileSending ?? this.errorWhileSending,
|
||||
kind: kind ?? this.kind,
|
||||
contentJson: contentJson ?? this.contentJson,
|
||||
openedAt: openedAt ?? this.openedAt,
|
||||
|
|
@ -1470,6 +1510,9 @@ class MessagesCompanion extends UpdateCompanion<Message> {
|
|||
if (acknowledgeByServer.present) {
|
||||
map['acknowledge_by_server'] = Variable<bool>(acknowledgeByServer.value);
|
||||
}
|
||||
if (errorWhileSending.present) {
|
||||
map['error_while_sending'] = Variable<bool>(errorWhileSending.value);
|
||||
}
|
||||
if (kind.present) {
|
||||
map['kind'] =
|
||||
Variable<String>($MessagesTable.$converterkind.toSql(kind.value));
|
||||
|
|
@ -1500,6 +1543,7 @@ class MessagesCompanion extends UpdateCompanion<Message> {
|
|||
..write('acknowledgeByUser: $acknowledgeByUser, ')
|
||||
..write('downloadState: $downloadState, ')
|
||||
..write('acknowledgeByServer: $acknowledgeByServer, ')
|
||||
..write('errorWhileSending: $errorWhileSending, ')
|
||||
..write('kind: $kind, ')
|
||||
..write('contentJson: $contentJson, ')
|
||||
..write('openedAt: $openedAt, ')
|
||||
|
|
@ -2966,6 +3010,7 @@ typedef $$MessagesTableCreateCompanionBuilder = MessagesCompanion Function({
|
|||
Value<bool> acknowledgeByUser,
|
||||
Value<DownloadState> downloadState,
|
||||
Value<bool> acknowledgeByServer,
|
||||
Value<bool> errorWhileSending,
|
||||
required MessageKind kind,
|
||||
Value<String?> contentJson,
|
||||
Value<DateTime?> openedAt,
|
||||
|
|
@ -2981,6 +3026,7 @@ typedef $$MessagesTableUpdateCompanionBuilder = MessagesCompanion Function({
|
|||
Value<bool> acknowledgeByUser,
|
||||
Value<DownloadState> downloadState,
|
||||
Value<bool> acknowledgeByServer,
|
||||
Value<bool> errorWhileSending,
|
||||
Value<MessageKind> kind,
|
||||
Value<String?> contentJson,
|
||||
Value<DateTime?> openedAt,
|
||||
|
|
@ -3045,6 +3091,10 @@ class $$MessagesTableFilterComposer
|
|||
column: $table.acknowledgeByServer,
|
||||
builder: (column) => ColumnFilters(column));
|
||||
|
||||
ColumnFilters<bool> get errorWhileSending => $composableBuilder(
|
||||
column: $table.errorWhileSending,
|
||||
builder: (column) => ColumnFilters(column));
|
||||
|
||||
ColumnWithTypeConverterFilters<MessageKind, MessageKind, String> get kind =>
|
||||
$composableBuilder(
|
||||
column: $table.kind,
|
||||
|
|
@ -3119,6 +3169,10 @@ class $$MessagesTableOrderingComposer
|
|||
column: $table.acknowledgeByServer,
|
||||
builder: (column) => ColumnOrderings(column));
|
||||
|
||||
ColumnOrderings<bool> get errorWhileSending => $composableBuilder(
|
||||
column: $table.errorWhileSending,
|
||||
builder: (column) => ColumnOrderings(column));
|
||||
|
||||
ColumnOrderings<String> get kind => $composableBuilder(
|
||||
column: $table.kind, builder: (column) => ColumnOrderings(column));
|
||||
|
||||
|
|
@ -3186,6 +3240,9 @@ class $$MessagesTableAnnotationComposer
|
|||
GeneratedColumn<bool> get acknowledgeByServer => $composableBuilder(
|
||||
column: $table.acknowledgeByServer, builder: (column) => column);
|
||||
|
||||
GeneratedColumn<bool> get errorWhileSending => $composableBuilder(
|
||||
column: $table.errorWhileSending, builder: (column) => column);
|
||||
|
||||
GeneratedColumnWithTypeConverter<MessageKind, String> get kind =>
|
||||
$composableBuilder(column: $table.kind, builder: (column) => column);
|
||||
|
||||
|
|
@ -3253,6 +3310,7 @@ class $$MessagesTableTableManager extends RootTableManager<
|
|||
Value<bool> acknowledgeByUser = const Value.absent(),
|
||||
Value<DownloadState> downloadState = const Value.absent(),
|
||||
Value<bool> acknowledgeByServer = const Value.absent(),
|
||||
Value<bool> errorWhileSending = const Value.absent(),
|
||||
Value<MessageKind> kind = const Value.absent(),
|
||||
Value<String?> contentJson = const Value.absent(),
|
||||
Value<DateTime?> openedAt = const Value.absent(),
|
||||
|
|
@ -3268,6 +3326,7 @@ class $$MessagesTableTableManager extends RootTableManager<
|
|||
acknowledgeByUser: acknowledgeByUser,
|
||||
downloadState: downloadState,
|
||||
acknowledgeByServer: acknowledgeByServer,
|
||||
errorWhileSending: errorWhileSending,
|
||||
kind: kind,
|
||||
contentJson: contentJson,
|
||||
openedAt: openedAt,
|
||||
|
|
@ -3283,6 +3342,7 @@ class $$MessagesTableTableManager extends RootTableManager<
|
|||
Value<bool> acknowledgeByUser = const Value.absent(),
|
||||
Value<DownloadState> downloadState = const Value.absent(),
|
||||
Value<bool> acknowledgeByServer = const Value.absent(),
|
||||
Value<bool> errorWhileSending = const Value.absent(),
|
||||
required MessageKind kind,
|
||||
Value<String?> contentJson = const Value.absent(),
|
||||
Value<DateTime?> openedAt = const Value.absent(),
|
||||
|
|
@ -3298,6 +3358,7 @@ class $$MessagesTableTableManager extends RootTableManager<
|
|||
acknowledgeByUser: acknowledgeByUser,
|
||||
downloadState: downloadState,
|
||||
acknowledgeByServer: acknowledgeByServer,
|
||||
errorWhileSending: errorWhileSending,
|
||||
kind: kind,
|
||||
contentJson: contentJson,
|
||||
openedAt: openedAt,
|
||||
|
|
|
|||
433
lib/src/database/twonly_database.steps.dart
Normal file
433
lib/src/database/twonly_database.steps.dart
Normal file
|
|
@ -0,0 +1,433 @@
|
|||
// dart format width=80
|
||||
import 'package:drift/internal/versioned_schema.dart' as i0;
|
||||
import 'package:drift/drift.dart' as i1;
|
||||
import 'dart:typed_data' as i2;
|
||||
import 'package:drift/drift.dart'; // ignore_for_file: type=lint,unused_import
|
||||
|
||||
// GENERATED BY drift_dev, DO NOT MODIFY.
|
||||
final class Schema2 extends i0.VersionedSchema {
|
||||
Schema2({required super.database}) : super(version: 2);
|
||||
@override
|
||||
late final List<i1.DatabaseSchemaEntity> entities = [
|
||||
contacts,
|
||||
messages,
|
||||
signalIdentityKeyStores,
|
||||
signalPreKeyStores,
|
||||
signalSenderKeyStores,
|
||||
signalSessionStores,
|
||||
];
|
||||
late final Shape0 contacts = Shape0(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'contacts',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: [
|
||||
'PRIMARY KEY(user_id)',
|
||||
],
|
||||
columns: [
|
||||
_column_0,
|
||||
_column_1,
|
||||
_column_2,
|
||||
_column_3,
|
||||
_column_4,
|
||||
_column_5,
|
||||
_column_6,
|
||||
_column_7,
|
||||
_column_8,
|
||||
_column_9,
|
||||
_column_10,
|
||||
_column_11,
|
||||
_column_12,
|
||||
_column_13,
|
||||
_column_14,
|
||||
_column_15,
|
||||
_column_16,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null);
|
||||
late final Shape1 messages = Shape1(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'messages',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: [],
|
||||
columns: [
|
||||
_column_17,
|
||||
_column_18,
|
||||
_column_19,
|
||||
_column_20,
|
||||
_column_21,
|
||||
_column_22,
|
||||
_column_23,
|
||||
_column_24,
|
||||
_column_25,
|
||||
_column_26,
|
||||
_column_27,
|
||||
_column_28,
|
||||
_column_29,
|
||||
_column_30,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null);
|
||||
late final Shape2 signalIdentityKeyStores = Shape2(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'signal_identity_key_stores',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: [
|
||||
'PRIMARY KEY(device_id, name)',
|
||||
],
|
||||
columns: [
|
||||
_column_31,
|
||||
_column_32,
|
||||
_column_33,
|
||||
_column_10,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null);
|
||||
late final Shape3 signalPreKeyStores = Shape3(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'signal_pre_key_stores',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: [
|
||||
'PRIMARY KEY(pre_key_id)',
|
||||
],
|
||||
columns: [
|
||||
_column_34,
|
||||
_column_35,
|
||||
_column_10,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null);
|
||||
late final Shape4 signalSenderKeyStores = Shape4(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'signal_sender_key_stores',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: [
|
||||
'PRIMARY KEY(sender_key_name)',
|
||||
],
|
||||
columns: [
|
||||
_column_36,
|
||||
_column_37,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null);
|
||||
late final Shape5 signalSessionStores = Shape5(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'signal_session_stores',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: [
|
||||
'PRIMARY KEY(device_id, name)',
|
||||
],
|
||||
columns: [
|
||||
_column_31,
|
||||
_column_32,
|
||||
_column_38,
|
||||
_column_10,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null);
|
||||
}
|
||||
|
||||
class Shape0 extends i0.VersionedTable {
|
||||
Shape0({required super.source, required super.alias}) : super.aliased();
|
||||
i1.GeneratedColumn<int> get userId =>
|
||||
columnsByName['user_id']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<String> get username =>
|
||||
columnsByName['username']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get displayName =>
|
||||
columnsByName['display_name']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get nickName =>
|
||||
columnsByName['nick_name']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get avatarSvg =>
|
||||
columnsByName['avatar_svg']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<int> get myAvatarCounter =>
|
||||
columnsByName['my_avatar_counter']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<bool> get accepted =>
|
||||
columnsByName['accepted']! as i1.GeneratedColumn<bool>;
|
||||
i1.GeneratedColumn<bool> get requested =>
|
||||
columnsByName['requested']! as i1.GeneratedColumn<bool>;
|
||||
i1.GeneratedColumn<bool> get blocked =>
|
||||
columnsByName['blocked']! as i1.GeneratedColumn<bool>;
|
||||
i1.GeneratedColumn<bool> get verified =>
|
||||
columnsByName['verified']! as i1.GeneratedColumn<bool>;
|
||||
i1.GeneratedColumn<DateTime> get createdAt =>
|
||||
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
|
||||
i1.GeneratedColumn<int> get totalMediaCounter =>
|
||||
columnsByName['total_media_counter']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<DateTime> get lastMessageSend =>
|
||||
columnsByName['last_message_send']! as i1.GeneratedColumn<DateTime>;
|
||||
i1.GeneratedColumn<DateTime> get lastMessageReceived =>
|
||||
columnsByName['last_message_received']! as i1.GeneratedColumn<DateTime>;
|
||||
i1.GeneratedColumn<DateTime> get lastFlameCounterChange =>
|
||||
columnsByName['last_flame_counter_change']!
|
||||
as i1.GeneratedColumn<DateTime>;
|
||||
i1.GeneratedColumn<DateTime> get lastMessageExchange =>
|
||||
columnsByName['last_message_exchange']! as i1.GeneratedColumn<DateTime>;
|
||||
i1.GeneratedColumn<int> get flameCounter =>
|
||||
columnsByName['flame_counter']! as i1.GeneratedColumn<int>;
|
||||
}
|
||||
|
||||
i1.GeneratedColumn<int> _column_0(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>('user_id', aliasedName, false,
|
||||
type: i1.DriftSqlType.int);
|
||||
i1.GeneratedColumn<String> _column_1(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>('username', aliasedName, false,
|
||||
type: i1.DriftSqlType.string,
|
||||
defaultConstraints: i1.GeneratedColumn.constraintIsAlways('UNIQUE'));
|
||||
i1.GeneratedColumn<String> _column_2(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>('display_name', aliasedName, true,
|
||||
type: i1.DriftSqlType.string);
|
||||
i1.GeneratedColumn<String> _column_3(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>('nick_name', aliasedName, true,
|
||||
type: i1.DriftSqlType.string);
|
||||
i1.GeneratedColumn<String> _column_4(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>('avatar_svg', aliasedName, true,
|
||||
type: i1.DriftSqlType.string);
|
||||
i1.GeneratedColumn<int> _column_5(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>('my_avatar_counter', aliasedName, false,
|
||||
type: i1.DriftSqlType.int, defaultValue: const CustomExpression('0'));
|
||||
i1.GeneratedColumn<bool> _column_6(String aliasedName) =>
|
||||
i1.GeneratedColumn<bool>('accepted', aliasedName, false,
|
||||
type: i1.DriftSqlType.bool,
|
||||
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
||||
'CHECK ("accepted" IN (0, 1))'),
|
||||
defaultValue: const CustomExpression('0'));
|
||||
i1.GeneratedColumn<bool> _column_7(String aliasedName) =>
|
||||
i1.GeneratedColumn<bool>('requested', aliasedName, false,
|
||||
type: i1.DriftSqlType.bool,
|
||||
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
||||
'CHECK ("requested" IN (0, 1))'),
|
||||
defaultValue: const CustomExpression('0'));
|
||||
i1.GeneratedColumn<bool> _column_8(String aliasedName) =>
|
||||
i1.GeneratedColumn<bool>('blocked', aliasedName, false,
|
||||
type: i1.DriftSqlType.bool,
|
||||
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
||||
'CHECK ("blocked" IN (0, 1))'),
|
||||
defaultValue: const CustomExpression('0'));
|
||||
i1.GeneratedColumn<bool> _column_9(String aliasedName) =>
|
||||
i1.GeneratedColumn<bool>('verified', aliasedName, false,
|
||||
type: i1.DriftSqlType.bool,
|
||||
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
||||
'CHECK ("verified" IN (0, 1))'),
|
||||
defaultValue: const CustomExpression('0'));
|
||||
i1.GeneratedColumn<DateTime> _column_10(String aliasedName) =>
|
||||
i1.GeneratedColumn<DateTime>('created_at', aliasedName, false,
|
||||
type: i1.DriftSqlType.dateTime,
|
||||
defaultValue: const CustomExpression(
|
||||
'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)'));
|
||||
i1.GeneratedColumn<int> _column_11(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>('total_media_counter', aliasedName, false,
|
||||
type: i1.DriftSqlType.int, defaultValue: const CustomExpression('0'));
|
||||
i1.GeneratedColumn<DateTime> _column_12(String aliasedName) =>
|
||||
i1.GeneratedColumn<DateTime>('last_message_send', aliasedName, true,
|
||||
type: i1.DriftSqlType.dateTime);
|
||||
i1.GeneratedColumn<DateTime> _column_13(String aliasedName) =>
|
||||
i1.GeneratedColumn<DateTime>('last_message_received', aliasedName, true,
|
||||
type: i1.DriftSqlType.dateTime);
|
||||
i1.GeneratedColumn<DateTime> _column_14(String aliasedName) =>
|
||||
i1.GeneratedColumn<DateTime>('last_flame_counter_change', aliasedName, true,
|
||||
type: i1.DriftSqlType.dateTime);
|
||||
i1.GeneratedColumn<DateTime> _column_15(String aliasedName) =>
|
||||
i1.GeneratedColumn<DateTime>('last_message_exchange', aliasedName, false,
|
||||
type: i1.DriftSqlType.dateTime,
|
||||
defaultValue: const CustomExpression(
|
||||
'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)'));
|
||||
i1.GeneratedColumn<int> _column_16(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>('flame_counter', aliasedName, false,
|
||||
type: i1.DriftSqlType.int, defaultValue: const CustomExpression('0'));
|
||||
|
||||
class Shape1 extends i0.VersionedTable {
|
||||
Shape1({required super.source, required super.alias}) : super.aliased();
|
||||
i1.GeneratedColumn<int> get contactId =>
|
||||
columnsByName['contact_id']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<int> get messageId =>
|
||||
columnsByName['message_id']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<int> get messageOtherId =>
|
||||
columnsByName['message_other_id']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<int> get responseToMessageId =>
|
||||
columnsByName['response_to_message_id']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<int> get responseToOtherMessageId =>
|
||||
columnsByName['response_to_other_message_id']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<bool> get acknowledgeByUser =>
|
||||
columnsByName['acknowledge_by_user']! as i1.GeneratedColumn<bool>;
|
||||
i1.GeneratedColumn<int> get downloadState =>
|
||||
columnsByName['download_state']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<bool> get acknowledgeByServer =>
|
||||
columnsByName['acknowledge_by_server']! as i1.GeneratedColumn<bool>;
|
||||
i1.GeneratedColumn<bool> get errorWhileSending =>
|
||||
columnsByName['error_while_sending']! as i1.GeneratedColumn<bool>;
|
||||
i1.GeneratedColumn<String> get kind =>
|
||||
columnsByName['kind']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get contentJson =>
|
||||
columnsByName['content_json']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<DateTime> get openedAt =>
|
||||
columnsByName['opened_at']! as i1.GeneratedColumn<DateTime>;
|
||||
i1.GeneratedColumn<DateTime> get sendAt =>
|
||||
columnsByName['send_at']! as i1.GeneratedColumn<DateTime>;
|
||||
i1.GeneratedColumn<DateTime> get updatedAt =>
|
||||
columnsByName['updated_at']! as i1.GeneratedColumn<DateTime>;
|
||||
}
|
||||
|
||||
i1.GeneratedColumn<int> _column_17(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>('contact_id', aliasedName, false,
|
||||
type: i1.DriftSqlType.int,
|
||||
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
||||
'REFERENCES contacts (user_id)'));
|
||||
i1.GeneratedColumn<int> _column_18(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>('message_id', aliasedName, false,
|
||||
hasAutoIncrement: true,
|
||||
type: i1.DriftSqlType.int,
|
||||
defaultConstraints:
|
||||
i1.GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
|
||||
i1.GeneratedColumn<int> _column_19(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>('message_other_id', aliasedName, true,
|
||||
type: i1.DriftSqlType.int);
|
||||
i1.GeneratedColumn<int> _column_20(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>('response_to_message_id', aliasedName, true,
|
||||
type: i1.DriftSqlType.int);
|
||||
i1.GeneratedColumn<int> _column_21(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>('response_to_other_message_id', aliasedName, true,
|
||||
type: i1.DriftSqlType.int);
|
||||
i1.GeneratedColumn<bool> _column_22(String aliasedName) =>
|
||||
i1.GeneratedColumn<bool>('acknowledge_by_user', aliasedName, false,
|
||||
type: i1.DriftSqlType.bool,
|
||||
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
||||
'CHECK ("acknowledge_by_user" IN (0, 1))'),
|
||||
defaultValue: const CustomExpression('0'));
|
||||
i1.GeneratedColumn<int> _column_23(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>('download_state', aliasedName, false,
|
||||
type: i1.DriftSqlType.int, defaultValue: const CustomExpression('2'));
|
||||
i1.GeneratedColumn<bool> _column_24(String aliasedName) =>
|
||||
i1.GeneratedColumn<bool>('acknowledge_by_server', aliasedName, false,
|
||||
type: i1.DriftSqlType.bool,
|
||||
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
||||
'CHECK ("acknowledge_by_server" IN (0, 1))'),
|
||||
defaultValue: const CustomExpression('0'));
|
||||
i1.GeneratedColumn<bool> _column_25(String aliasedName) =>
|
||||
i1.GeneratedColumn<bool>('error_while_sending', aliasedName, false,
|
||||
type: i1.DriftSqlType.bool,
|
||||
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
||||
'CHECK ("error_while_sending" IN (0, 1))'),
|
||||
defaultValue: const CustomExpression('0'));
|
||||
i1.GeneratedColumn<String> _column_26(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>('kind', aliasedName, false,
|
||||
type: i1.DriftSqlType.string);
|
||||
i1.GeneratedColumn<String> _column_27(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>('content_json', aliasedName, true,
|
||||
type: i1.DriftSqlType.string);
|
||||
i1.GeneratedColumn<DateTime> _column_28(String aliasedName) =>
|
||||
i1.GeneratedColumn<DateTime>('opened_at', aliasedName, true,
|
||||
type: i1.DriftSqlType.dateTime);
|
||||
i1.GeneratedColumn<DateTime> _column_29(String aliasedName) =>
|
||||
i1.GeneratedColumn<DateTime>('send_at', aliasedName, false,
|
||||
type: i1.DriftSqlType.dateTime,
|
||||
defaultValue: const CustomExpression(
|
||||
'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)'));
|
||||
i1.GeneratedColumn<DateTime> _column_30(String aliasedName) =>
|
||||
i1.GeneratedColumn<DateTime>('updated_at', aliasedName, false,
|
||||
type: i1.DriftSqlType.dateTime,
|
||||
defaultValue: const CustomExpression(
|
||||
'CAST(strftime(\'%s\', CURRENT_TIMESTAMP) AS INTEGER)'));
|
||||
|
||||
class Shape2 extends i0.VersionedTable {
|
||||
Shape2({required super.source, required super.alias}) : super.aliased();
|
||||
i1.GeneratedColumn<int> get deviceId =>
|
||||
columnsByName['device_id']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<String> get name =>
|
||||
columnsByName['name']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<i2.Uint8List> get identityKey =>
|
||||
columnsByName['identity_key']! as i1.GeneratedColumn<i2.Uint8List>;
|
||||
i1.GeneratedColumn<DateTime> get createdAt =>
|
||||
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
|
||||
}
|
||||
|
||||
i1.GeneratedColumn<int> _column_31(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>('device_id', aliasedName, false,
|
||||
type: i1.DriftSqlType.int);
|
||||
i1.GeneratedColumn<String> _column_32(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>('name', aliasedName, false,
|
||||
type: i1.DriftSqlType.string);
|
||||
i1.GeneratedColumn<i2.Uint8List> _column_33(String aliasedName) =>
|
||||
i1.GeneratedColumn<i2.Uint8List>('identity_key', aliasedName, false,
|
||||
type: i1.DriftSqlType.blob);
|
||||
|
||||
class Shape3 extends i0.VersionedTable {
|
||||
Shape3({required super.source, required super.alias}) : super.aliased();
|
||||
i1.GeneratedColumn<int> get preKeyId =>
|
||||
columnsByName['pre_key_id']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<i2.Uint8List> get preKey =>
|
||||
columnsByName['pre_key']! as i1.GeneratedColumn<i2.Uint8List>;
|
||||
i1.GeneratedColumn<DateTime> get createdAt =>
|
||||
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
|
||||
}
|
||||
|
||||
i1.GeneratedColumn<int> _column_34(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>('pre_key_id', aliasedName, false,
|
||||
type: i1.DriftSqlType.int);
|
||||
i1.GeneratedColumn<i2.Uint8List> _column_35(String aliasedName) =>
|
||||
i1.GeneratedColumn<i2.Uint8List>('pre_key', aliasedName, false,
|
||||
type: i1.DriftSqlType.blob);
|
||||
|
||||
class Shape4 extends i0.VersionedTable {
|
||||
Shape4({required super.source, required super.alias}) : super.aliased();
|
||||
i1.GeneratedColumn<String> get senderKeyName =>
|
||||
columnsByName['sender_key_name']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<i2.Uint8List> get senderKey =>
|
||||
columnsByName['sender_key']! as i1.GeneratedColumn<i2.Uint8List>;
|
||||
}
|
||||
|
||||
i1.GeneratedColumn<String> _column_36(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>('sender_key_name', aliasedName, false,
|
||||
type: i1.DriftSqlType.string);
|
||||
i1.GeneratedColumn<i2.Uint8List> _column_37(String aliasedName) =>
|
||||
i1.GeneratedColumn<i2.Uint8List>('sender_key', aliasedName, false,
|
||||
type: i1.DriftSqlType.blob);
|
||||
|
||||
class Shape5 extends i0.VersionedTable {
|
||||
Shape5({required super.source, required super.alias}) : super.aliased();
|
||||
i1.GeneratedColumn<int> get deviceId =>
|
||||
columnsByName['device_id']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<String> get name =>
|
||||
columnsByName['name']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<i2.Uint8List> get sessionRecord =>
|
||||
columnsByName['session_record']! as i1.GeneratedColumn<i2.Uint8List>;
|
||||
i1.GeneratedColumn<DateTime> get createdAt =>
|
||||
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
|
||||
}
|
||||
|
||||
i1.GeneratedColumn<i2.Uint8List> _column_38(String aliasedName) =>
|
||||
i1.GeneratedColumn<i2.Uint8List>('session_record', aliasedName, false,
|
||||
type: i1.DriftSqlType.blob);
|
||||
i0.MigrationStepWithVersion migrationSteps({
|
||||
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
|
||||
}) {
|
||||
return (currentVersion, database) async {
|
||||
switch (currentVersion) {
|
||||
case 1:
|
||||
final schema = Schema2(database: database);
|
||||
final migrator = i1.Migrator(database, schema);
|
||||
await from1To2(migrator, schema);
|
||||
return 2;
|
||||
default:
|
||||
throw ArgumentError.value('Unknown migration from $currentVersion');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
i1.OnUpgrade stepByStep({
|
||||
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
|
||||
}) =>
|
||||
i0.VersionedSchema.stepByStepHelper(
|
||||
step: migrationSteps(
|
||||
from1To2: from1To2,
|
||||
));
|
||||
|
|
@ -1,16 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
enum MessageKind {
|
||||
textMessage,
|
||||
storedMediaFile,
|
||||
media,
|
||||
contactRequest,
|
||||
profileChange,
|
||||
rejectRequest,
|
||||
acceptRequest,
|
||||
opened,
|
||||
ack
|
||||
}
|
||||
import 'package:twonly/src/database/tables/messages_table.dart';
|
||||
|
||||
Map<String, Color> messageKindColors = {
|
||||
"video": Colors.deepPurple,
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@
|
|||
"messageSendState_Sending": "Wird gesendet",
|
||||
"messageSendState_TapToLoad": "Tippe zum Laden",
|
||||
"messageSendState_Loading": "Herunterladen",
|
||||
"messageStoredInGalery": "In Gallerie gespeichert",
|
||||
"imageEditorDrawOk": "Zeichnung machen",
|
||||
"settingsTitle": "Einstellungen",
|
||||
"settingsProfile": "Profil",
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@
|
|||
"messageSendState_Sending": "Sending",
|
||||
"messageSendState_TapToLoad": "Tap to load",
|
||||
"messageSendState_Loading": "Downloading",
|
||||
"messageStoredInGalery": "Stored in gallery",
|
||||
"imageEditorDrawOk": "Take drawing",
|
||||
"settingsTitle": "Settings",
|
||||
"settingsProfile": "Profile",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
|
@ -15,32 +16,75 @@ import 'package:twonly/src/utils/signal.dart' as SignalHelper;
|
|||
import 'package:twonly/src/utils/storage.dart';
|
||||
|
||||
Future tryTransmitMessages() async {
|
||||
List<Message> retransmit =
|
||||
await twonlyDatabase.messagesDao.getAllMessagesForRetransmitting();
|
||||
Map<String, dynamic> retransmit = await getAllMessagesForRetransmitting();
|
||||
|
||||
if (retransmit.isEmpty) return;
|
||||
|
||||
Logger("api.dart").info("try sending messages: ${retransmit.length}");
|
||||
|
||||
Box box = await getMediaStorage();
|
||||
for (int i = 0; i < retransmit.length; i++) {
|
||||
int msgId = retransmit[i].messageId;
|
||||
Map<String, dynamic> failed = {};
|
||||
|
||||
for (String key in retransmit.keys) {
|
||||
RetransmitMessage msg =
|
||||
RetransmitMessage.fromJson(jsonDecode(retransmit[key]));
|
||||
|
||||
Uint8List? bytes = box.get("retransmit-$msgId-textmessage");
|
||||
if (bytes != null) {
|
||||
Result resp = await apiProvider.sendTextMessage(
|
||||
retransmit[i].contactId,
|
||||
bytes,
|
||||
msg.userId,
|
||||
msg.bytes,
|
||||
);
|
||||
if (resp.isSuccess) {
|
||||
if (msg.messageId != null) {
|
||||
await twonlyDatabase.messagesDao.updateMessageByMessageId(
|
||||
msgId, MessagesCompanion(acknowledgeByServer: Value(true)));
|
||||
box.delete("retransmit-$msgId-textmessage");
|
||||
msg.messageId!,
|
||||
MessagesCompanion(
|
||||
acknowledgeByServer: Value(true),
|
||||
),
|
||||
);
|
||||
}
|
||||
failed[key] = retransmit[key];
|
||||
} else {
|
||||
// in case of error do nothing. As the message is not removed the app will try again when relaunched
|
||||
}
|
||||
}
|
||||
Box box = await getMediaStorage();
|
||||
box.put("messages-to-retransmit", jsonEncode(failed));
|
||||
}
|
||||
|
||||
class RetransmitMessage {
|
||||
int? messageId;
|
||||
int userId;
|
||||
Uint8List bytes;
|
||||
RetransmitMessage(
|
||||
{this.messageId, required this.userId, required this.bytes});
|
||||
|
||||
// From JSON constructor
|
||||
factory RetransmitMessage.fromJson(Map<String, dynamic> json) {
|
||||
return RetransmitMessage(
|
||||
messageId: json['messageId'],
|
||||
userId: json['userId'],
|
||||
bytes: base64Decode(json['bytes']),
|
||||
);
|
||||
}
|
||||
|
||||
// To JSON method
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'messageId': messageId,
|
||||
'userId': userId,
|
||||
'bytes': base64Encode(bytes),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getAllMessagesForRetransmitting() async {
|
||||
Box box = await getMediaStorage();
|
||||
String? retransmitJson = box.get("messages-to-retransmit");
|
||||
Map<String, dynamic> retransmit = {};
|
||||
|
||||
if (retransmitJson != null) {
|
||||
retransmit = jsonDecode(retransmitJson);
|
||||
}
|
||||
return retransmit;
|
||||
}
|
||||
|
||||
// this functions ensures that the message is received by the server and in case of errors will try again later
|
||||
|
|
@ -53,9 +97,19 @@ Future<Result> encryptAndSendMessage(
|
|||
return Result.error(ErrorCode.InternalError);
|
||||
}
|
||||
|
||||
String stateId = (messageId ?? (60001 + Random().nextInt(100000))).toString();
|
||||
Box box = await getMediaStorage();
|
||||
if (messageId != null) {
|
||||
box.put("retransmit-$messageId-textmessage", bytes);
|
||||
|
||||
{
|
||||
var retransmit = await getAllMessagesForRetransmitting();
|
||||
|
||||
retransmit[stateId] = jsonEncode(RetransmitMessage(
|
||||
messageId: messageId,
|
||||
userId: userId,
|
||||
bytes: bytes,
|
||||
).toJson());
|
||||
|
||||
box.put("messages-to-retransmit", jsonEncode(retransmit));
|
||||
}
|
||||
|
||||
Result resp = await apiProvider.sendTextMessage(userId, bytes);
|
||||
|
|
@ -66,6 +120,13 @@ Future<Result> encryptAndSendMessage(
|
|||
messageId,
|
||||
MessagesCompanion(acknowledgeByServer: Value(true)),
|
||||
);
|
||||
|
||||
{
|
||||
var retransmit = await getAllMessagesForRetransmitting();
|
||||
retransmit.remove(stateId);
|
||||
box.put("messages-to-retransmit", jsonEncode(retransmit));
|
||||
}
|
||||
|
||||
box.delete("retransmit-$messageId-textmessage");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -341,7 +341,7 @@ Future sendImage(
|
|||
Map<String, dynamic> allMediaFiles = {};
|
||||
|
||||
if (mediaFilesJson != null) {
|
||||
// allMediaFiles = jsonDecode(mediaFilesJson);
|
||||
allMediaFiles = jsonDecode(mediaFilesJson);
|
||||
}
|
||||
|
||||
allMediaFiles[stateId] = jsonEncode(states.toJson());
|
||||
|
|
@ -361,26 +361,42 @@ Future retransmitMediaFiles() async {
|
|||
|
||||
Map<String, dynamic> allMediaFiles = jsonDecode(mediaFilesJson);
|
||||
|
||||
bool allSuccess = true;
|
||||
|
||||
for (final entry in allMediaFiles.entries) {
|
||||
try {
|
||||
String stateId = entry.key;
|
||||
States states = States.fromJson(jsonDecode(entry.value));
|
||||
// upload one by one
|
||||
allSuccess = allSuccess &
|
||||
await uploadMediaState(stateId, states.prepareState, states.metadata);
|
||||
} catch (e) {
|
||||
Logger("media.dart").shout(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (allSuccess) {
|
||||
// when all retransmittions where sucessfull tag the errors
|
||||
final pendings = await twonlyDatabase.messagesDao
|
||||
.getAllMessagesPendingUploadOlderThanAMinute();
|
||||
for (final pending in pendings) {
|
||||
twonlyDatabase.messagesDao.updateMessageByMessageId(
|
||||
pending.messageId,
|
||||
MessagesCompanion(errorWhileSending: Value(true)),
|
||||
);
|
||||
}
|
||||
}
|
||||
// return allSuccess;
|
||||
}
|
||||
|
||||
// if the upload failes this function is called again from the retransmitMediaFiles function which is
|
||||
// called when the WebSocket is reconnected again.
|
||||
Future uploadMediaState(
|
||||
Future<bool> uploadMediaState(
|
||||
String stateId, PrepareState prepareState, Metadata metadata) async {
|
||||
final uploadState =
|
||||
await ImageUploader.uploadState(prepareState, metadata.userIds.length);
|
||||
if (uploadState == null) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -395,11 +411,8 @@ Future uploadMediaState(
|
|||
}
|
||||
}
|
||||
|
||||
final notifyState =
|
||||
await ImageUploader.notifyState(prepareState, uploadState, metadata);
|
||||
if (notifyState == null) {
|
||||
return;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Future tryDownloadMedia(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import 'dart:convert';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:twonly/src/services/notification_service.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
|
||||
Future initMediaStorage() async {
|
||||
|
|
@ -19,8 +21,9 @@ Future initMediaStorage() async {
|
|||
}
|
||||
|
||||
Future<Box> getMediaStorage() async {
|
||||
final storage = getSecureStorage();
|
||||
try {
|
||||
await initMediaStorage();
|
||||
final storage = getSecureStorage();
|
||||
|
||||
var encryptionKey =
|
||||
base64Url.decode((await storage.read(key: 'hive_encryption_key'))!);
|
||||
|
|
@ -29,4 +32,10 @@ Future<Box> getMediaStorage() async {
|
|||
'media_storage',
|
||||
encryptionCipher: HiveAesCipher(encryptionKey),
|
||||
);
|
||||
} catch (e) {
|
||||
await customLocalPushNotification("Secure Storage Error",
|
||||
"Settings > Apps > twonly > Storage and Cache > Press clear on both");
|
||||
Logger("hive.dart").shout(e);
|
||||
throw Exception(e);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ import 'package:flutter/services.dart';
|
|||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||
import 'package:twonly/src/database/daos/contacts_dao.dart';
|
||||
import 'package:twonly/src/database/tables/messages_table.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:twonly/src/json_models/message.dart' as my;
|
||||
|
||||
|
|
@ -151,16 +152,16 @@ Future localPushNotificationNewMessage(
|
|||
}
|
||||
}
|
||||
|
||||
if (message.kind == my.MessageKind.contactRequest) {
|
||||
if (message.kind == MessageKind.contactRequest) {
|
||||
msg =
|
||||
getPushNotificationText("contactRequest", getContactDisplayName(user));
|
||||
}
|
||||
|
||||
if (message.kind == my.MessageKind.acceptRequest) {
|
||||
if (message.kind == MessageKind.acceptRequest) {
|
||||
msg = getPushNotificationText("acceptRequest", getContactDisplayName(user));
|
||||
}
|
||||
|
||||
if (message.kind == my.MessageKind.storedMediaFile) {
|
||||
if (message.kind == MessageKind.storedMediaFile) {
|
||||
msg =
|
||||
getPushNotificationText("storedMediaFile", getContactDisplayName(user));
|
||||
}
|
||||
|
|
@ -193,3 +194,26 @@ Future localPushNotificationNewMessage(
|
|||
payload: message.kind.index.toString(),
|
||||
);
|
||||
}
|
||||
|
||||
Future customLocalPushNotification(String title, String msg) async {
|
||||
const AndroidNotificationDetails androidNotificationDetails =
|
||||
AndroidNotificationDetails(
|
||||
'1',
|
||||
'Error',
|
||||
channelDescription: 'Error messages.',
|
||||
importance: Importance.max,
|
||||
priority: Priority.max,
|
||||
);
|
||||
|
||||
const DarwinNotificationDetails darwinNotificationDetails =
|
||||
DarwinNotificationDetails();
|
||||
const NotificationDetails notificationDetails = NotificationDetails(
|
||||
android: androidNotificationDetails, iOS: darwinNotificationDetails);
|
||||
|
||||
await flutterLocalNotificationsPlugin.show(
|
||||
897898,
|
||||
title,
|
||||
msg,
|
||||
notificationDetails,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import 'package:twonly/globals.dart';
|
|||
import 'package:twonly/src/components/image_editor/action_button.dart';
|
||||
import 'package:twonly/src/components/media_view_sizing.dart';
|
||||
import 'package:twonly/src/components/notification_badge.dart';
|
||||
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||
import 'package:twonly/src/database/daos/contacts_dao.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:twonly/src/providers/api/media.dart';
|
||||
import 'package:twonly/src/providers/send_next_media_to.dart';
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import 'package:twonly/src/components/flame.dart';
|
|||
import 'package:twonly/src/components/headline.dart';
|
||||
import 'package:twonly/src/components/initialsavatar.dart';
|
||||
import 'package:twonly/src/components/verified_shield.dart';
|
||||
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||
import 'package:twonly/src/database/daos/contacts_dao.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:twonly/src/providers/api/media.dart';
|
||||
import 'package:twonly/src/providers/send_next_media_to.dart';
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import 'package:twonly/src/components/better_text.dart';
|
|||
import 'package:twonly/src/components/initialsavatar.dart';
|
||||
import 'package:twonly/src/components/message_send_state_icon.dart';
|
||||
import 'package:twonly/src/components/verified_shield.dart';
|
||||
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||
import 'package:twonly/src/database/daos/contacts_dao.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:twonly/src/database/tables/messages_table.dart';
|
||||
import 'package:twonly/src/json_models/message.dart';
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import 'package:twonly/src/components/initialsavatar.dart';
|
|||
import 'package:twonly/src/components/message_send_state_icon.dart';
|
||||
import 'package:twonly/src/components/notification_badge.dart';
|
||||
import 'package:twonly/src/components/user_context_menu.dart';
|
||||
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||
import 'package:twonly/src/database/daos/contacts_dao.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:twonly/src/database/tables/messages_table.dart';
|
||||
import 'package:twonly/src/json_models/message.dart';
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ import 'dart:async';
|
|||
import 'package:drift/drift.dart' hide Column;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:twonly/src/components/alert_dialog.dart';
|
||||
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||
import 'package:twonly/src/database/daos/contacts_dao.dart';
|
||||
import 'package:twonly/src/database/tables/messages_table.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import 'package:qr_flutter/qr_flutter.dart';
|
|||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/components/format_long_string.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||
import 'package:twonly/src/database/daos/contacts_dao.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/utils/signal.dart';
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@ import 'package:twonly/src/components/flame.dart';
|
|||
import 'package:twonly/src/components/initialsavatar.dart';
|
||||
import 'package:twonly/src/components/verified_shield.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/views/contact/contact_verify_view.dart';
|
||||
import 'package:twonly/src/database/daos/contacts_dao.dart';
|
||||
|
||||
class ContactView extends StatefulWidget {
|
||||
const ContactView(this.userId, {super.key});
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import 'package:drift/drift.dart' hide Column;
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/components/initialsavatar.dart';
|
||||
import 'package:twonly/src/database/tables/contacts_table.dart';
|
||||
import 'package:twonly/src/database/daos/contacts_dao.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
|
||||
|
|
|
|||
23
test/drift/twonly_database/generated/schema.dart
Normal file
23
test/drift/twonly_database/generated/schema.dart
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
// dart format width=80
|
||||
// GENERATED CODE, DO NOT EDIT BY HAND.
|
||||
// ignore_for_file: type=lint
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:drift/internal/migrations.dart';
|
||||
import 'schema_v1.dart' as v1;
|
||||
import 'schema_v2.dart' as v2;
|
||||
|
||||
class GeneratedHelper implements SchemaInstantiationHelper {
|
||||
@override
|
||||
GeneratedDatabase databaseForVersion(QueryExecutor db, int version) {
|
||||
switch (version) {
|
||||
case 1:
|
||||
return v1.DatabaseAtV1(db);
|
||||
case 2:
|
||||
return v2.DatabaseAtV2(db);
|
||||
default:
|
||||
throw MissingSchemaException(version, versions);
|
||||
}
|
||||
}
|
||||
|
||||
static const versions = const [1, 2];
|
||||
}
|
||||
2122
test/drift/twonly_database/generated/schema_v1.dart
Normal file
2122
test/drift/twonly_database/generated/schema_v1.dart
Normal file
File diff suppressed because it is too large
Load diff
2157
test/drift/twonly_database/generated/schema_v2.dart
Normal file
2157
test/drift/twonly_database/generated/schema_v2.dart
Normal file
File diff suppressed because it is too large
Load diff
103
test/drift/twonly_database/migration_test.dart
Normal file
103
test/drift/twonly_database/migration_test.dart
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
// dart format width=80
|
||||
// ignore_for_file: unused_local_variable, unused_import
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:drift_dev/api/migrations_native.dart';
|
||||
import 'package:twonly/src/database/twonly_database.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'generated/schema.dart';
|
||||
|
||||
import 'generated/schema_v1.dart' as v1;
|
||||
import 'generated/schema_v2.dart' as v2;
|
||||
|
||||
void main() {
|
||||
driftRuntimeOptions.dontWarnAboutMultipleDatabases = true;
|
||||
late SchemaVerifier verifier;
|
||||
|
||||
setUpAll(() {
|
||||
verifier = SchemaVerifier(GeneratedHelper());
|
||||
});
|
||||
|
||||
group('simple database migrations', () {
|
||||
// These simple tests verify all possible schema updates with a simple (no
|
||||
// data) migration. This is a quick way to ensure that written database
|
||||
// migrations properly alter the schema.
|
||||
const versions = GeneratedHelper.versions;
|
||||
for (final (i, fromVersion) in versions.indexed) {
|
||||
group('from $fromVersion', () {
|
||||
for (final toVersion in versions.skip(i + 1)) {
|
||||
test('to $toVersion', () async {
|
||||
final schema = await verifier.schemaAt(fromVersion);
|
||||
final db = TwonlyDatabase(schema.newConnection());
|
||||
await verifier.migrateAndValidate(db, toVersion);
|
||||
await db.close();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// The following template shows how to write tests ensuring your migrations
|
||||
// preserve existing data.
|
||||
// Testing this can be useful for migrations that change existing columns
|
||||
// (e.g. by alterating their type or constraints). Migrations that only add
|
||||
// tables or columns typically don't need these advanced tests. For more
|
||||
// information, see https://drift.simonbinder.eu/migrations/tests/#verifying-data-integrity
|
||||
// TODO: This generated template shows how these tests could be written. Adopt
|
||||
// it to your own needs when testing migrations with data integrity.
|
||||
test('migration from v1 to v2 does not corrupt data', () async {
|
||||
// Add data to insert into the old database, and the expected rows after the
|
||||
// migration.
|
||||
// TODO: Fill these lists
|
||||
final oldContactsData = <v1.ContactsData>[];
|
||||
final expectedNewContactsData = <v2.ContactsData>[];
|
||||
|
||||
final oldMessagesData = <v1.MessagesData>[];
|
||||
final expectedNewMessagesData = <v2.MessagesData>[];
|
||||
|
||||
final oldSignalIdentityKeyStoresData = <v1.SignalIdentityKeyStoresData>[];
|
||||
final expectedNewSignalIdentityKeyStoresData =
|
||||
<v2.SignalIdentityKeyStoresData>[];
|
||||
|
||||
final oldSignalPreKeyStoresData = <v1.SignalPreKeyStoresData>[];
|
||||
final expectedNewSignalPreKeyStoresData = <v2.SignalPreKeyStoresData>[];
|
||||
|
||||
final oldSignalSenderKeyStoresData = <v1.SignalSenderKeyStoresData>[];
|
||||
final expectedNewSignalSenderKeyStoresData =
|
||||
<v2.SignalSenderKeyStoresData>[];
|
||||
|
||||
final oldSignalSessionStoresData = <v1.SignalSessionStoresData>[];
|
||||
final expectedNewSignalSessionStoresData = <v2.SignalSessionStoresData>[];
|
||||
|
||||
await verifier.testWithDataIntegrity(
|
||||
oldVersion: 1,
|
||||
newVersion: 2,
|
||||
createOld: v1.DatabaseAtV1.new,
|
||||
createNew: v2.DatabaseAtV2.new,
|
||||
openTestedDatabase: TwonlyDatabase.new,
|
||||
createItems: (batch, oldDb) {
|
||||
batch.insertAll(oldDb.contacts, oldContactsData);
|
||||
batch.insertAll(oldDb.messages, oldMessagesData);
|
||||
batch.insertAll(
|
||||
oldDb.signalIdentityKeyStores, oldSignalIdentityKeyStoresData);
|
||||
batch.insertAll(oldDb.signalPreKeyStores, oldSignalPreKeyStoresData);
|
||||
batch.insertAll(
|
||||
oldDb.signalSenderKeyStores, oldSignalSenderKeyStoresData);
|
||||
batch.insertAll(oldDb.signalSessionStores, oldSignalSessionStoresData);
|
||||
},
|
||||
validateItems: (newDb) async {
|
||||
expect(
|
||||
expectedNewContactsData, await newDb.select(newDb.contacts).get());
|
||||
expect(
|
||||
expectedNewMessagesData, await newDb.select(newDb.messages).get());
|
||||
expect(expectedNewSignalIdentityKeyStoresData,
|
||||
await newDb.select(newDb.signalIdentityKeyStores).get());
|
||||
expect(expectedNewSignalPreKeyStoresData,
|
||||
await newDb.select(newDb.signalPreKeyStores).get());
|
||||
expect(expectedNewSignalSenderKeyStoresData,
|
||||
await newDb.select(newDb.signalSenderKeyStores).get());
|
||||
expect(expectedNewSignalSessionStoresData,
|
||||
await newDb.select(newDb.signalSessionStores).get());
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
Loading…
Reference in a new issue