This commit is contained in:
otsmr 2025-05-27 23:41:43 +02:00
parent f3c9dbb796
commit 87bc1ed824
18 changed files with 3909 additions and 402 deletions

File diff suppressed because one or more lines are too long

View file

@ -129,8 +129,8 @@ class _AppState extends State<App> with WidgetsBindingObserver {
themeMode: context.watch<SettingsChangeProvider>().themeMode, themeMode: context.watch<SettingsChangeProvider>().themeMode,
initialRoute: '/', initialRoute: '/',
routes: { routes: {
"/": (context) => AppMainWidget(initialPage: 0), "/": (context) => AppMainWidget(initialPage: 1),
"/chats": (context) => AppMainWidget(initialPage: 1) "/chats": (context) => AppMainWidget(initialPage: 0)
}, },
); );
}, },

View file

@ -2,16 +2,18 @@ import 'dart:convert';
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
enum UploadState { enum UploadState {
pending, // legacy
addedToMessagesDb, addedToMessagesDb,
// added .compressed to filename
isCompressed, isCompressed,
// added .encypted to filename
isEncrypted, isEncrypted,
hasUploadToken, hasUploadToken,
isUploaded, isUploaded,
// ^^ legacy ^^
pending,
readyToUpload,
receiverNotified, receiverNotified,
// after all users notified all media files that are not storeable by the other person will be deleted // after all users notified all media files that are not storable by the other person will be deleted
} }
@DataClassName('MediaUpload') @DataClassName('MediaUpload')
@ -20,7 +22,8 @@ class MediaUploads extends Table {
TextColumn get state => TextColumn get state =>
textEnum<UploadState>().withDefault(Constant(UploadState.pending.name))(); textEnum<UploadState>().withDefault(Constant(UploadState.pending.name))();
TextColumn get metadata => text().map(MediaUploadMetadataConverter())(); TextColumn get metadata =>
text().map(MediaUploadMetadataConverter()).nullable()();
/// exists in UploadState.addedToMessagesDb /// exists in UploadState.addedToMessagesDb
TextColumn get messageIds => text().map(IntListTypeConverter()).nullable()(); TextColumn get messageIds => text().map(IntListTypeConverter()).nullable()();

View file

@ -43,7 +43,7 @@ class TwonlyDatabase extends _$TwonlyDatabase {
TwonlyDatabase.forTesting(DatabaseConnection super.connection); TwonlyDatabase.forTesting(DatabaseConnection super.connection);
@override @override
int get schemaVersion => 8; int get schemaVersion => 9;
static QueryExecutor _openConnection() { static QueryExecutor _openConnection() {
return driftDatabase( return driftDatabase(
@ -57,34 +57,34 @@ class TwonlyDatabase extends _$TwonlyDatabase {
@override @override
MigrationStrategy get migration { MigrationStrategy get migration {
return MigrationStrategy( return MigrationStrategy(
onUpgrade: stepByStep( onUpgrade: stepByStep(from1To2: (m, schema) async {
from1To2: (m, schema) async {
m.addColumn(schema.messages, schema.messages.errorWhileSending); m.addColumn(schema.messages, schema.messages.errorWhileSending);
}, }, from2To3: (m, schema) async {
from2To3: (m, schema) async {
m.addColumn(schema.contacts, schema.contacts.archived); m.addColumn(schema.contacts, schema.contacts.archived);
m.addColumn( m.addColumn(
schema.contacts, schema.contacts.deleteMessagesAfterXMinutes); schema.contacts, schema.contacts.deleteMessagesAfterXMinutes);
}, }, from3To4: (m, schema) async {
from3To4: (m, schema) async {
m.createTable(mediaUploads); m.createTable(mediaUploads);
}, }, from4To5: (m, schema) async {
from4To5: (m, schema) async {
m.createTable(mediaDownloads); m.createTable(mediaDownloads);
m.addColumn(schema.messages, schema.messages.mediaDownloadId); m.addColumn(schema.messages, schema.messages.mediaDownloadId);
m.addColumn(schema.messages, schema.messages.mediaUploadId); m.addColumn(schema.messages, schema.messages.mediaUploadId);
}, }, from5To6: (m, schema) async {
from5To6: (m, schema) async {
m.addColumn(schema.messages, schema.messages.mediaStored); m.addColumn(schema.messages, schema.messages.mediaStored);
}, }, from6To7: (m, schema) async {
from6To7: (m, schema) async {
m.addColumn(schema.contacts, schema.contacts.pinned); m.addColumn(schema.contacts, schema.contacts.pinned);
}, }, from7To8: (m, schema) async {
from7To8: (m, schema) async {
m.addColumn(schema.contacts, schema.contacts.alsoBestFriend); m.addColumn(schema.contacts, schema.contacts.alsoBestFriend);
m.addColumn(schema.contacts, schema.contacts.lastFlameSync); m.addColumn(schema.contacts, schema.contacts.lastFlameSync);
}, from8To9: (m, schema) async {
await m.alterTable(TableMigration(
schema.mediaUploads,
columnTransformer: {
schema.mediaUploads.metadata:
schema.mediaUploads.metadata.cast<String>(),
}, },
), ));
}),
); );
} }

View file

@ -1934,11 +1934,11 @@ class $MediaUploadsTable extends MediaUploads
defaultValue: Constant(UploadState.pending.name)) defaultValue: Constant(UploadState.pending.name))
.withConverter<UploadState>($MediaUploadsTable.$converterstate); .withConverter<UploadState>($MediaUploadsTable.$converterstate);
@override @override
late final GeneratedColumnWithTypeConverter<MediaUploadMetadata, String> late final GeneratedColumnWithTypeConverter<MediaUploadMetadata?, String>
metadata = GeneratedColumn<String>('metadata', aliasedName, false, metadata = GeneratedColumn<String>('metadata', aliasedName, true,
type: DriftSqlType.string, requiredDuringInsert: true) type: DriftSqlType.string, requiredDuringInsert: false)
.withConverter<MediaUploadMetadata>( .withConverter<MediaUploadMetadata?>(
$MediaUploadsTable.$convertermetadata); $MediaUploadsTable.$convertermetadatan);
@override @override
late final GeneratedColumnWithTypeConverter<List<int>?, String> messageIds = late final GeneratedColumnWithTypeConverter<List<int>?, String> messageIds =
GeneratedColumn<String>('message_ids', aliasedName, true, GeneratedColumn<String>('message_ids', aliasedName, true,
@ -2006,9 +2006,9 @@ class $MediaUploadsTable extends MediaUploads
state: $MediaUploadsTable.$converterstate.fromSql(attachedDatabase state: $MediaUploadsTable.$converterstate.fromSql(attachedDatabase
.typeMapping .typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}state'])!), .read(DriftSqlType.string, data['${effectivePrefix}state'])!),
metadata: $MediaUploadsTable.$convertermetadata.fromSql(attachedDatabase metadata: $MediaUploadsTable.$convertermetadatan.fromSql(attachedDatabase
.typeMapping .typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}metadata'])!), .read(DriftSqlType.string, data['${effectivePrefix}metadata'])),
messageIds: $MediaUploadsTable.$convertermessageIdsn.fromSql( messageIds: $MediaUploadsTable.$convertermessageIdsn.fromSql(
attachedDatabase.typeMapping.read( attachedDatabase.typeMapping.read(
DriftSqlType.string, data['${effectivePrefix}message_ids'])), DriftSqlType.string, data['${effectivePrefix}message_ids'])),
@ -2033,6 +2033,9 @@ class $MediaUploadsTable extends MediaUploads
const EnumNameConverter<UploadState>(UploadState.values); const EnumNameConverter<UploadState>(UploadState.values);
static JsonTypeConverter2<MediaUploadMetadata, String, Map<String, Object?>> static JsonTypeConverter2<MediaUploadMetadata, String, Map<String, Object?>>
$convertermetadata = MediaUploadMetadataConverter(); $convertermetadata = MediaUploadMetadataConverter();
static JsonTypeConverter2<MediaUploadMetadata?, String?,
Map<String, Object?>?> $convertermetadatan =
JsonTypeConverter2.asNullable($convertermetadata);
static TypeConverter<List<int>, String> $convertermessageIds = static TypeConverter<List<int>, String> $convertermessageIds =
IntListTypeConverter(); IntListTypeConverter();
static TypeConverter<List<int>?, String?> $convertermessageIdsn = static TypeConverter<List<int>?, String?> $convertermessageIdsn =
@ -2054,7 +2057,7 @@ class $MediaUploadsTable extends MediaUploads
class MediaUpload extends DataClass implements Insertable<MediaUpload> { class MediaUpload extends DataClass implements Insertable<MediaUpload> {
final int mediaUploadId; final int mediaUploadId;
final UploadState state; final UploadState state;
final MediaUploadMetadata metadata; final MediaUploadMetadata? metadata;
/// exists in UploadState.addedToMessagesDb /// exists in UploadState.addedToMessagesDb
final List<int>? messageIds; final List<int>? messageIds;
@ -2070,7 +2073,7 @@ class MediaUpload extends DataClass implements Insertable<MediaUpload> {
const MediaUpload( const MediaUpload(
{required this.mediaUploadId, {required this.mediaUploadId,
required this.state, required this.state,
required this.metadata, this.metadata,
this.messageIds, this.messageIds,
this.encryptionData, this.encryptionData,
this.uploadTokens, this.uploadTokens,
@ -2083,9 +2086,9 @@ class MediaUpload extends DataClass implements Insertable<MediaUpload> {
map['state'] = map['state'] =
Variable<String>($MediaUploadsTable.$converterstate.toSql(state)); Variable<String>($MediaUploadsTable.$converterstate.toSql(state));
} }
{ if (!nullToAbsent || metadata != null) {
map['metadata'] = Variable<String>( map['metadata'] = Variable<String>(
$MediaUploadsTable.$convertermetadata.toSql(metadata)); $MediaUploadsTable.$convertermetadatan.toSql(metadata));
} }
if (!nullToAbsent || messageIds != null) { if (!nullToAbsent || messageIds != null) {
map['message_ids'] = Variable<String>( map['message_ids'] = Variable<String>(
@ -2110,7 +2113,9 @@ class MediaUpload extends DataClass implements Insertable<MediaUpload> {
return MediaUploadsCompanion( return MediaUploadsCompanion(
mediaUploadId: Value(mediaUploadId), mediaUploadId: Value(mediaUploadId),
state: Value(state), state: Value(state),
metadata: Value(metadata), metadata: metadata == null && nullToAbsent
? const Value.absent()
: Value(metadata),
messageIds: messageIds == null && nullToAbsent messageIds: messageIds == null && nullToAbsent
? const Value.absent() ? const Value.absent()
: Value(messageIds), : Value(messageIds),
@ -2131,8 +2136,8 @@ class MediaUpload extends DataClass implements Insertable<MediaUpload> {
mediaUploadId: serializer.fromJson<int>(json['mediaUploadId']), mediaUploadId: serializer.fromJson<int>(json['mediaUploadId']),
state: $MediaUploadsTable.$converterstate state: $MediaUploadsTable.$converterstate
.fromJson(serializer.fromJson<String>(json['state'])), .fromJson(serializer.fromJson<String>(json['state'])),
metadata: $MediaUploadsTable.$convertermetadata.fromJson( metadata: $MediaUploadsTable.$convertermetadatan.fromJson(
serializer.fromJson<Map<String, Object?>>(json['metadata'])), serializer.fromJson<Map<String, Object?>?>(json['metadata'])),
messageIds: serializer.fromJson<List<int>?>(json['messageIds']), messageIds: serializer.fromJson<List<int>?>(json['messageIds']),
encryptionData: $MediaUploadsTable.$converterencryptionDatan.fromJson( encryptionData: $MediaUploadsTable.$converterencryptionDatan.fromJson(
serializer.fromJson<Map<String, Object?>?>(json['encryptionData'])), serializer.fromJson<Map<String, Object?>?>(json['encryptionData'])),
@ -2148,8 +2153,8 @@ class MediaUpload extends DataClass implements Insertable<MediaUpload> {
'mediaUploadId': serializer.toJson<int>(mediaUploadId), 'mediaUploadId': serializer.toJson<int>(mediaUploadId),
'state': serializer 'state': serializer
.toJson<String>($MediaUploadsTable.$converterstate.toJson(state)), .toJson<String>($MediaUploadsTable.$converterstate.toJson(state)),
'metadata': serializer.toJson<Map<String, Object?>>( 'metadata': serializer.toJson<Map<String, Object?>?>(
$MediaUploadsTable.$convertermetadata.toJson(metadata)), $MediaUploadsTable.$convertermetadatan.toJson(metadata)),
'messageIds': serializer.toJson<List<int>?>(messageIds), 'messageIds': serializer.toJson<List<int>?>(messageIds),
'encryptionData': serializer.toJson<Map<String, Object?>?>( 'encryptionData': serializer.toJson<Map<String, Object?>?>(
$MediaUploadsTable.$converterencryptionDatan.toJson(encryptionData)), $MediaUploadsTable.$converterencryptionDatan.toJson(encryptionData)),
@ -2162,7 +2167,7 @@ class MediaUpload extends DataClass implements Insertable<MediaUpload> {
MediaUpload copyWith( MediaUpload copyWith(
{int? mediaUploadId, {int? mediaUploadId,
UploadState? state, UploadState? state,
MediaUploadMetadata? metadata, Value<MediaUploadMetadata?> metadata = const Value.absent(),
Value<List<int>?> messageIds = const Value.absent(), Value<List<int>?> messageIds = const Value.absent(),
Value<MediaEncryptionData?> encryptionData = const Value.absent(), Value<MediaEncryptionData?> encryptionData = const Value.absent(),
Value<MediaUploadTokens?> uploadTokens = const Value.absent(), Value<MediaUploadTokens?> uploadTokens = const Value.absent(),
@ -2170,7 +2175,7 @@ class MediaUpload extends DataClass implements Insertable<MediaUpload> {
MediaUpload( MediaUpload(
mediaUploadId: mediaUploadId ?? this.mediaUploadId, mediaUploadId: mediaUploadId ?? this.mediaUploadId,
state: state ?? this.state, state: state ?? this.state,
metadata: metadata ?? this.metadata, metadata: metadata.present ? metadata.value : this.metadata,
messageIds: messageIds.present ? messageIds.value : this.messageIds, messageIds: messageIds.present ? messageIds.value : this.messageIds,
encryptionData: encryptionData:
encryptionData.present ? encryptionData.value : this.encryptionData, encryptionData.present ? encryptionData.value : this.encryptionData,
@ -2232,7 +2237,7 @@ class MediaUpload extends DataClass implements Insertable<MediaUpload> {
class MediaUploadsCompanion extends UpdateCompanion<MediaUpload> { class MediaUploadsCompanion extends UpdateCompanion<MediaUpload> {
final Value<int> mediaUploadId; final Value<int> mediaUploadId;
final Value<UploadState> state; final Value<UploadState> state;
final Value<MediaUploadMetadata> metadata; final Value<MediaUploadMetadata?> metadata;
final Value<List<int>?> messageIds; final Value<List<int>?> messageIds;
final Value<MediaEncryptionData?> encryptionData; final Value<MediaEncryptionData?> encryptionData;
final Value<MediaUploadTokens?> uploadTokens; final Value<MediaUploadTokens?> uploadTokens;
@ -2249,12 +2254,12 @@ class MediaUploadsCompanion extends UpdateCompanion<MediaUpload> {
MediaUploadsCompanion.insert({ MediaUploadsCompanion.insert({
this.mediaUploadId = const Value.absent(), this.mediaUploadId = const Value.absent(),
this.state = const Value.absent(), this.state = const Value.absent(),
required MediaUploadMetadata metadata, this.metadata = const Value.absent(),
this.messageIds = const Value.absent(), this.messageIds = const Value.absent(),
this.encryptionData = const Value.absent(), this.encryptionData = const Value.absent(),
this.uploadTokens = const Value.absent(), this.uploadTokens = const Value.absent(),
this.alreadyNotified = const Value.absent(), this.alreadyNotified = const Value.absent(),
}) : metadata = Value(metadata); });
static Insertable<MediaUpload> custom({ static Insertable<MediaUpload> custom({
Expression<int>? mediaUploadId, Expression<int>? mediaUploadId,
Expression<String>? state, Expression<String>? state,
@ -2278,7 +2283,7 @@ class MediaUploadsCompanion extends UpdateCompanion<MediaUpload> {
MediaUploadsCompanion copyWith( MediaUploadsCompanion copyWith(
{Value<int>? mediaUploadId, {Value<int>? mediaUploadId,
Value<UploadState>? state, Value<UploadState>? state,
Value<MediaUploadMetadata>? metadata, Value<MediaUploadMetadata?>? metadata,
Value<List<int>?>? messageIds, Value<List<int>?>? messageIds,
Value<MediaEncryptionData?>? encryptionData, Value<MediaEncryptionData?>? encryptionData,
Value<MediaUploadTokens?>? uploadTokens, Value<MediaUploadTokens?>? uploadTokens,
@ -2306,7 +2311,7 @@ class MediaUploadsCompanion extends UpdateCompanion<MediaUpload> {
} }
if (metadata.present) { if (metadata.present) {
map['metadata'] = Variable<String>( map['metadata'] = Variable<String>(
$MediaUploadsTable.$convertermetadata.toSql(metadata.value)); $MediaUploadsTable.$convertermetadatan.toSql(metadata.value));
} }
if (messageIds.present) { if (messageIds.present) {
map['message_ids'] = Variable<String>( map['message_ids'] = Variable<String>(
@ -4549,7 +4554,7 @@ typedef $$MediaUploadsTableCreateCompanionBuilder = MediaUploadsCompanion
Function({ Function({
Value<int> mediaUploadId, Value<int> mediaUploadId,
Value<UploadState> state, Value<UploadState> state,
required MediaUploadMetadata metadata, Value<MediaUploadMetadata?> metadata,
Value<List<int>?> messageIds, Value<List<int>?> messageIds,
Value<MediaEncryptionData?> encryptionData, Value<MediaEncryptionData?> encryptionData,
Value<MediaUploadTokens?> uploadTokens, Value<MediaUploadTokens?> uploadTokens,
@ -4559,7 +4564,7 @@ typedef $$MediaUploadsTableUpdateCompanionBuilder = MediaUploadsCompanion
Function({ Function({
Value<int> mediaUploadId, Value<int> mediaUploadId,
Value<UploadState> state, Value<UploadState> state,
Value<MediaUploadMetadata> metadata, Value<MediaUploadMetadata?> metadata,
Value<List<int>?> messageIds, Value<List<int>?> messageIds,
Value<MediaEncryptionData?> encryptionData, Value<MediaEncryptionData?> encryptionData,
Value<MediaUploadTokens?> uploadTokens, Value<MediaUploadTokens?> uploadTokens,
@ -4583,7 +4588,7 @@ class $$MediaUploadsTableFilterComposer
column: $table.state, column: $table.state,
builder: (column) => ColumnWithTypeConverterFilters(column)); builder: (column) => ColumnWithTypeConverterFilters(column));
ColumnWithTypeConverterFilters<MediaUploadMetadata, MediaUploadMetadata, ColumnWithTypeConverterFilters<MediaUploadMetadata?, MediaUploadMetadata,
String> String>
get metadata => $composableBuilder( get metadata => $composableBuilder(
column: $table.metadata, column: $table.metadata,
@ -4661,7 +4666,7 @@ class $$MediaUploadsTableAnnotationComposer
GeneratedColumnWithTypeConverter<UploadState, String> get state => GeneratedColumnWithTypeConverter<UploadState, String> get state =>
$composableBuilder(column: $table.state, builder: (column) => column); $composableBuilder(column: $table.state, builder: (column) => column);
GeneratedColumnWithTypeConverter<MediaUploadMetadata, String> get metadata => GeneratedColumnWithTypeConverter<MediaUploadMetadata?, String> get metadata =>
$composableBuilder(column: $table.metadata, builder: (column) => column); $composableBuilder(column: $table.metadata, builder: (column) => column);
GeneratedColumnWithTypeConverter<List<int>?, String> get messageIds => GeneratedColumnWithTypeConverter<List<int>?, String> get messageIds =>
@ -4709,7 +4714,7 @@ class $$MediaUploadsTableTableManager extends RootTableManager<
updateCompanionCallback: ({ updateCompanionCallback: ({
Value<int> mediaUploadId = const Value.absent(), Value<int> mediaUploadId = const Value.absent(),
Value<UploadState> state = const Value.absent(), Value<UploadState> state = const Value.absent(),
Value<MediaUploadMetadata> metadata = const Value.absent(), Value<MediaUploadMetadata?> metadata = const Value.absent(),
Value<List<int>?> messageIds = const Value.absent(), Value<List<int>?> messageIds = const Value.absent(),
Value<MediaEncryptionData?> encryptionData = const Value.absent(), Value<MediaEncryptionData?> encryptionData = const Value.absent(),
Value<MediaUploadTokens?> uploadTokens = const Value.absent(), Value<MediaUploadTokens?> uploadTokens = const Value.absent(),
@ -4727,7 +4732,7 @@ class $$MediaUploadsTableTableManager extends RootTableManager<
createCompanionCallback: ({ createCompanionCallback: ({
Value<int> mediaUploadId = const Value.absent(), Value<int> mediaUploadId = const Value.absent(),
Value<UploadState> state = const Value.absent(), Value<UploadState> state = const Value.absent(),
required MediaUploadMetadata metadata, Value<MediaUploadMetadata?> metadata = const Value.absent(),
Value<List<int>?> messageIds = const Value.absent(), Value<List<int>?> messageIds = const Value.absent(),
Value<MediaEncryptionData?> encryptionData = const Value.absent(), Value<MediaEncryptionData?> encryptionData = const Value.absent(),
Value<MediaUploadTokens?> uploadTokens = const Value.absent(), Value<MediaUploadTokens?> uploadTokens = const Value.absent(),

View file

@ -1701,6 +1701,184 @@ i1.GeneratedColumn<bool> _column_54(String aliasedName) =>
i1.GeneratedColumn<DateTime> _column_55(String aliasedName) => i1.GeneratedColumn<DateTime> _column_55(String aliasedName) =>
i1.GeneratedColumn<DateTime>('last_flame_sync', aliasedName, true, i1.GeneratedColumn<DateTime>('last_flame_sync', aliasedName, true,
type: i1.DriftSqlType.dateTime); type: i1.DriftSqlType.dateTime);
final class Schema9 extends i0.VersionedSchema {
Schema9({required super.database}) : super(version: 9);
@override
late final List<i1.DatabaseSchemaEntity> entities = [
contacts,
messages,
mediaUploads,
mediaDownloads,
signalIdentityKeyStores,
signalPreKeyStores,
signalSenderKeyStores,
signalSessionStores,
];
late final Shape12 contacts = Shape12(
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_39,
_column_53,
_column_54,
_column_40,
_column_10,
_column_11,
_column_12,
_column_13,
_column_14,
_column_55,
_column_15,
_column_16,
],
attachedDatabase: database,
),
alias: null);
late final Shape10 messages = Shape10(
source: i0.VersionedTable(
entityName: 'messages',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_17,
_column_18,
_column_19,
_column_48,
_column_49,
_column_20,
_column_21,
_column_22,
_column_52,
_column_23,
_column_24,
_column_25,
_column_26,
_column_27,
_column_28,
_column_29,
_column_30,
],
attachedDatabase: database,
),
alias: null);
late final Shape7 mediaUploads = Shape7(
source: i0.VersionedTable(
entityName: 'media_uploads',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_41,
_column_42,
_column_56,
_column_44,
_column_45,
_column_46,
_column_47,
],
attachedDatabase: database,
),
alias: null);
late final Shape9 mediaDownloads = Shape9(
source: i0.VersionedTable(
entityName: 'media_downloads',
withoutRowId: false,
isStrict: false,
tableConstraints: [],
columns: [
_column_50,
_column_51,
],
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);
}
i1.GeneratedColumn<String> _column_56(String aliasedName) =>
i1.GeneratedColumn<String>('metadata', aliasedName, true,
type: i1.DriftSqlType.string);
i0.MigrationStepWithVersion migrationSteps({ i0.MigrationStepWithVersion migrationSteps({
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2, required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3, required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
@ -1709,6 +1887,7 @@ i0.MigrationStepWithVersion migrationSteps({
required Future<void> Function(i1.Migrator m, Schema6 schema) from5To6, required Future<void> Function(i1.Migrator m, Schema6 schema) from5To6,
required Future<void> Function(i1.Migrator m, Schema7 schema) from6To7, required Future<void> Function(i1.Migrator m, Schema7 schema) from6To7,
required Future<void> Function(i1.Migrator m, Schema8 schema) from7To8, required Future<void> Function(i1.Migrator m, Schema8 schema) from7To8,
required Future<void> Function(i1.Migrator m, Schema9 schema) from8To9,
}) { }) {
return (currentVersion, database) async { return (currentVersion, database) async {
switch (currentVersion) { switch (currentVersion) {
@ -1747,6 +1926,11 @@ i0.MigrationStepWithVersion migrationSteps({
final migrator = i1.Migrator(database, schema); final migrator = i1.Migrator(database, schema);
await from7To8(migrator, schema); await from7To8(migrator, schema);
return 8; return 8;
case 8:
final schema = Schema9(database: database);
final migrator = i1.Migrator(database, schema);
await from8To9(migrator, schema);
return 9;
default: default:
throw ArgumentError.value('Unknown migration from $currentVersion'); throw ArgumentError.value('Unknown migration from $currentVersion');
} }
@ -1761,6 +1945,7 @@ i1.OnUpgrade stepByStep({
required Future<void> Function(i1.Migrator m, Schema6 schema) from5To6, required Future<void> Function(i1.Migrator m, Schema6 schema) from5To6,
required Future<void> Function(i1.Migrator m, Schema7 schema) from6To7, required Future<void> Function(i1.Migrator m, Schema7 schema) from6To7,
required Future<void> Function(i1.Migrator m, Schema8 schema) from7To8, required Future<void> Function(i1.Migrator m, Schema8 schema) from7To8,
required Future<void> Function(i1.Migrator m, Schema9 schema) from8To9,
}) => }) =>
i0.VersionedSchema.stepByStepHelper( i0.VersionedSchema.stepByStepHelper(
step: migrationSteps( step: migrationSteps(
@ -1771,4 +1956,5 @@ i1.OnUpgrade stepByStep({
from5To6: from5To6, from5To6: from5To6,
from6To7: from6To7, from6To7: from6To7,
from7To8: from7To8, from7To8: from7To8,
from8To9: from8To9,
)); ));

View file

@ -1545,6 +1545,70 @@ class ApplicationData_UploadData extends $pb.GeneratedMessage {
void clearChecksum() => clearField(4); void clearChecksum() => clearField(4);
} }
class ApplicationData_UploadDone extends $pb.GeneratedMessage {
factory ApplicationData_UploadDone({
$core.List<$core.int>? uploadToken,
$core.int? recipientsCount,
}) {
final $result = create();
if (uploadToken != null) {
$result.uploadToken = uploadToken;
}
if (recipientsCount != null) {
$result.recipientsCount = recipientsCount;
}
return $result;
}
ApplicationData_UploadDone._() : super();
factory ApplicationData_UploadDone.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory ApplicationData_UploadDone.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ApplicationData.UploadDone', package: const $pb.PackageName(_omitMessageNames ? '' : 'client_to_server'), createEmptyInstance: create)
..a<$core.List<$core.int>>(1, _omitFieldNames ? '' : 'uploadToken', $pb.PbFieldType.OY)
..a<$core.int>(2, _omitFieldNames ? '' : 'recipientsCount', $pb.PbFieldType.OU3)
..hasRequiredFields = false
;
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
'Will be removed in next major version')
ApplicationData_UploadDone clone() => ApplicationData_UploadDone()..mergeFromMessage(this);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version')
ApplicationData_UploadDone copyWith(void Function(ApplicationData_UploadDone) updates) => super.copyWith((message) => updates(message as ApplicationData_UploadDone)) as ApplicationData_UploadDone;
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static ApplicationData_UploadDone create() => ApplicationData_UploadDone._();
ApplicationData_UploadDone createEmptyInstance() => create();
static $pb.PbList<ApplicationData_UploadDone> createRepeated() => $pb.PbList<ApplicationData_UploadDone>();
@$core.pragma('dart2js:noInline')
static ApplicationData_UploadDone getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<ApplicationData_UploadDone>(create);
static ApplicationData_UploadDone? _defaultInstance;
@$pb.TagNumber(1)
$core.List<$core.int> get uploadToken => $_getN(0);
@$pb.TagNumber(1)
set uploadToken($core.List<$core.int> v) { $_setBytes(0, v); }
@$pb.TagNumber(1)
$core.bool hasUploadToken() => $_has(0);
@$pb.TagNumber(1)
void clearUploadToken() => clearField(1);
@$pb.TagNumber(2)
$core.int get recipientsCount => $_getIZ(1);
@$pb.TagNumber(2)
set recipientsCount($core.int v) { $_setUnsignedInt32(1, v); }
@$pb.TagNumber(2)
$core.bool hasRecipientsCount() => $_has(1);
@$pb.TagNumber(2)
void clearRecipientsCount() => clearField(2);
}
class ApplicationData_DownloadData extends $pb.GeneratedMessage { class ApplicationData_DownloadData extends $pb.GeneratedMessage {
factory ApplicationData_DownloadData({ factory ApplicationData_DownloadData({
$core.List<$core.int>? downloadToken, $core.List<$core.int>? downloadToken,
@ -1609,6 +1673,56 @@ class ApplicationData_DownloadData extends $pb.GeneratedMessage {
void clearOffset() => clearField(2); void clearOffset() => clearField(2);
} }
class ApplicationData_DownloadDone extends $pb.GeneratedMessage {
factory ApplicationData_DownloadDone({
$core.List<$core.int>? downloadToken,
}) {
final $result = create();
if (downloadToken != null) {
$result.downloadToken = downloadToken;
}
return $result;
}
ApplicationData_DownloadDone._() : super();
factory ApplicationData_DownloadDone.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory ApplicationData_DownloadDone.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ApplicationData.DownloadDone', package: const $pb.PackageName(_omitMessageNames ? '' : 'client_to_server'), createEmptyInstance: create)
..a<$core.List<$core.int>>(1, _omitFieldNames ? '' : 'downloadToken', $pb.PbFieldType.OY)
..hasRequiredFields = false
;
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
'Will be removed in next major version')
ApplicationData_DownloadDone clone() => ApplicationData_DownloadDone()..mergeFromMessage(this);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version')
ApplicationData_DownloadDone copyWith(void Function(ApplicationData_DownloadDone) updates) => super.copyWith((message) => updates(message as ApplicationData_DownloadDone)) as ApplicationData_DownloadDone;
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static ApplicationData_DownloadDone create() => ApplicationData_DownloadDone._();
ApplicationData_DownloadDone createEmptyInstance() => create();
static $pb.PbList<ApplicationData_DownloadDone> createRepeated() => $pb.PbList<ApplicationData_DownloadDone>();
@$core.pragma('dart2js:noInline')
static ApplicationData_DownloadDone getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<ApplicationData_DownloadDone>(create);
static ApplicationData_DownloadDone? _defaultInstance;
@$pb.TagNumber(1)
$core.List<$core.int> get downloadToken => $_getN(0);
@$pb.TagNumber(1)
set downloadToken($core.List<$core.int> v) { $_setBytes(0, v); }
@$pb.TagNumber(1)
$core.bool hasDownloadToken() => $_has(0);
@$pb.TagNumber(1)
void clearDownloadToken() => clearField(1);
}
enum ApplicationData_ApplicationData { enum ApplicationData_ApplicationData {
textmessage, textmessage,
getuserbyusername, getuserbyusername,
@ -1629,6 +1743,8 @@ enum ApplicationData_ApplicationData {
redeemadditionalcode, redeemadditionalcode,
removeadditionaluser, removeadditionaluser,
updateplanoptions, updateplanoptions,
downloaddone,
uploaddone,
notSet notSet
} }
@ -1653,6 +1769,8 @@ class ApplicationData extends $pb.GeneratedMessage {
ApplicationData_RedeemAdditionalCode? redeemadditionalcode, ApplicationData_RedeemAdditionalCode? redeemadditionalcode,
ApplicationData_RemoveAdditionalUser? removeadditionaluser, ApplicationData_RemoveAdditionalUser? removeadditionaluser,
ApplicationData_UpdatePlanOptions? updateplanoptions, ApplicationData_UpdatePlanOptions? updateplanoptions,
ApplicationData_DownloadDone? downloaddone,
ApplicationData_UploadDone? uploaddone,
}) { }) {
final $result = create(); final $result = create();
if (textmessage != null) { if (textmessage != null) {
@ -1712,6 +1830,12 @@ class ApplicationData extends $pb.GeneratedMessage {
if (updateplanoptions != null) { if (updateplanoptions != null) {
$result.updateplanoptions = updateplanoptions; $result.updateplanoptions = updateplanoptions;
} }
if (downloaddone != null) {
$result.downloaddone = downloaddone;
}
if (uploaddone != null) {
$result.uploaddone = uploaddone;
}
return $result; return $result;
} }
ApplicationData._() : super(); ApplicationData._() : super();
@ -1738,10 +1862,12 @@ class ApplicationData extends $pb.GeneratedMessage {
17 : ApplicationData_ApplicationData.redeemadditionalcode, 17 : ApplicationData_ApplicationData.redeemadditionalcode,
18 : ApplicationData_ApplicationData.removeadditionaluser, 18 : ApplicationData_ApplicationData.removeadditionaluser,
19 : ApplicationData_ApplicationData.updateplanoptions, 19 : ApplicationData_ApplicationData.updateplanoptions,
20 : ApplicationData_ApplicationData.downloaddone,
21 : ApplicationData_ApplicationData.uploaddone,
0 : ApplicationData_ApplicationData.notSet 0 : ApplicationData_ApplicationData.notSet
}; };
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ApplicationData', package: const $pb.PackageName(_omitMessageNames ? '' : 'client_to_server'), createEmptyInstance: create) static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ApplicationData', package: const $pb.PackageName(_omitMessageNames ? '' : 'client_to_server'), createEmptyInstance: create)
..oo(0, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]) ..oo(0, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21])
..aOM<ApplicationData_TextMessage>(1, _omitFieldNames ? '' : 'textmessage', subBuilder: ApplicationData_TextMessage.create) ..aOM<ApplicationData_TextMessage>(1, _omitFieldNames ? '' : 'textmessage', subBuilder: ApplicationData_TextMessage.create)
..aOM<ApplicationData_GetUserByUsername>(2, _omitFieldNames ? '' : 'getuserbyusername', subBuilder: ApplicationData_GetUserByUsername.create) ..aOM<ApplicationData_GetUserByUsername>(2, _omitFieldNames ? '' : 'getuserbyusername', subBuilder: ApplicationData_GetUserByUsername.create)
..aOM<ApplicationData_GetPrekeysByUserId>(3, _omitFieldNames ? '' : 'getprekeysbyuserid', subBuilder: ApplicationData_GetPrekeysByUserId.create) ..aOM<ApplicationData_GetPrekeysByUserId>(3, _omitFieldNames ? '' : 'getprekeysbyuserid', subBuilder: ApplicationData_GetPrekeysByUserId.create)
@ -1761,6 +1887,8 @@ class ApplicationData extends $pb.GeneratedMessage {
..aOM<ApplicationData_RedeemAdditionalCode>(17, _omitFieldNames ? '' : 'redeemadditionalcode', subBuilder: ApplicationData_RedeemAdditionalCode.create) ..aOM<ApplicationData_RedeemAdditionalCode>(17, _omitFieldNames ? '' : 'redeemadditionalcode', subBuilder: ApplicationData_RedeemAdditionalCode.create)
..aOM<ApplicationData_RemoveAdditionalUser>(18, _omitFieldNames ? '' : 'removeadditionaluser', subBuilder: ApplicationData_RemoveAdditionalUser.create) ..aOM<ApplicationData_RemoveAdditionalUser>(18, _omitFieldNames ? '' : 'removeadditionaluser', subBuilder: ApplicationData_RemoveAdditionalUser.create)
..aOM<ApplicationData_UpdatePlanOptions>(19, _omitFieldNames ? '' : 'updateplanoptions', subBuilder: ApplicationData_UpdatePlanOptions.create) ..aOM<ApplicationData_UpdatePlanOptions>(19, _omitFieldNames ? '' : 'updateplanoptions', subBuilder: ApplicationData_UpdatePlanOptions.create)
..aOM<ApplicationData_DownloadDone>(20, _omitFieldNames ? '' : 'downloaddone', subBuilder: ApplicationData_DownloadDone.create)
..aOM<ApplicationData_UploadDone>(21, _omitFieldNames ? '' : 'uploaddone', subBuilder: ApplicationData_UploadDone.create)
..hasRequiredFields = false ..hasRequiredFields = false
; ;
@ -1996,6 +2124,28 @@ class ApplicationData extends $pb.GeneratedMessage {
void clearUpdateplanoptions() => clearField(19); void clearUpdateplanoptions() => clearField(19);
@$pb.TagNumber(19) @$pb.TagNumber(19)
ApplicationData_UpdatePlanOptions ensureUpdateplanoptions() => $_ensure(18); ApplicationData_UpdatePlanOptions ensureUpdateplanoptions() => $_ensure(18);
@$pb.TagNumber(20)
ApplicationData_DownloadDone get downloaddone => $_getN(19);
@$pb.TagNumber(20)
set downloaddone(ApplicationData_DownloadDone v) { setField(20, v); }
@$pb.TagNumber(20)
$core.bool hasDownloaddone() => $_has(19);
@$pb.TagNumber(20)
void clearDownloaddone() => clearField(20);
@$pb.TagNumber(20)
ApplicationData_DownloadDone ensureDownloaddone() => $_ensure(19);
@$pb.TagNumber(21)
ApplicationData_UploadDone get uploaddone => $_getN(20);
@$pb.TagNumber(21)
set uploaddone(ApplicationData_UploadDone v) { setField(21, v); }
@$pb.TagNumber(21)
$core.bool hasUploaddone() => $_has(20);
@$pb.TagNumber(21)
void clearUploaddone() => clearField(21);
@$pb.TagNumber(21)
ApplicationData_UploadDone ensureUploaddone() => $_ensure(20);
} }
class Response_PreKey extends $pb.GeneratedMessage { class Response_PreKey extends $pb.GeneratedMessage {

View file

@ -155,8 +155,10 @@ const ApplicationData$json = {
{'1': 'redeemadditionalcode', '3': 17, '4': 1, '5': 11, '6': '.client_to_server.ApplicationData.RedeemAdditionalCode', '9': 0, '10': 'redeemadditionalcode'}, {'1': 'redeemadditionalcode', '3': 17, '4': 1, '5': 11, '6': '.client_to_server.ApplicationData.RedeemAdditionalCode', '9': 0, '10': 'redeemadditionalcode'},
{'1': 'removeadditionaluser', '3': 18, '4': 1, '5': 11, '6': '.client_to_server.ApplicationData.RemoveAdditionalUser', '9': 0, '10': 'removeadditionaluser'}, {'1': 'removeadditionaluser', '3': 18, '4': 1, '5': 11, '6': '.client_to_server.ApplicationData.RemoveAdditionalUser', '9': 0, '10': 'removeadditionaluser'},
{'1': 'updateplanoptions', '3': 19, '4': 1, '5': 11, '6': '.client_to_server.ApplicationData.UpdatePlanOptions', '9': 0, '10': 'updateplanoptions'}, {'1': 'updateplanoptions', '3': 19, '4': 1, '5': 11, '6': '.client_to_server.ApplicationData.UpdatePlanOptions', '9': 0, '10': 'updateplanoptions'},
{'1': 'downloaddone', '3': 20, '4': 1, '5': 11, '6': '.client_to_server.ApplicationData.DownloadDone', '9': 0, '10': 'downloaddone'},
{'1': 'uploaddone', '3': 21, '4': 1, '5': 11, '6': '.client_to_server.ApplicationData.UploadDone', '9': 0, '10': 'uploaddone'},
], ],
'3': [ApplicationData_TextMessage$json, ApplicationData_GetUserByUsername$json, ApplicationData_UpdateGoogleFcmToken$json, ApplicationData_GetUserById$json, ApplicationData_RedeemVoucher$json, ApplicationData_SwitchToPayedPlan$json, ApplicationData_UpdatePlanOptions$json, ApplicationData_CreateVoucher$json, ApplicationData_GetLocation$json, ApplicationData_GetVouchers$json, ApplicationData_GetAvailablePlans$json, ApplicationData_GetAddAccountsInvites$json, ApplicationData_GetCurrentPlanInfos$json, ApplicationData_RedeemAdditionalCode$json, ApplicationData_RemoveAdditionalUser$json, ApplicationData_GetPrekeysByUserId$json, ApplicationData_GetUploadToken$json, ApplicationData_UploadData$json, ApplicationData_DownloadData$json], '3': [ApplicationData_TextMessage$json, ApplicationData_GetUserByUsername$json, ApplicationData_UpdateGoogleFcmToken$json, ApplicationData_GetUserById$json, ApplicationData_RedeemVoucher$json, ApplicationData_SwitchToPayedPlan$json, ApplicationData_UpdatePlanOptions$json, ApplicationData_CreateVoucher$json, ApplicationData_GetLocation$json, ApplicationData_GetVouchers$json, ApplicationData_GetAvailablePlans$json, ApplicationData_GetAddAccountsInvites$json, ApplicationData_GetCurrentPlanInfos$json, ApplicationData_RedeemAdditionalCode$json, ApplicationData_RemoveAdditionalUser$json, ApplicationData_GetPrekeysByUserId$json, ApplicationData_GetUploadToken$json, ApplicationData_UploadData$json, ApplicationData_UploadDone$json, ApplicationData_DownloadData$json, ApplicationData_DownloadDone$json],
'8': [ '8': [
{'1': 'ApplicationData'}, {'1': 'ApplicationData'},
], ],
@ -304,6 +306,15 @@ const ApplicationData_UploadData$json = {
], ],
}; };
@$core.Deprecated('Use applicationDataDescriptor instead')
const ApplicationData_UploadDone$json = {
'1': 'UploadDone',
'2': [
{'1': 'upload_token', '3': 1, '4': 1, '5': 12, '10': 'uploadToken'},
{'1': 'recipients_count', '3': 2, '4': 1, '5': 13, '10': 'recipientsCount'},
],
};
@$core.Deprecated('Use applicationDataDescriptor instead') @$core.Deprecated('Use applicationDataDescriptor instead')
const ApplicationData_DownloadData$json = { const ApplicationData_DownloadData$json = {
'1': 'DownloadData', '1': 'DownloadData',
@ -313,6 +324,14 @@ const ApplicationData_DownloadData$json = {
], ],
}; };
@$core.Deprecated('Use applicationDataDescriptor instead')
const ApplicationData_DownloadDone$json = {
'1': 'DownloadDone',
'2': [
{'1': 'download_token', '3': 1, '4': 1, '5': 12, '10': 'downloadToken'},
],
};
/// Descriptor for `ApplicationData`. Decode as a `google.protobuf.DescriptorProto`. /// Descriptor for `ApplicationData`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List applicationDataDescriptor = $convert.base64Decode( final $typed_data.Uint8List applicationDataDescriptor = $convert.base64Decode(
'Cg9BcHBsaWNhdGlvbkRhdGESUQoLdGV4dG1lc3NhZ2UYASABKAsyLS5jbGllbnRfdG9fc2Vydm' 'Cg9BcHBsaWNhdGlvbkRhdGESUQoLdGV4dG1lc3NhZ2UYASABKAsyLS5jbGllbnRfdG9fc2Vydm'
@ -348,26 +367,32 @@ final $typed_data.Uint8List applicationDataDescriptor = $convert.base64Decode(
'Y2xpZW50X3RvX3NlcnZlci5BcHBsaWNhdGlvbkRhdGEuUmVtb3ZlQWRkaXRpb25hbFVzZXJIAF' 'Y2xpZW50X3RvX3NlcnZlci5BcHBsaWNhdGlvbkRhdGEuUmVtb3ZlQWRkaXRpb25hbFVzZXJIAF'
'IUcmVtb3ZlYWRkaXRpb25hbHVzZXISYwoRdXBkYXRlcGxhbm9wdGlvbnMYEyABKAsyMy5jbGll' 'IUcmVtb3ZlYWRkaXRpb25hbHVzZXISYwoRdXBkYXRlcGxhbm9wdGlvbnMYEyABKAsyMy5jbGll'
'bnRfdG9fc2VydmVyLkFwcGxpY2F0aW9uRGF0YS5VcGRhdGVQbGFuT3B0aW9uc0gAUhF1cGRhdG' 'bnRfdG9fc2VydmVyLkFwcGxpY2F0aW9uRGF0YS5VcGRhdGVQbGFuT3B0aW9uc0gAUhF1cGRhdG'
'VwbGFub3B0aW9ucxpqCgtUZXh0TWVzc2FnZRIXCgd1c2VyX2lkGAEgASgDUgZ1c2VySWQSEgoE' 'VwbGFub3B0aW9ucxJUCgxkb3dubG9hZGRvbmUYFCABKAsyLi5jbGllbnRfdG9fc2VydmVyLkFw'
'Ym9keRgDIAEoDFIEYm9keRIgCglwdXNoX2RhdGEYBCABKAxIAFIIcHVzaERhdGGIAQFCDAoKX3' 'cGxpY2F0aW9uRGF0YS5Eb3dubG9hZERvbmVIAFIMZG93bmxvYWRkb25lEk4KCnVwbG9hZGRvbm'
'B1c2hfZGF0YRovChFHZXRVc2VyQnlVc2VybmFtZRIaCgh1c2VybmFtZRgBIAEoCVIIdXNlcm5h' 'UYFSABKAsyLC5jbGllbnRfdG9fc2VydmVyLkFwcGxpY2F0aW9uRGF0YS5VcGxvYWREb25lSABS'
'bWUaNQoUVXBkYXRlR29vZ2xlRmNtVG9rZW4SHQoKZ29vZ2xlX2ZjbRgBIAEoCVIJZ29vZ2xlRm' 'CnVwbG9hZGRvbmUaagoLVGV4dE1lc3NhZ2USFwoHdXNlcl9pZBgBIAEoA1IGdXNlcklkEhIKBG'
'NtGiYKC0dldFVzZXJCeUlkEhcKB3VzZXJfaWQYASABKANSBnVzZXJJZBopCg1SZWRlZW1Wb3Vj' 'JvZHkYAyABKAxSBGJvZHkSIAoJcHVzaF9kYXRhGAQgASgMSABSCHB1c2hEYXRhiAEBQgwKCl9w'
'aGVyEhgKB3ZvdWNoZXIYASABKAlSB3ZvdWNoZXIacAoRU3dpdGNoVG9QYXllZFBsYW4SFwoHcG' 'dXNoX2RhdGEaLwoRR2V0VXNlckJ5VXNlcm5hbWUSGgoIdXNlcm5hbWUYASABKAlSCHVzZXJuYW'
'xhbl9pZBgBIAEoCVIGcGxhbklkEh8KC3BheV9tb250aGx5GAIgASgIUgpwYXlNb250aGx5EiEK' '1lGjUKFFVwZGF0ZUdvb2dsZUZjbVRva2VuEh0KCmdvb2dsZV9mY20YASABKAlSCWdvb2dsZUZj'
'DGF1dG9fcmVuZXdhbBgDIAEoCFILYXV0b1JlbmV3YWwaNgoRVXBkYXRlUGxhbk9wdGlvbnMSIQ' 'bRomCgtHZXRVc2VyQnlJZBIXCgd1c2VyX2lkGAEgASgDUgZ1c2VySWQaKQoNUmVkZWVtVm91Y2'
'oMYXV0b19yZW5ld2FsGAEgASgIUgthdXRvUmVuZXdhbBowCg1DcmVhdGVWb3VjaGVyEh8KC3Zh' 'hlchIYCgd2b3VjaGVyGAEgASgJUgd2b3VjaGVyGnAKEVN3aXRjaFRvUGF5ZWRQbGFuEhcKB3Bs'
'bHVlX2NlbnRzGAEgASgNUgp2YWx1ZUNlbnRzGg0KC0dldExvY2F0aW9uGg0KC0dldFZvdWNoZX' 'YW5faWQYASABKAlSBnBsYW5JZBIfCgtwYXlfbW9udGhseRgCIAEoCFIKcGF5TW9udGhseRIhCg'
'JzGhMKEUdldEF2YWlsYWJsZVBsYW5zGhcKFUdldEFkZEFjY291bnRzSW52aXRlcxoVChNHZXRD' 'xhdXRvX3JlbmV3YWwYAyABKAhSC2F1dG9SZW5ld2FsGjYKEVVwZGF0ZVBsYW5PcHRpb25zEiEK'
'dXJyZW50UGxhbkluZm9zGjcKFFJlZGVlbUFkZGl0aW9uYWxDb2RlEh8KC2ludml0ZV9jb2RlGA' 'DGF1dG9fcmVuZXdhbBgBIAEoCFILYXV0b1JlbmV3YWwaMAoNQ3JlYXRlVm91Y2hlchIfCgt2YW'
'IgASgJUgppbnZpdGVDb2RlGi8KFFJlbW92ZUFkZGl0aW9uYWxVc2VyEhcKB3VzZXJfaWQYASAB' 'x1ZV9jZW50cxgBIAEoDVIKdmFsdWVDZW50cxoNCgtHZXRMb2NhdGlvbhoNCgtHZXRWb3VjaGVy'
'KANSBnVzZXJJZBotChJHZXRQcmVrZXlzQnlVc2VySWQSFwoHdXNlcl9pZBgBIAEoA1IGdXNlck' 'cxoTChFHZXRBdmFpbGFibGVQbGFucxoXChVHZXRBZGRBY2NvdW50c0ludml0ZXMaFQoTR2V0Q3'
'lkGjsKDkdldFVwbG9hZFRva2VuEikKEHJlY2lwaWVudHNfY291bnQYASABKA1SD3JlY2lwaWVu' 'VycmVudFBsYW5JbmZvcxo3ChRSZWRlZW1BZGRpdGlvbmFsQ29kZRIfCgtpbnZpdGVfY29kZRgC'
'dHNDb3VudBqJAQoKVXBsb2FkRGF0YRIhCgx1cGxvYWRfdG9rZW4YASABKAxSC3VwbG9hZFRva2' 'IAEoCVIKaW52aXRlQ29kZRovChRSZW1vdmVBZGRpdGlvbmFsVXNlchIXCgd1c2VyX2lkGAEgAS'
'VuEhYKBm9mZnNldBgCIAEoDVIGb2Zmc2V0EhIKBGRhdGEYAyABKAxSBGRhdGESHwoIY2hlY2tz' 'gDUgZ1c2VySWQaLQoSR2V0UHJla2V5c0J5VXNlcklkEhcKB3VzZXJfaWQYASABKANSBnVzZXJJ'
'dW0YBCABKAxIAFIIY2hlY2tzdW2IAQFCCwoJX2NoZWNrc3VtGk0KDERvd25sb2FkRGF0YRIlCg' 'ZBo7Cg5HZXRVcGxvYWRUb2tlbhIpChByZWNpcGllbnRzX2NvdW50GAEgASgNUg9yZWNpcGllbn'
'5kb3dubG9hZF90b2tlbhgBIAEoDFINZG93bmxvYWRUb2tlbhIWCgZvZmZzZXQYAiABKA1SBm9m' 'RzQ291bnQaiQEKClVwbG9hZERhdGESIQoMdXBsb2FkX3Rva2VuGAEgASgMUgt1cGxvYWRUb2tl'
'ZnNldEIRCg9BcHBsaWNhdGlvbkRhdGE='); 'bhIWCgZvZmZzZXQYAiABKA1SBm9mZnNldBISCgRkYXRhGAMgASgMUgRkYXRhEh8KCGNoZWNrc3'
'VtGAQgASgMSABSCGNoZWNrc3VtiAEBQgsKCV9jaGVja3N1bRpaCgpVcGxvYWREb25lEiEKDHVw'
'bG9hZF90b2tlbhgBIAEoDFILdXBsb2FkVG9rZW4SKQoQcmVjaXBpZW50c19jb3VudBgCIAEoDV'
'IPcmVjaXBpZW50c0NvdW50Gk0KDERvd25sb2FkRGF0YRIlCg5kb3dubG9hZF90b2tlbhgBIAEo'
'DFINZG93bmxvYWRUb2tlbhIWCgZvZmZzZXQYAiABKA1SBm9mZnNldBo1CgxEb3dubG9hZERvbm'
'USJQoOZG93bmxvYWRfdG9rZW4YASABKAxSDWRvd25sb2FkVG9rZW5CEQoPQXBwbGljYXRpb25E'
'YXRh');
@$core.Deprecated('Use responseDescriptor instead') @$core.Deprecated('Use responseDescriptor instead')
const Response$json = { const Response$json = {

View file

@ -1524,6 +1524,50 @@ class Response_UploadToken extends $pb.GeneratedMessage {
$core.List<$core.List<$core.int>> get downloadTokens => $_getList(1); $core.List<$core.List<$core.int>> get downloadTokens => $_getList(1);
} }
class Response_DownloadTokens extends $pb.GeneratedMessage {
factory Response_DownloadTokens({
$core.Iterable<$core.List<$core.int>>? downloadTokens,
}) {
final $result = create();
if (downloadTokens != null) {
$result.downloadTokens.addAll(downloadTokens);
}
return $result;
}
Response_DownloadTokens._() : super();
factory Response_DownloadTokens.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory Response_DownloadTokens.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Response.DownloadTokens', package: const $pb.PackageName(_omitMessageNames ? '' : 'server_to_client'), createEmptyInstance: create)
..p<$core.List<$core.int>>(1, _omitFieldNames ? '' : 'downloadTokens', $pb.PbFieldType.PY)
..hasRequiredFields = false
;
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
'Will be removed in next major version')
Response_DownloadTokens clone() => Response_DownloadTokens()..mergeFromMessage(this);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version')
Response_DownloadTokens copyWith(void Function(Response_DownloadTokens) updates) => super.copyWith((message) => updates(message as Response_DownloadTokens)) as Response_DownloadTokens;
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static Response_DownloadTokens create() => Response_DownloadTokens._();
Response_DownloadTokens createEmptyInstance() => create();
static $pb.PbList<Response_DownloadTokens> createRepeated() => $pb.PbList<Response_DownloadTokens>();
@$core.pragma('dart2js:noInline')
static Response_DownloadTokens getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Response_DownloadTokens>(create);
static Response_DownloadTokens? _defaultInstance;
@$pb.TagNumber(1)
$core.List<$core.List<$core.int>> get downloadTokens => $_getList(0);
}
enum Response_Ok_Ok { enum Response_Ok_Ok {
none, none,
userid, userid,
@ -1537,6 +1581,7 @@ enum Response_Ok_Ok {
planballance, planballance,
vouchers, vouchers,
addaccountsinvites, addaccountsinvites,
downloadtokens,
notSet notSet
} }
@ -1554,6 +1599,7 @@ class Response_Ok extends $pb.GeneratedMessage {
Response_PlanBallance? planballance, Response_PlanBallance? planballance,
Response_Vouchers? vouchers, Response_Vouchers? vouchers,
Response_AddAccountsInvites? addaccountsinvites, Response_AddAccountsInvites? addaccountsinvites,
Response_DownloadTokens? downloadtokens,
}) { }) {
final $result = create(); final $result = create();
if (none != null) { if (none != null) {
@ -1592,6 +1638,9 @@ class Response_Ok extends $pb.GeneratedMessage {
if (addaccountsinvites != null) { if (addaccountsinvites != null) {
$result.addaccountsinvites = addaccountsinvites; $result.addaccountsinvites = addaccountsinvites;
} }
if (downloadtokens != null) {
$result.downloadtokens = downloadtokens;
}
return $result; return $result;
} }
Response_Ok._() : super(); Response_Ok._() : super();
@ -1611,10 +1660,11 @@ class Response_Ok extends $pb.GeneratedMessage {
10 : Response_Ok_Ok.planballance, 10 : Response_Ok_Ok.planballance,
11 : Response_Ok_Ok.vouchers, 11 : Response_Ok_Ok.vouchers,
12 : Response_Ok_Ok.addaccountsinvites, 12 : Response_Ok_Ok.addaccountsinvites,
13 : Response_Ok_Ok.downloadtokens,
0 : Response_Ok_Ok.notSet 0 : Response_Ok_Ok.notSet
}; };
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Response.Ok', package: const $pb.PackageName(_omitMessageNames ? '' : 'server_to_client'), createEmptyInstance: create) static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Response.Ok', package: const $pb.PackageName(_omitMessageNames ? '' : 'server_to_client'), createEmptyInstance: create)
..oo(0, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) ..oo(0, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13])
..aOB(1, _omitFieldNames ? '' : 'None', protoName: 'None') ..aOB(1, _omitFieldNames ? '' : 'None', protoName: 'None')
..aInt64(2, _omitFieldNames ? '' : 'userid') ..aInt64(2, _omitFieldNames ? '' : 'userid')
..a<$core.List<$core.int>>(3, _omitFieldNames ? '' : 'authchallenge', $pb.PbFieldType.OY) ..a<$core.List<$core.int>>(3, _omitFieldNames ? '' : 'authchallenge', $pb.PbFieldType.OY)
@ -1627,6 +1677,7 @@ class Response_Ok extends $pb.GeneratedMessage {
..aOM<Response_PlanBallance>(10, _omitFieldNames ? '' : 'planballance', subBuilder: Response_PlanBallance.create) ..aOM<Response_PlanBallance>(10, _omitFieldNames ? '' : 'planballance', subBuilder: Response_PlanBallance.create)
..aOM<Response_Vouchers>(11, _omitFieldNames ? '' : 'vouchers', subBuilder: Response_Vouchers.create) ..aOM<Response_Vouchers>(11, _omitFieldNames ? '' : 'vouchers', subBuilder: Response_Vouchers.create)
..aOM<Response_AddAccountsInvites>(12, _omitFieldNames ? '' : 'addaccountsinvites', subBuilder: Response_AddAccountsInvites.create) ..aOM<Response_AddAccountsInvites>(12, _omitFieldNames ? '' : 'addaccountsinvites', subBuilder: Response_AddAccountsInvites.create)
..aOM<Response_DownloadTokens>(13, _omitFieldNames ? '' : 'downloadtokens', subBuilder: Response_DownloadTokens.create)
..hasRequiredFields = false ..hasRequiredFields = false
; ;
@ -1777,6 +1828,17 @@ class Response_Ok extends $pb.GeneratedMessage {
void clearAddaccountsinvites() => clearField(12); void clearAddaccountsinvites() => clearField(12);
@$pb.TagNumber(12) @$pb.TagNumber(12)
Response_AddAccountsInvites ensureAddaccountsinvites() => $_ensure(11); Response_AddAccountsInvites ensureAddaccountsinvites() => $_ensure(11);
@$pb.TagNumber(13)
Response_DownloadTokens get downloadtokens => $_getN(12);
@$pb.TagNumber(13)
set downloadtokens(Response_DownloadTokens v) { setField(13, v); }
@$pb.TagNumber(13)
$core.bool hasDownloadtokens() => $_has(12);
@$pb.TagNumber(13)
void clearDownloadtokens() => clearField(13);
@$pb.TagNumber(13)
Response_DownloadTokens ensureDownloadtokens() => $_ensure(12);
} }
enum Response_Response { enum Response_Response {

View file

@ -92,7 +92,7 @@ const Response$json = {
{'1': 'ok', '3': 1, '4': 1, '5': 11, '6': '.server_to_client.Response.Ok', '9': 0, '10': 'ok'}, {'1': 'ok', '3': 1, '4': 1, '5': 11, '6': '.server_to_client.Response.Ok', '9': 0, '10': 'ok'},
{'1': 'error', '3': 2, '4': 1, '5': 14, '6': '.error.ErrorCode', '9': 0, '10': 'error'}, {'1': 'error', '3': 2, '4': 1, '5': 14, '6': '.error.ErrorCode', '9': 0, '10': 'error'},
], ],
'3': [Response_Authenticated$json, Response_Plan$json, Response_Plans$json, Response_AddAccountsInvite$json, Response_AddAccountsInvites$json, Response_Transaction$json, Response_AdditionalAccount$json, Response_Voucher$json, Response_Vouchers$json, Response_PlanBallance$json, Response_Location$json, Response_PreKey$json, Response_UserData$json, Response_UploadToken$json, Response_Ok$json], '3': [Response_Authenticated$json, Response_Plan$json, Response_Plans$json, Response_AddAccountsInvite$json, Response_AddAccountsInvites$json, Response_Transaction$json, Response_AdditionalAccount$json, Response_Voucher$json, Response_Vouchers$json, Response_PlanBallance$json, Response_Location$json, Response_PreKey$json, Response_UserData$json, Response_UploadToken$json, Response_DownloadTokens$json, Response_Ok$json],
'4': [Response_TransactionTypes$json], '4': [Response_TransactionTypes$json],
'8': [ '8': [
{'1': 'Response'}, {'1': 'Response'},
@ -258,6 +258,14 @@ const Response_UploadToken$json = {
], ],
}; };
@$core.Deprecated('Use responseDescriptor instead')
const Response_DownloadTokens$json = {
'1': 'DownloadTokens',
'2': [
{'1': 'download_tokens', '3': 1, '4': 3, '5': 12, '10': 'downloadTokens'},
],
};
@$core.Deprecated('Use responseDescriptor instead') @$core.Deprecated('Use responseDescriptor instead')
const Response_Ok$json = { const Response_Ok$json = {
'1': 'Ok', '1': 'Ok',
@ -274,6 +282,7 @@ const Response_Ok$json = {
{'1': 'planballance', '3': 10, '4': 1, '5': 11, '6': '.server_to_client.Response.PlanBallance', '9': 0, '10': 'planballance'}, {'1': 'planballance', '3': 10, '4': 1, '5': 11, '6': '.server_to_client.Response.PlanBallance', '9': 0, '10': 'planballance'},
{'1': 'vouchers', '3': 11, '4': 1, '5': 11, '6': '.server_to_client.Response.Vouchers', '9': 0, '10': 'vouchers'}, {'1': 'vouchers', '3': 11, '4': 1, '5': 11, '6': '.server_to_client.Response.Vouchers', '9': 0, '10': 'vouchers'},
{'1': 'addaccountsinvites', '3': 12, '4': 1, '5': 11, '6': '.server_to_client.Response.AddAccountsInvites', '9': 0, '10': 'addaccountsinvites'}, {'1': 'addaccountsinvites', '3': 12, '4': 1, '5': 11, '6': '.server_to_client.Response.AddAccountsInvites', '9': 0, '10': 'addaccountsinvites'},
{'1': 'downloadtokens', '3': 13, '4': 1, '5': 11, '6': '.server_to_client.Response.DownloadTokens', '9': 0, '10': 'downloadtokens'},
], ],
'8': [ '8': [
{'1': 'Ok'}, {'1': 'Ok'},
@ -347,21 +356,23 @@ final $typed_data.Uint8List responseDescriptor = $convert.base64Decode(
'9wdWJsaWNfaWRlbnRpdHlfa2V5QhAKDl9zaWduZWRfcHJla2V5QhoKGF9zaWduZWRfcHJla2V5' '9wdWJsaWNfaWRlbnRpdHlfa2V5QhAKDl9zaWduZWRfcHJla2V5QhoKGF9zaWduZWRfcHJla2V5'
'X3NpZ25hdHVyZUITChFfc2lnbmVkX3ByZWtleV9pZBpZCgtVcGxvYWRUb2tlbhIhCgx1cGxvYW' 'X3NpZ25hdHVyZUITChFfc2lnbmVkX3ByZWtleV9pZBpZCgtVcGxvYWRUb2tlbhIhCgx1cGxvYW'
'RfdG9rZW4YASABKAxSC3VwbG9hZFRva2VuEicKD2Rvd25sb2FkX3Rva2VucxgCIAMoDFIOZG93' 'RfdG9rZW4YASABKAxSC3VwbG9hZFRva2VuEicKD2Rvd25sb2FkX3Rva2VucxgCIAMoDFIOZG93'
'bmxvYWRUb2tlbnMa0wUKAk9rEhQKBE5vbmUYASABKAhIAFIETm9uZRIYCgZ1c2VyaWQYAiABKA' 'bmxvYWRUb2tlbnMaOQoORG93bmxvYWRUb2tlbnMSJwoPZG93bmxvYWRfdG9rZW5zGAEgAygMUg'
'NIAFIGdXNlcmlkEiYKDWF1dGhjaGFsbGVuZ2UYAyABKAxIAFINYXV0aGNoYWxsZW5nZRJKCgt1' '5kb3dubG9hZFRva2VucxqoBgoCT2sSFAoETm9uZRgBIAEoCEgAUgROb25lEhgKBnVzZXJpZBgC'
'cGxvYWR0b2tlbhgEIAEoCzImLnNlcnZlcl90b19jbGllbnQuUmVzcG9uc2UuVXBsb2FkVG9rZW' 'IAEoA0gAUgZ1c2VyaWQSJgoNYXV0aGNoYWxsZW5nZRgDIAEoDEgAUg1hdXRoY2hhbGxlbmdlEk'
'5IAFILdXBsb2FkdG9rZW4SQQoIdXNlcmRhdGEYBSABKAsyIy5zZXJ2ZXJfdG9fY2xpZW50LlJl' 'oKC3VwbG9hZHRva2VuGAQgASgLMiYuc2VydmVyX3RvX2NsaWVudC5SZXNwb25zZS5VcGxvYWRU'
'c3BvbnNlLlVzZXJEYXRhSABSCHVzZXJkYXRhEh4KCWF1dGh0b2tlbhgGIAEoDEgAUglhdXRodG' 'b2tlbkgAUgt1cGxvYWR0b2tlbhJBCgh1c2VyZGF0YRgFIAEoCzIjLnNlcnZlcl90b19jbGllbn'
'9rZW4SQQoIbG9jYXRpb24YByABKAsyIy5zZXJ2ZXJfdG9fY2xpZW50LlJlc3BvbnNlLkxvY2F0' 'QuUmVzcG9uc2UuVXNlckRhdGFIAFIIdXNlcmRhdGESHgoJYXV0aHRva2VuGAYgASgMSABSCWF1'
'aW9uSABSCGxvY2F0aW9uElAKDWF1dGhlbnRpY2F0ZWQYCCABKAsyKC5zZXJ2ZXJfdG9fY2xpZW' 'dGh0b2tlbhJBCghsb2NhdGlvbhgHIAEoCzIjLnNlcnZlcl90b19jbGllbnQuUmVzcG9uc2UuTG'
'50LlJlc3BvbnNlLkF1dGhlbnRpY2F0ZWRIAFINYXV0aGVudGljYXRlZBI4CgVwbGFucxgJIAEo' '9jYXRpb25IAFIIbG9jYXRpb24SUAoNYXV0aGVudGljYXRlZBgIIAEoCzIoLnNlcnZlcl90b19j'
'CzIgLnNlcnZlcl90b19jbGllbnQuUmVzcG9uc2UuUGxhbnNIAFIFcGxhbnMSTQoMcGxhbmJhbG' 'bGllbnQuUmVzcG9uc2UuQXV0aGVudGljYXRlZEgAUg1hdXRoZW50aWNhdGVkEjgKBXBsYW5zGA'
'xhbmNlGAogASgLMicuc2VydmVyX3RvX2NsaWVudC5SZXNwb25zZS5QbGFuQmFsbGFuY2VIAFIM' 'kgASgLMiAuc2VydmVyX3RvX2NsaWVudC5SZXNwb25zZS5QbGFuc0gAUgVwbGFucxJNCgxwbGFu'
'cGxhbmJhbGxhbmNlEkEKCHZvdWNoZXJzGAsgASgLMiMuc2VydmVyX3RvX2NsaWVudC5SZXNwb2' 'YmFsbGFuY2UYCiABKAsyJy5zZXJ2ZXJfdG9fY2xpZW50LlJlc3BvbnNlLlBsYW5CYWxsYW5jZU'
'5zZS5Wb3VjaGVyc0gAUgh2b3VjaGVycxJfChJhZGRhY2NvdW50c2ludml0ZXMYDCABKAsyLS5z' 'gAUgxwbGFuYmFsbGFuY2USQQoIdm91Y2hlcnMYCyABKAsyIy5zZXJ2ZXJfdG9fY2xpZW50LlJl'
'ZXJ2ZXJfdG9fY2xpZW50LlJlc3BvbnNlLkFkZEFjY291bnRzSW52aXRlc0gAUhJhZGRhY2NvdW' 'c3BvbnNlLlZvdWNoZXJzSABSCHZvdWNoZXJzEl8KEmFkZGFjY291bnRzaW52aXRlcxgMIAEoCz'
'50c2ludml0ZXNCBAoCT2silgEKEFRyYW5zYWN0aW9uVHlwZXMSCgoGUmVmdW5kEAASEwoPVm91' 'ItLnNlcnZlcl90b19jbGllbnQuUmVzcG9uc2UuQWRkQWNjb3VudHNJbnZpdGVzSABSEmFkZGFj'
'Y2hlclJlZGVlbWVkEAESEgoOVm91Y2hlckNyZWF0ZWQQAhIICgRDYXNoEAMSDwoLUGxhblVwZ3' 'Y291bnRzaW52aXRlcxJTCg5kb3dubG9hZHRva2VucxgNIAEoCzIpLnNlcnZlcl90b19jbGllbn'
'JhZGUQBBILCgdVbmtub3duEAUSFAoQVGhhbmtzRm9yVGVzdGluZxAGEg8KC0F1dG9SZW5ld2Fs' 'QuUmVzcG9uc2UuRG93bmxvYWRUb2tlbnNIAFIOZG93bmxvYWR0b2tlbnNCBAoCT2silgEKEFRy'
'EAdCCgoIUmVzcG9uc2U='); 'YW5zYWN0aW9uVHlwZXMSCgoGUmVmdW5kEAASEwoPVm91Y2hlclJlZGVlbWVkEAESEgoOVm91Y2'
'hlckNyZWF0ZWQQAhIICgRDYXNoEAMSDwoLUGxhblVwZ3JhZGUQBBILCgdVbmtub3duEAUSFAoQ'
'VGhhbmtzRm9yVGVzdGluZxAGEg8KC0F1dG9SZW5ld2FsEAdCCgoIUmVzcG9uc2U=');

View file

@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'dart:io'; import 'dart:io';
import 'dart:typed_data'; import 'dart:typed_data';
@ -18,6 +19,7 @@ import 'package:twonly/src/model/json/message.dart';
import 'package:twonly/src/model/protobuf/api/error.pb.dart'; import 'package:twonly/src/model/protobuf/api/error.pb.dart';
import 'package:twonly/src/model/protobuf/api/server_to_client.pb.dart'; import 'package:twonly/src/model/protobuf/api/server_to_client.pb.dart';
import 'package:twonly/src/providers/api/api.dart'; import 'package:twonly/src/providers/api/api.dart';
import 'package:twonly/src/providers/api/api_utils.dart';
import 'package:twonly/src/providers/api/media_received.dart'; import 'package:twonly/src/providers/api/media_received.dart';
import 'package:twonly/src/services/notification_service.dart'; import 'package:twonly/src/services/notification_service.dart';
import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/misc.dart';
@ -49,48 +51,17 @@ Future<ErrorCode?> isAllowedToSend() async {
return null; return null;
} }
/// Process the image up to the point more user informations are required /// States:
/// Returns the media upload id /// when user recorded an video
Future<int> preSendMediaFile(Uint8List imageBytes, File? videoFilePath) async { /// 1. Compress video
return 0; /// when user clicked the send button (direct send) or share with
} /// 2. Encrypt media files
/// 3. Upload media files
/// click send button
/// 4. Finalize upload by websocket -> get download tokens
/// 5. Send all users the message
Future cancelSendMediaFile(int mediaUploadId) async {} /// Create a new entry in the database
Future finalizeSendMediaFile(int mediaUploadId, List<int> userIds,
bool isRealTwonly, bool isVideo, bool mirrorVideo, int maxShowTime) async {}
Future sendMediaFile(
List<int> userIds,
Uint8List imageBytes,
bool isRealTwonly,
int maxShowTime,
File? videoFilePath,
bool mirrorVideo,
) async {
MediaUploadMetadata metadata = MediaUploadMetadata();
metadata.contactIds = userIds;
metadata.isRealTwonly = isRealTwonly;
metadata.messageSendAt = DateTime.now();
metadata.isVideo = videoFilePath != null;
metadata.maxShowTime = maxShowTime;
metadata.mirrorVideo = mirrorVideo;
int? mediaUploadId = await twonlyDatabase.mediaUploadsDao.insertMediaUpload(
MediaUploadsCompanion(
metadata: Value(metadata),
),
);
if (mediaUploadId != null) {
if (videoFilePath != null) {
String basePath = await getMediaFilePath(mediaUploadId, "send");
await videoFilePath.rename("$basePath.orginal.mp4");
}
await writeMediaFile(mediaUploadId, "orginal.png", imageBytes);
await handleSingleMediaFile(mediaUploadId, imageBytes);
}
}
final lockingHandleMediaFile = Mutex(); final lockingHandleMediaFile = Mutex();
Future retryMediaUpload({int maxRetries = 3}) async { Future retryMediaUpload({int maxRetries = 3}) async {
@ -99,7 +70,22 @@ Future retryMediaUpload({int maxRetries = 3}) async {
await twonlyDatabase.mediaUploadsDao.getMediaUploadsForRetry(); await twonlyDatabase.mediaUploadsDao.getMediaUploadsForRetry();
if (mediaFiles.isEmpty) return; if (mediaFiles.isEmpty) return;
for (final mediaFile in mediaFiles) { for (final mediaFile in mediaFiles) {
await handleSingleMediaFile(mediaFile.mediaUploadId, null); if (mediaFile.messageIds == null || mediaFile.metadata == null) {
// the media upload was canceled,
if (mediaFile.uploadTokens != null) {
/// the file was already uploaded.
/// notify the server to remove the upload
}
Logger("media_send.dart").shout(
"upload can be removed, the finalized function was never called...");
continue;
}
if (mediaFile.state == UploadState.readyToUpload) {
await handleNextMediaUploadSteps(mediaFile.mediaUploadId);
} else {
await handlePreProcessingState(mediaFile);
}
} }
}); });
if (maxRetries == 0) return; if (maxRetries == 0) return;
@ -109,100 +95,157 @@ Future retryMediaUpload({int maxRetries = 3}) async {
}); });
} }
Future handleSingleMediaFile( Future<int?> initMediaUpload() async {
int mediaUploadId, Uint8List? tmpCurrentImageBytes) async { return await twonlyDatabase.mediaUploadsDao
MediaUpload? media = await twonlyDatabase.mediaUploadsDao .insertMediaUpload(MediaUploadsCompanion());
.getMediaUploadById(mediaUploadId) }
.getSingleOrNull();
if (media == null) return;
Future<bool> addVideoToUpload(int mediaUploadId, File videoFilePath) async {
String basePath = await getMediaFilePath(mediaUploadId, "send");
await videoFilePath.rename("$basePath.original.mp4");
return await compressVideoIfExists(mediaUploadId);
}
Future<Uint8List> addOrModifyImageToUpload(
int mediaUploadId, Uint8List imageBytes) async {
Uint8List imageBytesCompressed;
try { try {
switch (media.state) { imageBytesCompressed = await FlutterImageCompress.compressWithList(
case UploadState.pending: format: CompressFormat.png,
await handleAddToMessageDb(media); imageBytes,
break; quality: 90,
case UploadState.addedToMessagesDb: );
tmpCurrentImageBytes =
await handleCompressionState(media, tmpCurrentImageBytes); if (imageBytesCompressed.length >= 2 * 1000 * 1000) {
break; // if the media file is over 2MB compress it with 60%
case UploadState.isCompressed: imageBytesCompressed = await FlutterImageCompress.compressWithList(
tmpCurrentImageBytes = format: CompressFormat.png,
await handleEncryptionState(media, tmpCurrentImageBytes); imageBytes,
break; quality: 60,
case UploadState.isEncrypted:
if (!await handleGetUploadToken(media)) {
return; // recoverable error. try again when connected again to the server...
}
break;
case UploadState.hasUploadToken:
if (!await handleUploadHttp(media, tmpCurrentImageBytes)) {
return;
}
// if (!await handleUpload(media, tmpCurrentImageBytes)) {
// return; // recoverable error. try again when connected again to the server...
// }
break;
case UploadState.isUploaded:
if (!await handleNotifyReceiver(media)) {
return; // recoverable error. try again when connected again to the server...
}
try {
// delete non compressed media files
await deleteMediaFile(media.mediaUploadId, "orginal.png");
await deleteMediaFile(media.mediaUploadId, "orginal.mp4");
await deleteMediaFile(media.mediaUploadId, "encrypted");
} catch (e) {
Logger("media_send.dart").shout("$e");
}
break;
case UploadState.receiverNotified:
return;
}
} catch (e) {
// if the messageIds are already there notify the user about this error...
if (media.messageIds != null) {
for (int messageId in media.messageIds!) {
await twonlyDatabase.messagesDao.updateMessageByMessageId(
messageId,
MessagesCompanion(
errorWhileSending: Value(true),
),
); );
} }
} await writeMediaFile(mediaUploadId, "png", imageBytesCompressed);
await twonlyDatabase.mediaUploadsDao.deleteMediaUpload(mediaUploadId); } catch (e) {
Logger("media_send.dart") Logger("media_send.dart").shout("$e");
.shout("Non recoverable error while sending media file: $e"); // as a fall back use the original image
return; await writeMediaFile(mediaUploadId, "png", imageBytes);
} imageBytesCompressed = imageBytes;
// this will be called until there is an recoverable error OR
// the upload is ready
await handleSingleMediaFile(mediaUploadId, tmpCurrentImageBytes);
} }
Future handleAddToMessageDb(MediaUpload media) async { /// in case the media file was already encrypted of even uploaded
/// remove the data so it will be done again.
/// TODO: when the uploadTokens are already set notify the server...
await twonlyDatabase.mediaUploadsDao.updateMediaUpload(
mediaUploadId,
MediaUploadsCompanion(
encryptionData: Value(null),
uploadTokens: Value(null),
),
);
return imageBytesCompressed;
}
Future handlePreProcessingState(MediaUpload media) async {
try {
final imageHandler = readMediaFile(media.mediaUploadId, "png");
final videoHandler = compressVideoIfExists(media.mediaUploadId);
await encryptAndPreUploadMediaFiles(
media.mediaUploadId,
imageHandler,
videoHandler,
);
} catch (e) {
await handleUploadError(media);
}
}
Future encryptAndPreUploadMediaFiles(
int mediaUploadId, Future imageHandler, Future<bool>? videoHandler) async {
Uint8List dataToEncrypt = await imageHandler;
/// if there is a video wait until it is finished with compression
if (videoHandler != null) {
if (await videoHandler) {
Uint8List compressedVideo = await readMediaFile(mediaUploadId, "mp4");
dataToEncrypt = combineUint8Lists(dataToEncrypt, compressedVideo);
}
}
var state = MediaEncryptionData();
final xchacha20 = Xchacha20.poly1305Aead();
SecretKeyData secretKey = await (await xchacha20.newSecretKey()).extract();
state.encryptionKey = secretKey.bytes;
state.encryptionNonce = xchacha20.newNonce();
final secretBox = await xchacha20.encrypt(
dataToEncrypt,
secretKey: secretKey,
nonce: state.encryptionNonce,
);
state.encryptionMac = secretBox.mac.bytes;
final algorithm = Sha256();
state.sha2Hash = (await algorithm.hash(secretBox.cipherText)).bytes;
final encryptedBytes = Uint8List.fromList(secretBox.cipherText);
await writeMediaFile(
mediaUploadId,
"encrypted",
encryptedBytes,
);
await twonlyDatabase.mediaUploadsDao.updateMediaUpload(
mediaUploadId,
MediaUploadsCompanion(
state: Value(UploadState.readyToUpload),
encryptionData: Value(state),
),
);
await handleNextMediaUploadSteps(mediaUploadId);
}
Future cancelSendMediaFile(int mediaUploadId) async {
await twonlyDatabase.mediaUploadsDao.deleteMediaUpload(mediaUploadId);
/// server should purge the uploads... when it did not receive a
}
Future finalizeUpload(int mediaUploadId, List<int> contactIds,
bool isRealTwonly, bool isVideo, bool mirrorVideo, int maxShowTime) async {
MediaUploadMetadata metadata = MediaUploadMetadata();
metadata.contactIds = contactIds;
metadata.isRealTwonly = isRealTwonly;
metadata.messageSendAt = DateTime.now();
metadata.isVideo = isVideo;
metadata.maxShowTime = maxShowTime;
metadata.mirrorVideo = mirrorVideo;
List<int> messageIds = []; List<int> messageIds = [];
for (final contactId in media.metadata.contactIds) { for (final contactId in contactIds) {
int? messageId = await twonlyDatabase.messagesDao.insertMessage( int? messageId = await twonlyDatabase.messagesDao.insertMessage(
MessagesCompanion( MessagesCompanion(
contactId: Value(contactId), contactId: Value(contactId),
kind: Value(MessageKind.media), kind: Value(MessageKind.media),
sendAt: Value(media.metadata.messageSendAt), sendAt: Value(metadata.messageSendAt),
downloadState: Value(DownloadState.pending), downloadState: Value(DownloadState.pending),
mediaUploadId: Value(media.mediaUploadId), mediaUploadId: Value(mediaUploadId),
contentJson: Value( contentJson: Value(
jsonEncode( jsonEncode(
MediaMessageContent( MediaMessageContent(
maxShowTime: media.metadata.maxShowTime, maxShowTime: maxShowTime,
isRealTwonly: media.metadata.isRealTwonly, isRealTwonly: isRealTwonly,
isVideo: media.metadata.isVideo, isVideo: isVideo,
mirrorVideo: media.metadata.mirrorVideo, mirrorVideo: mirrorVideo,
).toJson(), ).toJson(),
), ),
), ),
), ),
); // dearchive contact when sending a new message );
// de-archive contact when sending a new message
await twonlyDatabase.contactsDao.updateContact( await twonlyDatabase.contactsDao.updateContact(
contactId, contactId,
ContactsCompanion( ContactsCompanion(
@ -218,51 +261,257 @@ Future handleAddToMessageDb(MediaUpload media) async {
} }
await twonlyDatabase.mediaUploadsDao.updateMediaUpload( await twonlyDatabase.mediaUploadsDao.updateMediaUpload(
media.mediaUploadId, mediaUploadId,
MediaUploadsCompanion( MediaUploadsCompanion(
state: Value(UploadState.addedToMessagesDb),
messageIds: Value(messageIds), messageIds: Value(messageIds),
metadata: Value(metadata),
), ),
); );
} }
Future<Uint8List?> handleCompressionState( final lockingHandleNextMediaUploadStep = Mutex();
MediaUpload media, Future handleNextMediaUploadSteps(int mediaUploadId) async {
Uint8List? tmpCurrentImageBytes, bool rerun = await lockingHandleNextMediaUploadStep.protect<bool>(() async {
) async { var mediaUpload = await twonlyDatabase.mediaUploadsDao
Uint8List imageBytes = (tmpCurrentImageBytes != null) .getMediaUploadById(mediaUploadId)
? tmpCurrentImageBytes .getSingleOrNull();
: await readMediaFile(media, "orginal.png");
Uint8List imageBytesCompressed; if (mediaUpload == null) return false;
if (mediaUpload.state == UploadState.receiverNotified) {
/// Upload done and all users are notified :)
return false;
}
try { try {
imageBytesCompressed = await FlutterImageCompress.compressWithList( /// Stage 1: media files are not yet encrypted...
format: CompressFormat.png, if (mediaUpload.encryptionData == null) {
imageBytes, // when set this function will be called again by encryptAndPreUploadMediaFiles...
quality: 90, return false;
);
if (imageBytesCompressed.length >= 1 * 1000 * 1000) {
// if the media file is over 1MB compress it with 60%
imageBytesCompressed = await FlutterImageCompress.compressWithList(
format: CompressFormat.png,
imageBytes,
quality: 60,
);
} }
await writeMediaFile(media.mediaUploadId, "png", imageBytesCompressed);
if (mediaUpload.uploadTokens == null) {
/// the files are not yet uploaded, handle upload...
/// if the upload succeed the uploadTokens was updated and this function
/// can be called again to processed the upload done
return await handleMediaUpload(mediaUploadId);
}
if (mediaUpload.messageIds == null || mediaUpload.metadata == null) {
/// the finalize function was not called yet...
return false;
}
// at this point the media file is uploaded and the receiver are known.
final downloadTokens = mediaUpload.uploadTokens!.downloadTokens;
if (downloadTokens.isEmpty) {
/// there are no download tokens yet, request them...
return await handleUploadDone(mediaUpload);
}
// download tokens are known so send the media file to the receivers
await handleNotifyReceiver(mediaUpload);
} catch (e) { } catch (e) {
Logger("media_send.dart").shout("$e"); Logger("media_send.dart")
// as a fall back use the orginal image .shout("Non recoverable error while sending media file: $e");
await writeMediaFile(media.mediaUploadId, "png", imageBytes); await handleUploadError(mediaUpload);
imageBytesCompressed = imageBytes; }
return false;
});
if (rerun) {
handleNextMediaUploadSteps(mediaUploadId);
}
} }
if (media.metadata.isVideo) { ///
String basePath = await getMediaFilePath(media.mediaUploadId, "send"); /// -- private functions --
///
///
///
Future handleUploadError(MediaUpload mediaUpload) async {
// if the messageIds are already there notify the user about this error...
if (mediaUpload.messageIds != null) {
for (int messageId in mediaUpload.messageIds!) {
await twonlyDatabase.messagesDao.updateMessageByMessageId(
messageId,
MessagesCompanion(
errorWhileSending: Value(true),
),
);
}
}
await twonlyDatabase.mediaUploadsDao
.deleteMediaUpload(mediaUpload.mediaUploadId);
}
Future<bool> handleUploadDone(MediaUpload media) async {
Result res = await apiProvider.getDownloadTokens(
media.uploadTokens!.uploadToken, media.messageIds!.length);
if (res.isError || !res.value.hasDownloadtokens()) {
if (res.isError) {
if (res.error == ErrorCode.PlanNotAllowed) {
throw Exception("PlanNotAllowed");
}
if (res.error == ErrorCode.PlanLimitReached) {
throw Exception("PlanLimitReached");
}
}
Logger("media_send.dart")
.info("Upload done will be tried again when reconnected to server!");
return false;
}
Response_DownloadTokens tokens = res.value.downloadtokens;
var token = MediaUploadTokens();
token.uploadToken = media.uploadTokens!.uploadToken;
token.downloadTokens = tokens.downloadTokens;
await twonlyDatabase.mediaUploadsDao.updateMediaUpload(
media.mediaUploadId,
MediaUploadsCompanion(
uploadTokens: Value(token),
),
);
return true;
}
Future<bool> handleMediaUpload(int mediaUploadId) async {
Uint8List bytesToUpload = await readMediaFile(mediaUploadId, "encrypted");
final storage = FlutterSecureStorage();
String? apiAuthToken = await storage.read(key: "api_auth_token");
if (apiAuthToken == null) {
Logger("media_send.dart").shout("api auth token not defined.");
return false;
}
String apiUrl =
"http${apiProvider.apiSecure}://${apiProvider.apiHost}/api/upload";
var requestMultipart = http.MultipartRequest(
"POST",
Uri.parse(apiUrl),
);
requestMultipart.headers['x-twonly-auth-token'] =
uint8ListToHex(base64Decode(apiAuthToken));
requestMultipart.files.add(http.MultipartFile.fromBytes(
"file",
bytesToUpload,
filename: "upload",
));
try {
var streamedResponse = await requestMultipart.send();
final response = await http.Response.fromStream(streamedResponse);
if (response.statusCode == 200) {
Logger("media_send.dart").info("Uploaded: $response");
if (response.body.length != 64) {
Logger("media_send.dart").info("Got invalid upload token.");
return false;
}
final uploadToken = hexToUint8List(response.body);
var token = MediaUploadTokens();
token.uploadToken = uploadToken;
token.downloadTokens = [];
await twonlyDatabase.mediaUploadsDao.updateMediaUpload(
mediaUploadId,
MediaUploadsCompanion(
uploadTokens: Value(token),
),
);
return true;
}
} catch (e) {
Logger("media_send.dart").shout("Exception during upload: $e");
}
return false;
}
Future<bool> handleNotifyReceiver(MediaUpload media) async {
List<int> alreadyNotified = media.alreadyNotified;
for (var i = 0; i < media.messageIds!.length; i++) {
int messageId = media.messageIds![i];
if (alreadyNotified.contains(messageId)) {
continue;
}
Message? message = await twonlyDatabase.messagesDao
.getMessageByMessageId(messageId)
.getSingleOrNull();
if (message == null) continue;
await twonlyDatabase.contactsDao.incFlameCounter(
message.contactId,
false,
message.sendAt,
);
// Ensures the retransmit of the message
await encryptAndSendMessageAsync(
messageId,
message.contactId,
MessageJson(
kind: MessageKind.media,
messageId: messageId,
content: MediaMessageContent(
downloadToken: media.uploadTokens!.downloadTokens[i],
maxShowTime: media.metadata!.maxShowTime,
isRealTwonly: media.metadata!.isRealTwonly,
isVideo: media.metadata!.isVideo,
mirrorVideo: media.metadata!.mirrorVideo,
encryptionKey: media.encryptionData!.encryptionKey,
encryptionMac: media.encryptionData!.encryptionMac,
encryptionNonce: media.encryptionData!.encryptionNonce,
),
timestamp: media.metadata!.messageSendAt,
),
pushKind: (media.metadata!.isRealTwonly)
? PushKind.twonly
: (media.metadata!.isVideo)
? PushKind.video
: PushKind.image,
);
alreadyNotified.add(messageId);
await twonlyDatabase.mediaUploadsDao.updateMediaUpload(
media.mediaUploadId,
MediaUploadsCompanion(
alreadyNotified: Value(alreadyNotified),
),
);
}
await twonlyDatabase.mediaUploadsDao.updateMediaUpload(
media.mediaUploadId,
MediaUploadsCompanion(
state: Value(UploadState.receiverNotified),
),
);
return true;
}
Future<bool> compressVideoIfExists(int mediaUploadId) async {
String basePath = await getMediaFilePath(mediaUploadId, "send");
File videoOriginalFile = File("$basePath.orginal.mp4"); File videoOriginalFile = File("$basePath.orginal.mp4");
File videoCompressedFile = File("$basePath.mp4"); File videoCompressedFile = File("$basePath.mp4");
if (videoCompressedFile.existsSync()) {
// file is already compressed and exists
return true;
}
if (!videoOriginalFile.existsSync()) {
// media upload does not have a video
return false;
}
MediaInfo? mediaInfo; MediaInfo? mediaInfo;
try { try {
@ -296,215 +545,13 @@ Future<Uint8List?> handleCompressionState(
await mediaInfo.file!.copy(videoCompressedFile.path); await mediaInfo.file!.copy(videoCompressedFile.path);
await mediaInfo.file!.delete(); await mediaInfo.file!.delete();
} }
}
await twonlyDatabase.mediaUploadsDao.updateMediaUpload(
media.mediaUploadId,
MediaUploadsCompanion(
state: Value(UploadState.isCompressed),
),
);
return imageBytesCompressed;
}
Future<Uint8List> handleEncryptionState(
MediaUpload media, Uint8List? tmpCurrentImageBytes) async {
var state = MediaEncryptionData();
Uint8List dataToEncrypt = (tmpCurrentImageBytes != null)
? tmpCurrentImageBytes
: await readMediaFile(media, "png");
if (media.metadata.isVideo) {
Uint8List compressedVideo = await readMediaFile(media, "mp4");
dataToEncrypt = combineUint8Lists(dataToEncrypt, compressedVideo);
}
final xchacha20 = Xchacha20.poly1305Aead();
SecretKeyData secretKey = await (await xchacha20.newSecretKey()).extract();
state.encryptionKey = secretKey.bytes;
state.encryptionNonce = xchacha20.newNonce();
final secretBox = await xchacha20.encrypt(
dataToEncrypt,
secretKey: secretKey,
nonce: state.encryptionNonce,
);
state.encryptionMac = secretBox.mac.bytes;
final algorithm = Sha256();
state.sha2Hash = (await algorithm.hash(secretBox.cipherText)).bytes;
final encryptedBytes = Uint8List.fromList(secretBox.cipherText);
await writeMediaFile(
media.mediaUploadId,
"encrypted",
encryptedBytes,
);
await twonlyDatabase.mediaUploadsDao.updateMediaUpload(
media.mediaUploadId,
MediaUploadsCompanion(
state: Value(UploadState.isEncrypted),
encryptionData: Value(state),
),
);
return encryptedBytes;
}
Future<bool> handleGetUploadToken(MediaUpload media) async {
final res =
await apiProvider.getUploadToken(media.metadata.contactIds.length);
if (res.isError || !res.value.hasUploadtoken()) {
if (res.isError) {
if (res.error == ErrorCode.PlanNotAllowed) {
throw Exception("PlanNotAllowed");
}
if (res.error == ErrorCode.PlanLimitReached) {
throw Exception("PlanLimitReached");
}
}
Logger("media_send.dart")
.shout("Will be tried again when reconnected to server!");
return false;
}
Response_UploadToken tokens = res.value.uploadtoken;
var token = MediaUploadTokens();
token.uploadToken = tokens.uploadToken;
token.downloadTokens = tokens.downloadTokens;
await twonlyDatabase.mediaUploadsDao.updateMediaUpload(
media.mediaUploadId,
MediaUploadsCompanion(
state: Value(UploadState.hasUploadToken),
uploadTokens: Value(token),
),
);
return true;
}
String uint8ListToHex(List<int> bytes) {
return bytes.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join();
}
Future<bool> handleUploadHttp(
MediaUpload media, Uint8List? tmpCurrentImageBytes) async {
Uint8List bytesToUpload = (tmpCurrentImageBytes != null)
? tmpCurrentImageBytes
: await readMediaFile(media, "encrypted");
String apiUrl =
"http${apiProvider.apiSecure}://${apiProvider.apiHost}/api/upload";
var requestMultipart = http.MultipartRequest(
"POST",
Uri.parse(apiUrl),
);
requestMultipart.files.add(http.MultipartFile.fromBytes(
"file",
bytesToUpload,
filename: uint8ListToHex(media.uploadTokens!.uploadToken),
));
try {
var streamedResponse = await requestMultipart.send();
await streamedResponse.stream.drain();
Logger("media_send.dart").info("Uploaded: ${streamedResponse.statusCode}");
if (streamedResponse.statusCode == 200) {
await twonlyDatabase.mediaUploadsDao.updateMediaUpload(
media.mediaUploadId,
MediaUploadsCompanion(
state: Value(UploadState.isUploaded),
),
);
return true;
}
return false;
} catch (e) {
Logger("media_send.dart").shout("Exception during upload: $e");
return false;
}
}
Future<bool> handleNotifyReceiver(MediaUpload media) async {
List<int> alreadyNotified = media.alreadyNotified;
for (var i = 0; i < media.messageIds!.length; i++) {
int messageId = media.messageIds![i];
if (alreadyNotified.contains(messageId)) {
continue;
}
Message? message = await twonlyDatabase.messagesDao
.getMessageByMessageId(messageId)
.getSingleOrNull();
if (message == null) continue;
await twonlyDatabase.contactsDao.incFlameCounter(
message.contactId,
false,
message.sendAt,
);
// Ensures the retransmit of the message
await encryptAndSendMessageAsync(
messageId,
message.contactId,
MessageJson(
kind: MessageKind.media,
messageId: messageId,
content: MediaMessageContent(
downloadToken: media.uploadTokens!.downloadTokens[i],
maxShowTime: media.metadata.maxShowTime,
isRealTwonly: media.metadata.isRealTwonly,
isVideo: media.metadata.isVideo,
mirrorVideo: media.metadata.mirrorVideo,
encryptionKey: media.encryptionData!.encryptionKey,
encryptionMac: media.encryptionData!.encryptionMac,
encryptionNonce: media.encryptionData!.encryptionNonce,
),
timestamp: media.metadata.messageSendAt,
),
pushKind: (media.metadata.isRealTwonly)
? PushKind.twonly
: (media.metadata.isVideo)
? PushKind.video
: PushKind.image,
);
alreadyNotified.add(messageId);
await twonlyDatabase.mediaUploadsDao.updateMediaUpload(
media.mediaUploadId,
MediaUploadsCompanion(
alreadyNotified: Value(alreadyNotified),
),
);
}
await twonlyDatabase.mediaUploadsDao.updateMediaUpload(
media.mediaUploadId,
MediaUploadsCompanion(
state: Value(UploadState.receiverNotified),
),
);
return true; return true;
} }
/// --- helper functions --- /// --- helper functions ---
Future<Uint8List> readMediaFile(MediaUpload media, String type) async { Future<Uint8List> readMediaFile(int mediaUploadId, String type) async {
String basePath = await getMediaFilePath(media.mediaUploadId, "send"); String basePath = await getMediaFilePath(mediaUploadId, "send");
File file = File("$basePath.$type"); File file = File("$basePath.$type");
if (!await file.exists()) { if (!await file.exists()) {
throw Exception("$file not found"); throw Exception("$file not found");
@ -562,3 +609,11 @@ Future<void> purgeSendMediaFiles() async {
final directory = Directory(join(basedir.path, 'media', "send")); final directory = Directory(join(basedir.path, 'media', "send"));
await purgeMediaFiles(directory); await purgeMediaFiles(directory);
} }
String uint8ListToHex(List<int> bytes) {
return bytes.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join();
}
Uint8List hexToUint8List(String hex) => Uint8List.fromList(List<int>.generate(
hex.length ~/ 2,
(i) => int.parse(hex.substring(i * 2, i * 2 + 2), radix: 16)));

View file

@ -362,6 +362,16 @@ class ApiProvider {
return await sendRequestSync(req); return await sendRequestSync(req);
} }
Future<Result> getDownloadTokens(
List<int> uploadToken, int recipientsCount) async {
var get = ApplicationData_UploadDone()
..uploadToken = uploadToken
..recipientsCount = recipientsCount;
var appData = ApplicationData()..uploaddone = get;
var req = createClientToServerFromApplicationData(appData);
return await sendRequestSync(req);
}
Future<Result> getCurrentLocation() async { Future<Result> getCurrentLocation() async {
var get = ApplicationData_GetLocation(); var get = ApplicationData_GetLocation();
var appData = ApplicationData()..getlocation = get; var appData = ApplicationData()..getlocation = get;

View file

@ -267,7 +267,7 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
if (shoudReturn != null && shoudReturn) { if (shoudReturn != null && shoudReturn) {
// ignore: use_build_context_synchronously // ignore: use_build_context_synchronously
if (widget.sendTo == null) { if (widget.sendTo == null) {
globalUpdateOfHomeViewPageIndex(1); globalUpdateOfHomeViewPageIndex(0);
} else { } else {
Navigator.pop(context); Navigator.pop(context);
} }

View file

@ -52,23 +52,26 @@ class ShareImageEditorView extends StatefulWidget {
class _ShareImageEditorView extends State<ShareImageEditorView> { class _ShareImageEditorView extends State<ShareImageEditorView> {
bool _isRealTwonly = false; bool _isRealTwonly = false;
bool videoWithAudio = true;
int maxShowTime = gMediaShowInfinite; int maxShowTime = gMediaShowInfinite;
String? sendNextMediaToUserName; String? sendNextMediaToUserName;
double tabDownPostion = 0; double tabDownPosition = 0;
bool sendingOrLoadingImage = true; bool sendingOrLoadingImage = true;
bool isDisposed = false; bool isDisposed = false;
HashSet<int> selectedUserIds = HashSet(); HashSet<int> selectedUserIds = HashSet();
double widthRatio = 1, heightRatio = 1, pixelRatio = 1; double widthRatio = 1, heightRatio = 1, pixelRatio = 1;
VideoPlayerController? videoController; VideoPlayerController? videoController;
ImageItem currentImage = ImageItem(); ImageItem currentImage = ImageItem();
ScreenshotController screenshotController = ScreenshotController(); ScreenshotController screenshotController = ScreenshotController();
/// Media upload variables
int? mediaUploadId;
Future<bool>? videoUploadHandler;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
initAsync(); initAsync();
initMediaFileUpload();
if (widget.imageBytes != null) { if (widget.imageBytes != null) {
loadImage(widget.imageBytes!); loadImage(widget.imageBytes!);
} else if (widget.videoFilePath != null) { } else if (widget.videoFilePath != null) {
@ -97,6 +100,19 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
} }
} }
Future initMediaFileUpload() async {
// media init was already called...
if (mediaUploadId != null) return;
mediaUploadId = await initMediaUpload();
if (widget.videoFilePath != null && mediaUploadId != null) {
// start with the video compression...
videoUploadHandler =
addVideoToUpload(mediaUploadId!, widget.videoFilePath!);
}
}
@override @override
void dispose() { void dispose() {
isDisposed = true; isDisposed = true;
@ -221,19 +237,6 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
}, },
), ),
), ),
// https://github.com/jonataslaw/VideoCompress/issues/184
// if (widget.videoFilePath != null) const SizedBox(height: 8),
// if (widget.videoFilePath != null)
// ActionButton(
// (videoWithAudio) ? Icons.volume_up_rounded : Icons.volume_off_rounded,
// tooltipText: context.lang.protectAsARealTwonly,
// color: Colors.white,
// onPressed: () async {
// setState(() {
// videoWithAudio = !videoWithAudio;
// });
// },
// ),
const SizedBox(height: 8), const SizedBox(height: 8),
ActionButton( ActionButton(
FontAwesomeIcons.shieldHeart, FontAwesomeIcons.shieldHeart,
@ -312,8 +315,13 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
} }
Future pushShareImageView() async { Future pushShareImageView() async {
if (mediaUploadId == null) {
await initMediaFileUpload();
if (mediaUploadId == null) return;
}
Future<Uint8List?> imageBytes = getMergedImage(); Future<Uint8List?> imageBytes = getMergedImage();
videoController?.pause(); videoController?.pause();
if (isDisposed || !context.mounted) return;
bool? wasSend = await Navigator.push( bool? wasSend = await Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
@ -323,7 +331,8 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
maxShowTime: maxShowTime, maxShowTime: maxShowTime,
selectedUserIds: selectedUserIds, selectedUserIds: selectedUserIds,
updateStatus: updateStatus, updateStatus: updateStatus,
videoFilePath: widget.videoFilePath, videoUploadHandler: videoUploadHandler,
mediaUploadId: mediaUploadId!,
mirrorVideo: widget.mirrorVideo, mirrorVideo: widget.mirrorVideo,
), ),
), ),
@ -402,14 +411,26 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
); );
})); }));
} else { } else {
sendMediaFile( Future imageHandler =
addOrModifyImageToUpload(mediaUploadId!, imageBytes);
// first finalize the upload
await finalizeUpload(
mediaUploadId!,
[widget.sendTo!.userId], [widget.sendTo!.userId],
imageBytes,
_isRealTwonly, _isRealTwonly,
maxShowTime, widget.videoFilePath != null,
widget.videoFilePath,
widget.mirrorVideo, widget.mirrorVideo,
maxShowTime,
); );
/// then call the upload process in the background
encryptAndPreUploadMediaFiles(
mediaUploadId!,
imageHandler,
videoUploadHandler,
);
if (context.mounted) { if (context.mounted) {
// ignore: use_build_context_synchronously // ignore: use_build_context_synchronously
Navigator.pop(context, true); Navigator.pop(context, true);
@ -434,9 +455,9 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
GestureDetector( GestureDetector(
onTapDown: (details) { onTapDown: (details) {
if (details.globalPosition.dy > 60) { if (details.globalPosition.dy > 60) {
tabDownPostion = details.globalPosition.dy - 60; tabDownPosition = details.globalPosition.dy - 60;
} else { } else {
tabDownPostion = details.globalPosition.dy; tabDownPosition = details.globalPosition.dy;
} }
}, },
onTap: () { onTap: () {
@ -446,7 +467,7 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
undoLayers.clear(); undoLayers.clear();
removedLayers.clear(); removedLayers.clear();
layers.add(TextLayerData( layers.add(TextLayerData(
offset: Offset(0, tabDownPostion), offset: Offset(0, tabDownPosition),
textLayersBefore: layers.whereType<TextLayerData>().length, textLayersBefore: layers.whereType<TextLayerData>().length,
)); ));
setState(() {}); setState(() {});

View file

@ -1,6 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:collection'; import 'dart:collection';
import 'dart:io';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
@ -26,16 +25,18 @@ class ShareImageView extends StatefulWidget {
required this.maxShowTime, required this.maxShowTime,
required this.selectedUserIds, required this.selectedUserIds,
required this.updateStatus, required this.updateStatus,
required this.videoFilePath, required this.videoUploadHandler,
required this.mediaUploadId,
this.enableVideoAudio}); this.enableVideoAudio});
final Future<Uint8List?> imageBytesFuture; final Future<Uint8List?> imageBytesFuture;
final bool isRealTwonly; final bool isRealTwonly;
final bool mirrorVideo; final bool mirrorVideo;
final int maxShowTime; final int maxShowTime;
final File? videoFilePath;
final HashSet<int> selectedUserIds; final HashSet<int> selectedUserIds;
final bool? enableVideoAudio; final bool? enableVideoAudio;
final int mediaUploadId;
final Function(int, bool) updateStatus; final Function(int, bool) updateStatus;
final Future<bool>? videoUploadHandler;
@override @override
State<ShareImageView> createState() => _ShareImageView(); State<ShareImageView> createState() => _ShareImageView();
@ -73,6 +74,13 @@ class _ShareImageView extends State<ShareImageView> {
Future initAsync() async { Future initAsync() async {
imageBytes = await widget.imageBytesFuture; imageBytes = await widget.imageBytesFuture;
if (imageBytes != null) {
final imageHandler =
addOrModifyImageToUpload(widget.mediaUploadId, imageBytes!);
// start with the pre upload of the media file...
encryptAndPreUploadMediaFiles(
widget.mediaUploadId, imageHandler, widget.videoUploadHandler);
}
setState(() {}); setState(() {});
} }
@ -287,14 +295,18 @@ class _ShareImageView extends State<ShareImageView> {
sendingImage = true; sendingImage = true;
}); });
sendMediaFile( await finalizeUpload(
widget.mediaUploadId,
widget.selectedUserIds.toList(), widget.selectedUserIds.toList(),
imageBytes!,
widget.isRealTwonly, widget.isRealTwonly,
widget.maxShowTime, widget.videoUploadHandler != null,
widget.videoFilePath,
widget.mirrorVideo, widget.mirrorVideo,
widget.maxShowTime,
); );
/// trigger the upload of the media file.
handleNextMediaUploadSteps(widget.mediaUploadId);
if (context.mounted) { if (context.mounted) {
Navigator.pop(context, true); Navigator.pop(context, true);
// if (widget.preselectedUser != null) { // if (widget.preselectedUser != null) {

View file

@ -39,7 +39,7 @@ class HomeViewState extends State<HomeView> {
selectNotificationStream.stream selectNotificationStream.stream
.listen((NotificationResponse? response) async { .listen((NotificationResponse? response) async {
globalUpdateOfHomeViewPageIndex(1); globalUpdateOfHomeViewPageIndex(0);
}); });
initAsync(); initAsync();
} }
@ -50,7 +50,7 @@ class HomeViewState extends State<HomeView> {
if (notificationAppLaunchDetails != null) { if (notificationAppLaunchDetails != null) {
if (notificationAppLaunchDetails.didNotificationLaunchApp) { if (notificationAppLaunchDetails.didNotificationLaunchApp) {
globalUpdateOfHomeViewPageIndex(1); globalUpdateOfHomeViewPageIndex(0);
} }
} }
} }

View file

@ -11,6 +11,7 @@ import 'schema_v5.dart' as v5;
import 'schema_v6.dart' as v6; import 'schema_v6.dart' as v6;
import 'schema_v7.dart' as v7; import 'schema_v7.dart' as v7;
import 'schema_v8.dart' as v8; import 'schema_v8.dart' as v8;
import 'schema_v9.dart' as v9;
class GeneratedHelper implements SchemaInstantiationHelper { class GeneratedHelper implements SchemaInstantiationHelper {
@override @override
@ -32,10 +33,12 @@ class GeneratedHelper implements SchemaInstantiationHelper {
return v7.DatabaseAtV7(db); return v7.DatabaseAtV7(db);
case 8: case 8:
return v8.DatabaseAtV8(db); return v8.DatabaseAtV8(db);
case 9:
return v9.DatabaseAtV9(db);
default: default:
throw MissingSchemaException(version, versions); throw MissingSchemaException(version, versions);
} }
} }
static const versions = const [1, 2, 3, 4, 5, 6, 7, 8]; static const versions = const [1, 2, 3, 4, 5, 6, 7, 8, 9];
} }

File diff suppressed because it is too large Load diff