mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-04-18 14:22:53 +00:00
typing indicator
This commit is contained in:
parent
727949c3d9
commit
a73b2737e7
31 changed files with 10637 additions and 117 deletions
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
## 0.1.4
|
||||
|
||||
- New: Typing and chat open indicator
|
||||
- New: Screen lock for twonly (Can be enabled in the settings.)
|
||||
- Fix: Several minor issues with the user interface
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,11 @@
|
|||
// For information on using the generated types, please see the documentation:
|
||||
// https://github.com/apple/swift-protobuf/
|
||||
|
||||
#if canImport(FoundationEssentials)
|
||||
import FoundationEssentials
|
||||
#else
|
||||
import Foundation
|
||||
#endif
|
||||
import SwiftProtobuf
|
||||
|
||||
// If the compiler emits an error on this type, it is because this file
|
||||
|
|
|
|||
|
|
@ -129,6 +129,16 @@ class ReceiptsDao extends DatabaseAccessor<TwonlyDB> with _$ReceiptsDaoMixin {
|
|||
return select(receipts).watch();
|
||||
}
|
||||
|
||||
Future<int> getReceiptCountForContact(int contactId) {
|
||||
final countExp = countAll();
|
||||
|
||||
final query = selectOnly(receipts)
|
||||
..addColumns([countExp])
|
||||
..where(receipts.contactId.equals(contactId));
|
||||
|
||||
return query.map((row) => row.read(countExp)!).getSingle();
|
||||
}
|
||||
|
||||
Future<void> updateReceipt(
|
||||
String receiptId,
|
||||
ReceiptsCompanion updates,
|
||||
|
|
|
|||
2095
lib/src/database/schemas/twonly_db/drift_schema_v11.json
Normal file
2095
lib/src/database/schemas/twonly_db/drift_schema_v11.json
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -30,8 +30,9 @@ class Groups extends Table {
|
|||
BoolColumn get alsoBestFriend =>
|
||||
boolean().withDefault(const Constant(false))();
|
||||
|
||||
IntColumn get deleteMessagesAfterMilliseconds => integer()
|
||||
.withDefault(const Constant(defaultDeleteMessagesAfterMilliseconds))();
|
||||
IntColumn get deleteMessagesAfterMilliseconds => integer().withDefault(
|
||||
const Constant(defaultDeleteMessagesAfterMilliseconds),
|
||||
)();
|
||||
|
||||
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
|
||||
|
||||
|
|
@ -63,6 +64,9 @@ class GroupMembers extends Table {
|
|||
TextColumn get memberState => textEnum<MemberState>().nullable()();
|
||||
BlobColumn get groupPublicKey => blob().nullable()();
|
||||
|
||||
DateTimeColumn get lastChatOpened => dateTime().nullable()();
|
||||
DateTimeColumn get lastTypeIndicator => dateTime().nullable()();
|
||||
|
||||
DateTimeColumn get lastMessage => dateTime().nullable()();
|
||||
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
|
||||
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ class TwonlyDB extends _$TwonlyDB {
|
|||
TwonlyDB.forTesting(DatabaseConnection super.connection);
|
||||
|
||||
@override
|
||||
int get schemaVersion => 10;
|
||||
int get schemaVersion => 11;
|
||||
|
||||
static QueryExecutor _openConnection() {
|
||||
return driftDatabase(
|
||||
|
|
@ -143,6 +143,16 @@ class TwonlyDB extends _$TwonlyDB {
|
|||
schema.receipts.willBeRetriedByMediaUpload,
|
||||
);
|
||||
},
|
||||
from10To11: (m, schema) async {
|
||||
await m.addColumn(
|
||||
schema.groupMembers,
|
||||
schema.groupMembers.lastChatOpened,
|
||||
);
|
||||
await m.addColumn(
|
||||
schema.groupMembers,
|
||||
schema.groupMembers.lastTypeIndicator,
|
||||
);
|
||||
},
|
||||
)(m, from, to);
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -5198,6 +5198,30 @@ class $GroupMembersTable extends GroupMembers
|
|||
type: DriftSqlType.blob,
|
||||
requiredDuringInsert: false,
|
||||
);
|
||||
static const VerificationMeta _lastChatOpenedMeta = const VerificationMeta(
|
||||
'lastChatOpened',
|
||||
);
|
||||
@override
|
||||
late final GeneratedColumn<DateTime> lastChatOpened =
|
||||
GeneratedColumn<DateTime>(
|
||||
'last_chat_opened',
|
||||
aliasedName,
|
||||
true,
|
||||
type: DriftSqlType.dateTime,
|
||||
requiredDuringInsert: false,
|
||||
);
|
||||
static const VerificationMeta _lastTypeIndicatorMeta = const VerificationMeta(
|
||||
'lastTypeIndicator',
|
||||
);
|
||||
@override
|
||||
late final GeneratedColumn<DateTime> lastTypeIndicator =
|
||||
GeneratedColumn<DateTime>(
|
||||
'last_type_indicator',
|
||||
aliasedName,
|
||||
true,
|
||||
type: DriftSqlType.dateTime,
|
||||
requiredDuringInsert: false,
|
||||
);
|
||||
static const VerificationMeta _lastMessageMeta = const VerificationMeta(
|
||||
'lastMessage',
|
||||
);
|
||||
|
|
@ -5227,6 +5251,8 @@ class $GroupMembersTable extends GroupMembers
|
|||
contactId,
|
||||
memberState,
|
||||
groupPublicKey,
|
||||
lastChatOpened,
|
||||
lastTypeIndicator,
|
||||
lastMessage,
|
||||
createdAt,
|
||||
];
|
||||
|
|
@ -5267,6 +5293,24 @@ class $GroupMembersTable extends GroupMembers
|
|||
),
|
||||
);
|
||||
}
|
||||
if (data.containsKey('last_chat_opened')) {
|
||||
context.handle(
|
||||
_lastChatOpenedMeta,
|
||||
lastChatOpened.isAcceptableOrUnknown(
|
||||
data['last_chat_opened']!,
|
||||
_lastChatOpenedMeta,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (data.containsKey('last_type_indicator')) {
|
||||
context.handle(
|
||||
_lastTypeIndicatorMeta,
|
||||
lastTypeIndicator.isAcceptableOrUnknown(
|
||||
data['last_type_indicator']!,
|
||||
_lastTypeIndicatorMeta,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (data.containsKey('last_message')) {
|
||||
context.handle(
|
||||
_lastMessageMeta,
|
||||
|
|
@ -5309,6 +5353,14 @@ class $GroupMembersTable extends GroupMembers
|
|||
DriftSqlType.blob,
|
||||
data['${effectivePrefix}group_public_key'],
|
||||
),
|
||||
lastChatOpened: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.dateTime,
|
||||
data['${effectivePrefix}last_chat_opened'],
|
||||
),
|
||||
lastTypeIndicator: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.dateTime,
|
||||
data['${effectivePrefix}last_type_indicator'],
|
||||
),
|
||||
lastMessage: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.dateTime,
|
||||
data['${effectivePrefix}last_message'],
|
||||
|
|
@ -5336,6 +5388,8 @@ class GroupMember extends DataClass implements Insertable<GroupMember> {
|
|||
final int contactId;
|
||||
final MemberState? memberState;
|
||||
final Uint8List? groupPublicKey;
|
||||
final DateTime? lastChatOpened;
|
||||
final DateTime? lastTypeIndicator;
|
||||
final DateTime? lastMessage;
|
||||
final DateTime createdAt;
|
||||
const GroupMember({
|
||||
|
|
@ -5343,6 +5397,8 @@ class GroupMember extends DataClass implements Insertable<GroupMember> {
|
|||
required this.contactId,
|
||||
this.memberState,
|
||||
this.groupPublicKey,
|
||||
this.lastChatOpened,
|
||||
this.lastTypeIndicator,
|
||||
this.lastMessage,
|
||||
required this.createdAt,
|
||||
});
|
||||
|
|
@ -5359,6 +5415,12 @@ class GroupMember extends DataClass implements Insertable<GroupMember> {
|
|||
if (!nullToAbsent || groupPublicKey != null) {
|
||||
map['group_public_key'] = Variable<Uint8List>(groupPublicKey);
|
||||
}
|
||||
if (!nullToAbsent || lastChatOpened != null) {
|
||||
map['last_chat_opened'] = Variable<DateTime>(lastChatOpened);
|
||||
}
|
||||
if (!nullToAbsent || lastTypeIndicator != null) {
|
||||
map['last_type_indicator'] = Variable<DateTime>(lastTypeIndicator);
|
||||
}
|
||||
if (!nullToAbsent || lastMessage != null) {
|
||||
map['last_message'] = Variable<DateTime>(lastMessage);
|
||||
}
|
||||
|
|
@ -5376,6 +5438,12 @@ class GroupMember extends DataClass implements Insertable<GroupMember> {
|
|||
groupPublicKey: groupPublicKey == null && nullToAbsent
|
||||
? const Value.absent()
|
||||
: Value(groupPublicKey),
|
||||
lastChatOpened: lastChatOpened == null && nullToAbsent
|
||||
? const Value.absent()
|
||||
: Value(lastChatOpened),
|
||||
lastTypeIndicator: lastTypeIndicator == null && nullToAbsent
|
||||
? const Value.absent()
|
||||
: Value(lastTypeIndicator),
|
||||
lastMessage: lastMessage == null && nullToAbsent
|
||||
? const Value.absent()
|
||||
: Value(lastMessage),
|
||||
|
|
@ -5395,6 +5463,10 @@ class GroupMember extends DataClass implements Insertable<GroupMember> {
|
|||
serializer.fromJson<String?>(json['memberState']),
|
||||
),
|
||||
groupPublicKey: serializer.fromJson<Uint8List?>(json['groupPublicKey']),
|
||||
lastChatOpened: serializer.fromJson<DateTime?>(json['lastChatOpened']),
|
||||
lastTypeIndicator: serializer.fromJson<DateTime?>(
|
||||
json['lastTypeIndicator'],
|
||||
),
|
||||
lastMessage: serializer.fromJson<DateTime?>(json['lastMessage']),
|
||||
createdAt: serializer.fromJson<DateTime>(json['createdAt']),
|
||||
);
|
||||
|
|
@ -5409,6 +5481,8 @@ class GroupMember extends DataClass implements Insertable<GroupMember> {
|
|||
$GroupMembersTable.$convertermemberStaten.toJson(memberState),
|
||||
),
|
||||
'groupPublicKey': serializer.toJson<Uint8List?>(groupPublicKey),
|
||||
'lastChatOpened': serializer.toJson<DateTime?>(lastChatOpened),
|
||||
'lastTypeIndicator': serializer.toJson<DateTime?>(lastTypeIndicator),
|
||||
'lastMessage': serializer.toJson<DateTime?>(lastMessage),
|
||||
'createdAt': serializer.toJson<DateTime>(createdAt),
|
||||
};
|
||||
|
|
@ -5419,6 +5493,8 @@ class GroupMember extends DataClass implements Insertable<GroupMember> {
|
|||
int? contactId,
|
||||
Value<MemberState?> memberState = const Value.absent(),
|
||||
Value<Uint8List?> groupPublicKey = const Value.absent(),
|
||||
Value<DateTime?> lastChatOpened = const Value.absent(),
|
||||
Value<DateTime?> lastTypeIndicator = const Value.absent(),
|
||||
Value<DateTime?> lastMessage = const Value.absent(),
|
||||
DateTime? createdAt,
|
||||
}) => GroupMember(
|
||||
|
|
@ -5428,6 +5504,12 @@ class GroupMember extends DataClass implements Insertable<GroupMember> {
|
|||
groupPublicKey: groupPublicKey.present
|
||||
? groupPublicKey.value
|
||||
: this.groupPublicKey,
|
||||
lastChatOpened: lastChatOpened.present
|
||||
? lastChatOpened.value
|
||||
: this.lastChatOpened,
|
||||
lastTypeIndicator: lastTypeIndicator.present
|
||||
? lastTypeIndicator.value
|
||||
: this.lastTypeIndicator,
|
||||
lastMessage: lastMessage.present ? lastMessage.value : this.lastMessage,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
);
|
||||
|
|
@ -5441,6 +5523,12 @@ class GroupMember extends DataClass implements Insertable<GroupMember> {
|
|||
groupPublicKey: data.groupPublicKey.present
|
||||
? data.groupPublicKey.value
|
||||
: this.groupPublicKey,
|
||||
lastChatOpened: data.lastChatOpened.present
|
||||
? data.lastChatOpened.value
|
||||
: this.lastChatOpened,
|
||||
lastTypeIndicator: data.lastTypeIndicator.present
|
||||
? data.lastTypeIndicator.value
|
||||
: this.lastTypeIndicator,
|
||||
lastMessage: data.lastMessage.present
|
||||
? data.lastMessage.value
|
||||
: this.lastMessage,
|
||||
|
|
@ -5455,6 +5543,8 @@ class GroupMember extends DataClass implements Insertable<GroupMember> {
|
|||
..write('contactId: $contactId, ')
|
||||
..write('memberState: $memberState, ')
|
||||
..write('groupPublicKey: $groupPublicKey, ')
|
||||
..write('lastChatOpened: $lastChatOpened, ')
|
||||
..write('lastTypeIndicator: $lastTypeIndicator, ')
|
||||
..write('lastMessage: $lastMessage, ')
|
||||
..write('createdAt: $createdAt')
|
||||
..write(')'))
|
||||
|
|
@ -5467,6 +5557,8 @@ class GroupMember extends DataClass implements Insertable<GroupMember> {
|
|||
contactId,
|
||||
memberState,
|
||||
$driftBlobEquality.hash(groupPublicKey),
|
||||
lastChatOpened,
|
||||
lastTypeIndicator,
|
||||
lastMessage,
|
||||
createdAt,
|
||||
);
|
||||
|
|
@ -5481,6 +5573,8 @@ class GroupMember extends DataClass implements Insertable<GroupMember> {
|
|||
other.groupPublicKey,
|
||||
this.groupPublicKey,
|
||||
) &&
|
||||
other.lastChatOpened == this.lastChatOpened &&
|
||||
other.lastTypeIndicator == this.lastTypeIndicator &&
|
||||
other.lastMessage == this.lastMessage &&
|
||||
other.createdAt == this.createdAt);
|
||||
}
|
||||
|
|
@ -5490,6 +5584,8 @@ class GroupMembersCompanion extends UpdateCompanion<GroupMember> {
|
|||
final Value<int> contactId;
|
||||
final Value<MemberState?> memberState;
|
||||
final Value<Uint8List?> groupPublicKey;
|
||||
final Value<DateTime?> lastChatOpened;
|
||||
final Value<DateTime?> lastTypeIndicator;
|
||||
final Value<DateTime?> lastMessage;
|
||||
final Value<DateTime> createdAt;
|
||||
final Value<int> rowid;
|
||||
|
|
@ -5498,6 +5594,8 @@ class GroupMembersCompanion extends UpdateCompanion<GroupMember> {
|
|||
this.contactId = const Value.absent(),
|
||||
this.memberState = const Value.absent(),
|
||||
this.groupPublicKey = const Value.absent(),
|
||||
this.lastChatOpened = const Value.absent(),
|
||||
this.lastTypeIndicator = const Value.absent(),
|
||||
this.lastMessage = const Value.absent(),
|
||||
this.createdAt = const Value.absent(),
|
||||
this.rowid = const Value.absent(),
|
||||
|
|
@ -5507,6 +5605,8 @@ class GroupMembersCompanion extends UpdateCompanion<GroupMember> {
|
|||
required int contactId,
|
||||
this.memberState = const Value.absent(),
|
||||
this.groupPublicKey = const Value.absent(),
|
||||
this.lastChatOpened = const Value.absent(),
|
||||
this.lastTypeIndicator = const Value.absent(),
|
||||
this.lastMessage = const Value.absent(),
|
||||
this.createdAt = const Value.absent(),
|
||||
this.rowid = const Value.absent(),
|
||||
|
|
@ -5517,6 +5617,8 @@ class GroupMembersCompanion extends UpdateCompanion<GroupMember> {
|
|||
Expression<int>? contactId,
|
||||
Expression<String>? memberState,
|
||||
Expression<Uint8List>? groupPublicKey,
|
||||
Expression<DateTime>? lastChatOpened,
|
||||
Expression<DateTime>? lastTypeIndicator,
|
||||
Expression<DateTime>? lastMessage,
|
||||
Expression<DateTime>? createdAt,
|
||||
Expression<int>? rowid,
|
||||
|
|
@ -5526,6 +5628,8 @@ class GroupMembersCompanion extends UpdateCompanion<GroupMember> {
|
|||
if (contactId != null) 'contact_id': contactId,
|
||||
if (memberState != null) 'member_state': memberState,
|
||||
if (groupPublicKey != null) 'group_public_key': groupPublicKey,
|
||||
if (lastChatOpened != null) 'last_chat_opened': lastChatOpened,
|
||||
if (lastTypeIndicator != null) 'last_type_indicator': lastTypeIndicator,
|
||||
if (lastMessage != null) 'last_message': lastMessage,
|
||||
if (createdAt != null) 'created_at': createdAt,
|
||||
if (rowid != null) 'rowid': rowid,
|
||||
|
|
@ -5537,6 +5641,8 @@ class GroupMembersCompanion extends UpdateCompanion<GroupMember> {
|
|||
Value<int>? contactId,
|
||||
Value<MemberState?>? memberState,
|
||||
Value<Uint8List?>? groupPublicKey,
|
||||
Value<DateTime?>? lastChatOpened,
|
||||
Value<DateTime?>? lastTypeIndicator,
|
||||
Value<DateTime?>? lastMessage,
|
||||
Value<DateTime>? createdAt,
|
||||
Value<int>? rowid,
|
||||
|
|
@ -5546,6 +5652,8 @@ class GroupMembersCompanion extends UpdateCompanion<GroupMember> {
|
|||
contactId: contactId ?? this.contactId,
|
||||
memberState: memberState ?? this.memberState,
|
||||
groupPublicKey: groupPublicKey ?? this.groupPublicKey,
|
||||
lastChatOpened: lastChatOpened ?? this.lastChatOpened,
|
||||
lastTypeIndicator: lastTypeIndicator ?? this.lastTypeIndicator,
|
||||
lastMessage: lastMessage ?? this.lastMessage,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
rowid: rowid ?? this.rowid,
|
||||
|
|
@ -5569,6 +5677,12 @@ class GroupMembersCompanion extends UpdateCompanion<GroupMember> {
|
|||
if (groupPublicKey.present) {
|
||||
map['group_public_key'] = Variable<Uint8List>(groupPublicKey.value);
|
||||
}
|
||||
if (lastChatOpened.present) {
|
||||
map['last_chat_opened'] = Variable<DateTime>(lastChatOpened.value);
|
||||
}
|
||||
if (lastTypeIndicator.present) {
|
||||
map['last_type_indicator'] = Variable<DateTime>(lastTypeIndicator.value);
|
||||
}
|
||||
if (lastMessage.present) {
|
||||
map['last_message'] = Variable<DateTime>(lastMessage.value);
|
||||
}
|
||||
|
|
@ -5588,6 +5702,8 @@ class GroupMembersCompanion extends UpdateCompanion<GroupMember> {
|
|||
..write('contactId: $contactId, ')
|
||||
..write('memberState: $memberState, ')
|
||||
..write('groupPublicKey: $groupPublicKey, ')
|
||||
..write('lastChatOpened: $lastChatOpened, ')
|
||||
..write('lastTypeIndicator: $lastTypeIndicator, ')
|
||||
..write('lastMessage: $lastMessage, ')
|
||||
..write('createdAt: $createdAt, ')
|
||||
..write('rowid: $rowid')
|
||||
|
|
@ -13326,6 +13442,8 @@ typedef $$GroupMembersTableCreateCompanionBuilder =
|
|||
required int contactId,
|
||||
Value<MemberState?> memberState,
|
||||
Value<Uint8List?> groupPublicKey,
|
||||
Value<DateTime?> lastChatOpened,
|
||||
Value<DateTime?> lastTypeIndicator,
|
||||
Value<DateTime?> lastMessage,
|
||||
Value<DateTime> createdAt,
|
||||
Value<int> rowid,
|
||||
|
|
@ -13336,6 +13454,8 @@ typedef $$GroupMembersTableUpdateCompanionBuilder =
|
|||
Value<int> contactId,
|
||||
Value<MemberState?> memberState,
|
||||
Value<Uint8List?> groupPublicKey,
|
||||
Value<DateTime?> lastChatOpened,
|
||||
Value<DateTime?> lastTypeIndicator,
|
||||
Value<DateTime?> lastMessage,
|
||||
Value<DateTime> createdAt,
|
||||
Value<int> rowid,
|
||||
|
|
@ -13403,6 +13523,16 @@ class $$GroupMembersTableFilterComposer
|
|||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<DateTime> get lastChatOpened => $composableBuilder(
|
||||
column: $table.lastChatOpened,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<DateTime> get lastTypeIndicator => $composableBuilder(
|
||||
column: $table.lastTypeIndicator,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<DateTime> get lastMessage => $composableBuilder(
|
||||
column: $table.lastMessage,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
|
|
@ -13479,6 +13609,16 @@ class $$GroupMembersTableOrderingComposer
|
|||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<DateTime> get lastChatOpened => $composableBuilder(
|
||||
column: $table.lastChatOpened,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<DateTime> get lastTypeIndicator => $composableBuilder(
|
||||
column: $table.lastTypeIndicator,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<DateTime> get lastMessage => $composableBuilder(
|
||||
column: $table.lastMessage,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
|
|
@ -13556,6 +13696,16 @@ class $$GroupMembersTableAnnotationComposer
|
|||
builder: (column) => column,
|
||||
);
|
||||
|
||||
GeneratedColumn<DateTime> get lastChatOpened => $composableBuilder(
|
||||
column: $table.lastChatOpened,
|
||||
builder: (column) => column,
|
||||
);
|
||||
|
||||
GeneratedColumn<DateTime> get lastTypeIndicator => $composableBuilder(
|
||||
column: $table.lastTypeIndicator,
|
||||
builder: (column) => column,
|
||||
);
|
||||
|
||||
GeneratedColumn<DateTime> get lastMessage => $composableBuilder(
|
||||
column: $table.lastMessage,
|
||||
builder: (column) => column,
|
||||
|
|
@ -13643,6 +13793,8 @@ class $$GroupMembersTableTableManager
|
|||
Value<int> contactId = const Value.absent(),
|
||||
Value<MemberState?> memberState = const Value.absent(),
|
||||
Value<Uint8List?> groupPublicKey = const Value.absent(),
|
||||
Value<DateTime?> lastChatOpened = const Value.absent(),
|
||||
Value<DateTime?> lastTypeIndicator = const Value.absent(),
|
||||
Value<DateTime?> lastMessage = const Value.absent(),
|
||||
Value<DateTime> createdAt = const Value.absent(),
|
||||
Value<int> rowid = const Value.absent(),
|
||||
|
|
@ -13651,6 +13803,8 @@ class $$GroupMembersTableTableManager
|
|||
contactId: contactId,
|
||||
memberState: memberState,
|
||||
groupPublicKey: groupPublicKey,
|
||||
lastChatOpened: lastChatOpened,
|
||||
lastTypeIndicator: lastTypeIndicator,
|
||||
lastMessage: lastMessage,
|
||||
createdAt: createdAt,
|
||||
rowid: rowid,
|
||||
|
|
@ -13661,6 +13815,8 @@ class $$GroupMembersTableTableManager
|
|||
required int contactId,
|
||||
Value<MemberState?> memberState = const Value.absent(),
|
||||
Value<Uint8List?> groupPublicKey = const Value.absent(),
|
||||
Value<DateTime?> lastChatOpened = const Value.absent(),
|
||||
Value<DateTime?> lastTypeIndicator = const Value.absent(),
|
||||
Value<DateTime?> lastMessage = const Value.absent(),
|
||||
Value<DateTime> createdAt = const Value.absent(),
|
||||
Value<int> rowid = const Value.absent(),
|
||||
|
|
@ -13669,6 +13825,8 @@ class $$GroupMembersTableTableManager
|
|||
contactId: contactId,
|
||||
memberState: memberState,
|
||||
groupPublicKey: groupPublicKey,
|
||||
lastChatOpened: lastChatOpened,
|
||||
lastTypeIndicator: lastTypeIndicator,
|
||||
lastMessage: lastMessage,
|
||||
createdAt: createdAt,
|
||||
rowid: rowid,
|
||||
|
|
|
|||
|
|
@ -5484,6 +5484,345 @@ i1.GeneratedColumn<int> _column_208(
|
|||
'NOT NULL DEFAULT 0 CHECK (will_be_retried_by_media_upload IN (0, 1))',
|
||||
defaultValue: const i1.CustomExpression('0'),
|
||||
);
|
||||
|
||||
final class Schema11 extends i0.VersionedSchema {
|
||||
Schema11({required super.database}) : super(version: 11);
|
||||
@override
|
||||
late final List<i1.DatabaseSchemaEntity> entities = [
|
||||
contacts,
|
||||
groups,
|
||||
mediaFiles,
|
||||
messages,
|
||||
messageHistories,
|
||||
reactions,
|
||||
groupMembers,
|
||||
receipts,
|
||||
receivedReceipts,
|
||||
signalIdentityKeyStores,
|
||||
signalPreKeyStores,
|
||||
signalSenderKeyStores,
|
||||
signalSessionStores,
|
||||
messageActions,
|
||||
groupHistories,
|
||||
];
|
||||
late final Shape22 contacts = Shape22(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'contacts',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(user_id)'],
|
||||
columns: [
|
||||
_column_106,
|
||||
_column_107,
|
||||
_column_108,
|
||||
_column_109,
|
||||
_column_110,
|
||||
_column_111,
|
||||
_column_112,
|
||||
_column_113,
|
||||
_column_114,
|
||||
_column_115,
|
||||
_column_116,
|
||||
_column_117,
|
||||
_column_118,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape23 groups = Shape23(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'groups',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(group_id)'],
|
||||
columns: [
|
||||
_column_119,
|
||||
_column_120,
|
||||
_column_121,
|
||||
_column_122,
|
||||
_column_123,
|
||||
_column_124,
|
||||
_column_125,
|
||||
_column_126,
|
||||
_column_127,
|
||||
_column_128,
|
||||
_column_129,
|
||||
_column_130,
|
||||
_column_131,
|
||||
_column_132,
|
||||
_column_133,
|
||||
_column_134,
|
||||
_column_118,
|
||||
_column_135,
|
||||
_column_136,
|
||||
_column_137,
|
||||
_column_138,
|
||||
_column_139,
|
||||
_column_140,
|
||||
_column_141,
|
||||
_column_142,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape36 mediaFiles = Shape36(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'media_files',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(media_id)'],
|
||||
columns: [
|
||||
_column_143,
|
||||
_column_144,
|
||||
_column_145,
|
||||
_column_146,
|
||||
_column_147,
|
||||
_column_148,
|
||||
_column_149,
|
||||
_column_207,
|
||||
_column_150,
|
||||
_column_151,
|
||||
_column_152,
|
||||
_column_153,
|
||||
_column_154,
|
||||
_column_155,
|
||||
_column_156,
|
||||
_column_157,
|
||||
_column_118,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape25 messages = Shape25(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'messages',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(message_id)'],
|
||||
columns: [
|
||||
_column_158,
|
||||
_column_159,
|
||||
_column_160,
|
||||
_column_144,
|
||||
_column_161,
|
||||
_column_162,
|
||||
_column_163,
|
||||
_column_164,
|
||||
_column_165,
|
||||
_column_153,
|
||||
_column_166,
|
||||
_column_167,
|
||||
_column_168,
|
||||
_column_169,
|
||||
_column_118,
|
||||
_column_170,
|
||||
_column_171,
|
||||
_column_172,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape26 messageHistories = Shape26(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'message_histories',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: [],
|
||||
columns: [
|
||||
_column_173,
|
||||
_column_174,
|
||||
_column_175,
|
||||
_column_161,
|
||||
_column_118,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape27 reactions = Shape27(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'reactions',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(message_id, sender_id, emoji)'],
|
||||
columns: [_column_174, _column_176, _column_177, _column_118],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape38 groupMembers = Shape38(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'group_members',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(group_id, contact_id)'],
|
||||
columns: [
|
||||
_column_158,
|
||||
_column_178,
|
||||
_column_179,
|
||||
_column_180,
|
||||
_column_209,
|
||||
_column_210,
|
||||
_column_181,
|
||||
_column_118,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape37 receipts = Shape37(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'receipts',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(receipt_id)'],
|
||||
columns: [
|
||||
_column_182,
|
||||
_column_183,
|
||||
_column_184,
|
||||
_column_185,
|
||||
_column_186,
|
||||
_column_208,
|
||||
_column_187,
|
||||
_column_188,
|
||||
_column_189,
|
||||
_column_190,
|
||||
_column_191,
|
||||
_column_118,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape30 receivedReceipts = Shape30(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'received_receipts',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(receipt_id)'],
|
||||
columns: [_column_182, _column_118],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape31 signalIdentityKeyStores = Shape31(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'signal_identity_key_stores',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(device_id, name)'],
|
||||
columns: [_column_192, _column_193, _column_194, _column_118],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape32 signalPreKeyStores = Shape32(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'signal_pre_key_stores',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(pre_key_id)'],
|
||||
columns: [_column_195, _column_196, _column_118],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape11 signalSenderKeyStores = Shape11(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'signal_sender_key_stores',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(sender_key_name)'],
|
||||
columns: [_column_197, _column_198],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape33 signalSessionStores = Shape33(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'signal_session_stores',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(device_id, name)'],
|
||||
columns: [_column_192, _column_193, _column_199, _column_118],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape34 messageActions = Shape34(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'message_actions',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(message_id, contact_id, type)'],
|
||||
columns: [_column_174, _column_183, _column_144, _column_200],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
late final Shape35 groupHistories = Shape35(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'group_histories',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: ['PRIMARY KEY(group_history_id)'],
|
||||
columns: [
|
||||
_column_201,
|
||||
_column_158,
|
||||
_column_202,
|
||||
_column_203,
|
||||
_column_204,
|
||||
_column_205,
|
||||
_column_206,
|
||||
_column_144,
|
||||
_column_200,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
}
|
||||
|
||||
class Shape38 extends i0.VersionedTable {
|
||||
Shape38({required super.source, required super.alias}) : super.aliased();
|
||||
i1.GeneratedColumn<String> get groupId =>
|
||||
columnsByName['group_id']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<int> get contactId =>
|
||||
columnsByName['contact_id']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<String> get memberState =>
|
||||
columnsByName['member_state']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<i2.Uint8List> get groupPublicKey =>
|
||||
columnsByName['group_public_key']! as i1.GeneratedColumn<i2.Uint8List>;
|
||||
i1.GeneratedColumn<int> get lastChatOpened =>
|
||||
columnsByName['last_chat_opened']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<int> get lastTypeIndicator =>
|
||||
columnsByName['last_type_indicator']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<int> get lastMessage =>
|
||||
columnsByName['last_message']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<int> get createdAt =>
|
||||
columnsByName['created_at']! as i1.GeneratedColumn<int>;
|
||||
}
|
||||
|
||||
i1.GeneratedColumn<int> _column_209(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>(
|
||||
'last_chat_opened',
|
||||
aliasedName,
|
||||
true,
|
||||
type: i1.DriftSqlType.int,
|
||||
$customConstraints: 'NULL',
|
||||
);
|
||||
i1.GeneratedColumn<int> _column_210(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>(
|
||||
'last_type_indicator',
|
||||
aliasedName,
|
||||
true,
|
||||
type: i1.DriftSqlType.int,
|
||||
$customConstraints: 'NULL',
|
||||
);
|
||||
i0.MigrationStepWithVersion migrationSteps({
|
||||
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
|
||||
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
|
||||
|
|
@ -5494,6 +5833,7 @@ i0.MigrationStepWithVersion migrationSteps({
|
|||
required Future<void> Function(i1.Migrator m, Schema8 schema) from7To8,
|
||||
required Future<void> Function(i1.Migrator m, Schema9 schema) from8To9,
|
||||
required Future<void> Function(i1.Migrator m, Schema10 schema) from9To10,
|
||||
required Future<void> Function(i1.Migrator m, Schema11 schema) from10To11,
|
||||
}) {
|
||||
return (currentVersion, database) async {
|
||||
switch (currentVersion) {
|
||||
|
|
@ -5542,6 +5882,11 @@ i0.MigrationStepWithVersion migrationSteps({
|
|||
final migrator = i1.Migrator(database, schema);
|
||||
await from9To10(migrator, schema);
|
||||
return 10;
|
||||
case 10:
|
||||
final schema = Schema11(database: database);
|
||||
final migrator = i1.Migrator(database, schema);
|
||||
await from10To11(migrator, schema);
|
||||
return 11;
|
||||
default:
|
||||
throw ArgumentError.value('Unknown migration from $currentVersion');
|
||||
}
|
||||
|
|
@ -5558,6 +5903,7 @@ i1.OnUpgrade stepByStep({
|
|||
required Future<void> Function(i1.Migrator m, Schema8 schema) from7To8,
|
||||
required Future<void> Function(i1.Migrator m, Schema9 schema) from8To9,
|
||||
required Future<void> Function(i1.Migrator m, Schema10 schema) from9To10,
|
||||
required Future<void> Function(i1.Migrator m, Schema11 schema) from10To11,
|
||||
}) => i0.VersionedSchema.stepByStepHelper(
|
||||
step: migrationSteps(
|
||||
from1To2: from1To2,
|
||||
|
|
@ -5569,5 +5915,6 @@ i1.OnUpgrade stepByStep({
|
|||
from7To8: from7To8,
|
||||
from8To9: from8To9,
|
||||
from9To10: from9To10,
|
||||
from10To11: from10To11,
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -3129,6 +3129,18 @@ abstract class AppLocalizations {
|
|||
/// In en, this message translates to:
|
||||
/// **'Use your phone\'s unlock settings to unlock twonly'**
|
||||
String get unlockTwonlyDesc;
|
||||
|
||||
/// No description provided for @settingsTypingIndication.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Typing Indicators'**
|
||||
String get settingsTypingIndication;
|
||||
|
||||
/// No description provided for @settingsTypingIndicationSubtitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'When the typing indicator is turned off, you can\'t see when others are typing a message.'**
|
||||
String get settingsTypingIndicationSubtitle;
|
||||
}
|
||||
|
||||
class _AppLocalizationsDelegate
|
||||
|
|
|
|||
|
|
@ -1754,4 +1754,11 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
@override
|
||||
String get unlockTwonlyDesc =>
|
||||
'Entsperre twonly über die Sperreinstellungen deines Handys';
|
||||
|
||||
@override
|
||||
String get settingsTypingIndication => 'Tipp-Indikatoren';
|
||||
|
||||
@override
|
||||
String get settingsTypingIndicationSubtitle =>
|
||||
'Bei deaktiviertem Tipp-Indikatoren kannst du nicht sehen, wenn andere gerade eine Nachricht tippen.';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1742,4 +1742,11 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
@override
|
||||
String get unlockTwonlyDesc =>
|
||||
'Use your phone\'s unlock settings to unlock twonly';
|
||||
|
||||
@override
|
||||
String get settingsTypingIndication => 'Typing Indicators';
|
||||
|
||||
@override
|
||||
String get settingsTypingIndicationSubtitle =>
|
||||
'When the typing indicator is turned off, you can\'t see when others are typing a message.';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1742,4 +1742,11 @@ class AppLocalizationsSv extends AppLocalizations {
|
|||
@override
|
||||
String get unlockTwonlyDesc =>
|
||||
'Use your phone\'s unlock settings to unlock twonly';
|
||||
|
||||
@override
|
||||
String get settingsTypingIndication => 'Typing Indicators';
|
||||
|
||||
@override
|
||||
String get settingsTypingIndicationSubtitle =>
|
||||
'When the typing indicator is turned off, you can\'t see when others are typing a message.';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,6 +75,9 @@ class UserData {
|
|||
@JsonKey(defaultValue: false)
|
||||
bool autoStoreAllSendUnlimitedMediaFiles = false;
|
||||
|
||||
@JsonKey(defaultValue: true)
|
||||
bool typingIndicators = true;
|
||||
|
||||
String? lastPlanBallance;
|
||||
String? additionalUserInvites;
|
||||
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ UserData _$UserDataFromJson(Map<String, dynamic> json) =>
|
|||
json['storeMediaFilesInGallery'] as bool? ?? false
|
||||
..autoStoreAllSendUnlimitedMediaFiles =
|
||||
json['autoStoreAllSendUnlimitedMediaFiles'] as bool? ?? false
|
||||
..typingIndicators = json['typingIndicators'] as bool? ?? true
|
||||
..lastPlanBallance = json['lastPlanBallance'] as String?
|
||||
..additionalUserInvites = json['additionalUserInvites'] as String?
|
||||
..tutorialDisplayed = (json['tutorialDisplayed'] as List<dynamic>?)
|
||||
|
|
@ -117,6 +118,7 @@ Map<String, dynamic> _$UserDataToJson(UserData instance) => <String, dynamic>{
|
|||
'storeMediaFilesInGallery': instance.storeMediaFilesInGallery,
|
||||
'autoStoreAllSendUnlimitedMediaFiles':
|
||||
instance.autoStoreAllSendUnlimitedMediaFiles,
|
||||
'typingIndicators': instance.typingIndicators,
|
||||
'lastPlanBallance': instance.lastPlanBallance,
|
||||
'additionalUserInvites': instance.additionalUserInvites,
|
||||
'tutorialDisplayed': instance.tutorialDisplayed,
|
||||
|
|
|
|||
|
|
@ -1692,6 +1692,79 @@ class EncryptedContent_FlameSync extends $pb.GeneratedMessage {
|
|||
void clearForceUpdate() => $_clearField(4);
|
||||
}
|
||||
|
||||
class EncryptedContent_TypingIndicator extends $pb.GeneratedMessage {
|
||||
factory EncryptedContent_TypingIndicator({
|
||||
$core.bool? isTyping,
|
||||
$fixnum.Int64? createdAt,
|
||||
}) {
|
||||
final result = create();
|
||||
if (isTyping != null) result.isTyping = isTyping;
|
||||
if (createdAt != null) result.createdAt = createdAt;
|
||||
return result;
|
||||
}
|
||||
|
||||
EncryptedContent_TypingIndicator._();
|
||||
|
||||
factory EncryptedContent_TypingIndicator.fromBuffer(
|
||||
$core.List<$core.int> data,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(data, registry);
|
||||
factory EncryptedContent_TypingIndicator.fromJson($core.String json,
|
||||
[$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(json, registry);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'EncryptedContent.TypingIndicator',
|
||||
createEmptyInstance: create)
|
||||
..aOB(1, _omitFieldNames ? '' : 'isTyping')
|
||||
..aInt64(2, _omitFieldNames ? '' : 'createdAt')
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
EncryptedContent_TypingIndicator clone() =>
|
||||
EncryptedContent_TypingIndicator()..mergeFromMessage(this);
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
EncryptedContent_TypingIndicator copyWith(
|
||||
void Function(EncryptedContent_TypingIndicator) updates) =>
|
||||
super.copyWith(
|
||||
(message) => updates(message as EncryptedContent_TypingIndicator))
|
||||
as EncryptedContent_TypingIndicator;
|
||||
|
||||
@$core.override
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static EncryptedContent_TypingIndicator create() =>
|
||||
EncryptedContent_TypingIndicator._();
|
||||
@$core.override
|
||||
EncryptedContent_TypingIndicator createEmptyInstance() => create();
|
||||
static $pb.PbList<EncryptedContent_TypingIndicator> createRepeated() =>
|
||||
$pb.PbList<EncryptedContent_TypingIndicator>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static EncryptedContent_TypingIndicator getDefault() => _defaultInstance ??=
|
||||
$pb.GeneratedMessage.$_defaultFor<EncryptedContent_TypingIndicator>(
|
||||
create);
|
||||
static EncryptedContent_TypingIndicator? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool get isTyping => $_getBF(0);
|
||||
@$pb.TagNumber(1)
|
||||
set isTyping($core.bool value) => $_setBool(0, value);
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasIsTyping() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearIsTyping() => $_clearField(1);
|
||||
|
||||
@$pb.TagNumber(2)
|
||||
$fixnum.Int64 get createdAt => $_getI64(1);
|
||||
@$pb.TagNumber(2)
|
||||
set createdAt($fixnum.Int64 value) => $_setInt64(1, value);
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasCreatedAt() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearCreatedAt() => $_clearField(2);
|
||||
}
|
||||
|
||||
class EncryptedContent extends $pb.GeneratedMessage {
|
||||
factory EncryptedContent({
|
||||
$core.String? groupId,
|
||||
|
|
@ -1712,6 +1785,7 @@ class EncryptedContent extends $pb.GeneratedMessage {
|
|||
EncryptedContent_ResendGroupPublicKey? resendGroupPublicKey,
|
||||
EncryptedContent_ErrorMessages? errorMessages,
|
||||
EncryptedContent_AdditionalDataMessage? additionalDataMessage,
|
||||
EncryptedContent_TypingIndicator? typingIndicator,
|
||||
}) {
|
||||
final result = create();
|
||||
if (groupId != null) result.groupId = groupId;
|
||||
|
|
@ -1735,6 +1809,7 @@ class EncryptedContent extends $pb.GeneratedMessage {
|
|||
if (errorMessages != null) result.errorMessages = errorMessages;
|
||||
if (additionalDataMessage != null)
|
||||
result.additionalDataMessage = additionalDataMessage;
|
||||
if (typingIndicator != null) result.typingIndicator = typingIndicator;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -1801,6 +1876,9 @@ class EncryptedContent extends $pb.GeneratedMessage {
|
|||
..aOM<EncryptedContent_AdditionalDataMessage>(
|
||||
19, _omitFieldNames ? '' : 'additionalDataMessage',
|
||||
subBuilder: EncryptedContent_AdditionalDataMessage.create)
|
||||
..aOM<EncryptedContent_TypingIndicator>(
|
||||
20, _omitFieldNames ? '' : 'typingIndicator',
|
||||
subBuilder: EncryptedContent_TypingIndicator.create)
|
||||
..hasRequiredFields = false;
|
||||
|
||||
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
|
||||
|
|
@ -2025,6 +2103,18 @@ class EncryptedContent extends $pb.GeneratedMessage {
|
|||
@$pb.TagNumber(19)
|
||||
EncryptedContent_AdditionalDataMessage ensureAdditionalDataMessage() =>
|
||||
$_ensure(17);
|
||||
|
||||
@$pb.TagNumber(20)
|
||||
EncryptedContent_TypingIndicator get typingIndicator => $_getN(18);
|
||||
@$pb.TagNumber(20)
|
||||
set typingIndicator(EncryptedContent_TypingIndicator value) =>
|
||||
$_setField(20, value);
|
||||
@$pb.TagNumber(20)
|
||||
$core.bool hasTypingIndicator() => $_has(18);
|
||||
@$pb.TagNumber(20)
|
||||
void clearTypingIndicator() => $_clearField(20);
|
||||
@$pb.TagNumber(20)
|
||||
EncryptedContent_TypingIndicator ensureTypingIndicator() => $_ensure(18);
|
||||
}
|
||||
|
||||
const $core.bool _omitFieldNames =
|
||||
|
|
|
|||
|
|
@ -326,6 +326,16 @@ const EncryptedContent$json = {
|
|||
'10': 'additionalDataMessage',
|
||||
'17': true
|
||||
},
|
||||
{
|
||||
'1': 'typing_indicator',
|
||||
'3': 20,
|
||||
'4': 1,
|
||||
'5': 11,
|
||||
'6': '.EncryptedContent.TypingIndicator',
|
||||
'9': 18,
|
||||
'10': 'typingIndicator',
|
||||
'17': true
|
||||
},
|
||||
],
|
||||
'3': [
|
||||
EncryptedContent_ErrorMessages$json,
|
||||
|
|
@ -342,7 +352,8 @@ const EncryptedContent$json = {
|
|||
EncryptedContent_ContactRequest$json,
|
||||
EncryptedContent_ContactUpdate$json,
|
||||
EncryptedContent_PushKeys$json,
|
||||
EncryptedContent_FlameSync$json
|
||||
EncryptedContent_FlameSync$json,
|
||||
EncryptedContent_TypingIndicator$json
|
||||
],
|
||||
'8': [
|
||||
{'1': '_groupId'},
|
||||
|
|
@ -363,6 +374,7 @@ const EncryptedContent$json = {
|
|||
{'1': '_resendGroupPublicKey'},
|
||||
{'1': '_error_messages'},
|
||||
{'1': '_additional_data_message'},
|
||||
{'1': '_typing_indicator'},
|
||||
],
|
||||
};
|
||||
|
||||
|
|
@ -840,6 +852,15 @@ const EncryptedContent_FlameSync$json = {
|
|||
],
|
||||
};
|
||||
|
||||
@$core.Deprecated('Use encryptedContentDescriptor instead')
|
||||
const EncryptedContent_TypingIndicator$json = {
|
||||
'1': 'TypingIndicator',
|
||||
'2': [
|
||||
{'1': 'is_typing', '3': 1, '4': 1, '5': 8, '10': 'isTyping'},
|
||||
{'1': 'created_at', '3': 2, '4': 1, '5': 3, '10': 'createdAt'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `EncryptedContent`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List encryptedContentDescriptor = $convert.base64Decode(
|
||||
'ChBFbmNyeXB0ZWRDb250ZW50Eh0KB2dyb3VwSWQYAiABKAlIAFIHZ3JvdXBJZIgBARInCgxpc0'
|
||||
|
|
@ -864,68 +885,71 @@ final $typed_data.Uint8List encryptedContentDescriptor = $convert.base64Decode(
|
|||
'gBARJLCg5lcnJvcl9tZXNzYWdlcxgSIAEoCzIfLkVuY3J5cHRlZENvbnRlbnQuRXJyb3JNZXNz'
|
||||
'YWdlc0gQUg1lcnJvck1lc3NhZ2VziAEBEmQKF2FkZGl0aW9uYWxfZGF0YV9tZXNzYWdlGBMgAS'
|
||||
'gLMicuRW5jcnlwdGVkQ29udGVudC5BZGRpdGlvbmFsRGF0YU1lc3NhZ2VIEVIVYWRkaXRpb25h'
|
||||
'bERhdGFNZXNzYWdliAEBGvABCg1FcnJvck1lc3NhZ2VzEjgKBHR5cGUYASABKA4yJC5FbmNyeX'
|
||||
'B0ZWRDb250ZW50LkVycm9yTWVzc2FnZXMuVHlwZVIEdHlwZRIsChJyZWxhdGVkX3JlY2VpcHRf'
|
||||
'aWQYAiABKAlSEHJlbGF0ZWRSZWNlaXB0SWQidwoEVHlwZRI8CjhFUlJPUl9QUk9DRVNTSU5HX0'
|
||||
'1FU1NBR0VfQ1JFQVRFRF9BQ0NPVU5UX1JFUVVFU1RfSU5TVEVBRBAAEhgKFFVOS05PV05fTUVT'
|
||||
'U0FHRV9UWVBFEAISFwoTU0VTU0lPTl9PVVRfT0ZfU1lOQxADGlEKC0dyb3VwQ3JlYXRlEhoKCH'
|
||||
'N0YXRlS2V5GAMgASgMUghzdGF0ZUtleRImCg5ncm91cFB1YmxpY0tleRgEIAEoDFIOZ3JvdXBQ'
|
||||
'dWJsaWNLZXkaMwoJR3JvdXBKb2luEiYKDmdyb3VwUHVibGljS2V5GAEgASgMUg5ncm91cFB1Ym'
|
||||
'xpY0tleRoWChRSZXNlbmRHcm91cFB1YmxpY0tleRq2AgoLR3JvdXBVcGRhdGUSKAoPZ3JvdXBB'
|
||||
'Y3Rpb25UeXBlGAEgASgJUg9ncm91cEFjdGlvblR5cGUSMQoRYWZmZWN0ZWRDb250YWN0SWQYAi'
|
||||
'ABKANIAFIRYWZmZWN0ZWRDb250YWN0SWSIAQESJwoMbmV3R3JvdXBOYW1lGAMgASgJSAFSDG5l'
|
||||
'd0dyb3VwTmFtZYgBARJTCiJuZXdEZWxldGVNZXNzYWdlc0FmdGVyTWlsbGlzZWNvbmRzGAQgAS'
|
||||
'gDSAJSIm5ld0RlbGV0ZU1lc3NhZ2VzQWZ0ZXJNaWxsaXNlY29uZHOIAQFCFAoSX2FmZmVjdGVk'
|
||||
'Q29udGFjdElkQg8KDV9uZXdHcm91cE5hbWVCJQojX25ld0RlbGV0ZU1lc3NhZ2VzQWZ0ZXJNaW'
|
||||
'xsaXNlY29uZHMaqQEKC1RleHRNZXNzYWdlEigKD3NlbmRlck1lc3NhZ2VJZBgBIAEoCVIPc2Vu'
|
||||
'ZGVyTWVzc2FnZUlkEhIKBHRleHQYAiABKAlSBHRleHQSHAoJdGltZXN0YW1wGAMgASgDUgl0aW'
|
||||
'1lc3RhbXASKwoOcXVvdGVNZXNzYWdlSWQYBCABKAlIAFIOcXVvdGVNZXNzYWdlSWSIAQFCEQoP'
|
||||
'X3F1b3RlTWVzc2FnZUlkGs4BChVBZGRpdGlvbmFsRGF0YU1lc3NhZ2USKgoRc2VuZGVyX21lc3'
|
||||
'NhZ2VfaWQYASABKAlSD3NlbmRlck1lc3NhZ2VJZBIcCgl0aW1lc3RhbXAYAiABKANSCXRpbWVz'
|
||||
'dGFtcBISCgR0eXBlGAMgASgJUgR0eXBlEjsKF2FkZGl0aW9uYWxfbWVzc2FnZV9kYXRhGAQgAS'
|
||||
'gMSABSFWFkZGl0aW9uYWxNZXNzYWdlRGF0YYgBAUIaChhfYWRkaXRpb25hbF9tZXNzYWdlX2Rh'
|
||||
'dGEaYgoIUmVhY3Rpb24SKAoPdGFyZ2V0TWVzc2FnZUlkGAEgASgJUg90YXJnZXRNZXNzYWdlSW'
|
||||
'QSFAoFZW1vamkYAiABKAlSBWVtb2ppEhYKBnJlbW92ZRgDIAEoCFIGcmVtb3ZlGrcCCg1NZXNz'
|
||||
'YWdlVXBkYXRlEjgKBHR5cGUYASABKA4yJC5FbmNyeXB0ZWRDb250ZW50Lk1lc3NhZ2VVcGRhdG'
|
||||
'UuVHlwZVIEdHlwZRItCg9zZW5kZXJNZXNzYWdlSWQYAiABKAlIAFIPc2VuZGVyTWVzc2FnZUlk'
|
||||
'iAEBEjoKGG11bHRpcGxlVGFyZ2V0TWVzc2FnZUlkcxgDIAMoCVIYbXVsdGlwbGVUYXJnZXRNZX'
|
||||
'NzYWdlSWRzEhcKBHRleHQYBCABKAlIAVIEdGV4dIgBARIcCgl0aW1lc3RhbXAYBSABKANSCXRp'
|
||||
'bWVzdGFtcCItCgRUeXBlEgoKBkRFTEVURRAAEg0KCUVESVRfVEVYVBABEgoKBk9QRU5FRBACQh'
|
||||
'IKEF9zZW5kZXJNZXNzYWdlSWRCBwoFX3RleHQa8AUKBU1lZGlhEigKD3NlbmRlck1lc3NhZ2VJ'
|
||||
'ZBgBIAEoCVIPc2VuZGVyTWVzc2FnZUlkEjAKBHR5cGUYAiABKA4yHC5FbmNyeXB0ZWRDb250ZW'
|
||||
'50Lk1lZGlhLlR5cGVSBHR5cGUSQwoaZGlzcGxheUxpbWl0SW5NaWxsaXNlY29uZHMYAyABKANI'
|
||||
'AFIaZGlzcGxheUxpbWl0SW5NaWxsaXNlY29uZHOIAQESNgoWcmVxdWlyZXNBdXRoZW50aWNhdG'
|
||||
'lvbhgEIAEoCFIWcmVxdWlyZXNBdXRoZW50aWNhdGlvbhIcCgl0aW1lc3RhbXAYBSABKANSCXRp'
|
||||
'bWVzdGFtcBIrCg5xdW90ZU1lc3NhZ2VJZBgGIAEoCUgBUg5xdW90ZU1lc3NhZ2VJZIgBARIpCg'
|
||||
'1kb3dubG9hZFRva2VuGAcgASgMSAJSDWRvd25sb2FkVG9rZW6IAQESKQoNZW5jcnlwdGlvbktl'
|
||||
'eRgIIAEoDEgDUg1lbmNyeXB0aW9uS2V5iAEBEikKDWVuY3J5cHRpb25NYWMYCSABKAxIBFINZW'
|
||||
'5jcnlwdGlvbk1hY4gBARItCg9lbmNyeXB0aW9uTm9uY2UYCiABKAxIBVIPZW5jcnlwdGlvbk5v'
|
||||
'bmNliAEBEjsKF2FkZGl0aW9uYWxfbWVzc2FnZV9kYXRhGAsgASgMSAZSFWFkZGl0aW9uYWxNZX'
|
||||
'NzYWdlRGF0YYgBASI+CgRUeXBlEgwKCFJFVVBMT0FEEAASCQoFSU1BR0UQARIJCgVWSURFTxAC'
|
||||
'EgcKA0dJRhADEgkKBUFVRElPEARCHQobX2Rpc3BsYXlMaW1pdEluTWlsbGlzZWNvbmRzQhEKD1'
|
||||
'9xdW90ZU1lc3NhZ2VJZEIQCg5fZG93bmxvYWRUb2tlbkIQCg5fZW5jcnlwdGlvbktleUIQCg5f'
|
||||
'ZW5jcnlwdGlvbk1hY0ISChBfZW5jcnlwdGlvbk5vbmNlQhoKGF9hZGRpdGlvbmFsX21lc3NhZ2'
|
||||
'VfZGF0YRqnAQoLTWVkaWFVcGRhdGUSNgoEdHlwZRgBIAEoDjIiLkVuY3J5cHRlZENvbnRlbnQu'
|
||||
'TWVkaWFVcGRhdGUuVHlwZVIEdHlwZRIoCg90YXJnZXRNZXNzYWdlSWQYAiABKAlSD3RhcmdldE'
|
||||
'1lc3NhZ2VJZCI2CgRUeXBlEgwKCFJFT1BFTkVEEAASCgoGU1RPUkVEEAESFAoQREVDUllQVElP'
|
||||
'Tl9FUlJPUhACGngKDkNvbnRhY3RSZXF1ZXN0EjkKBHR5cGUYASABKA4yJS5FbmNyeXB0ZWRDb2'
|
||||
'50ZW50LkNvbnRhY3RSZXF1ZXN0LlR5cGVSBHR5cGUiKwoEVHlwZRILCgdSRVFVRVNUEAASCgoG'
|
||||
'UkVKRUNUEAESCgoGQUNDRVBUEAIangIKDUNvbnRhY3RVcGRhdGUSOAoEdHlwZRgBIAEoDjIkLk'
|
||||
'VuY3J5cHRlZENvbnRlbnQuQ29udGFjdFVwZGF0ZS5UeXBlUgR0eXBlEjUKE2F2YXRhclN2Z0Nv'
|
||||
'bXByZXNzZWQYAiABKAxIAFITYXZhdGFyU3ZnQ29tcHJlc3NlZIgBARIfCgh1c2VybmFtZRgDIA'
|
||||
'EoCUgBUgh1c2VybmFtZYgBARIlCgtkaXNwbGF5TmFtZRgEIAEoCUgCUgtkaXNwbGF5TmFtZYgB'
|
||||
'ASIfCgRUeXBlEgsKB1JFUVVFU1QQABIKCgZVUERBVEUQAUIWChRfYXZhdGFyU3ZnQ29tcHJlc3'
|
||||
'NlZEILCglfdXNlcm5hbWVCDgoMX2Rpc3BsYXlOYW1lGtUBCghQdXNoS2V5cxIzCgR0eXBlGAEg'
|
||||
'ASgOMh8uRW5jcnlwdGVkQ29udGVudC5QdXNoS2V5cy5UeXBlUgR0eXBlEhkKBWtleUlkGAIgAS'
|
||||
'gDSABSBWtleUlkiAEBEhUKA2tleRgDIAEoDEgBUgNrZXmIAQESIQoJY3JlYXRlZEF0GAQgASgD'
|
||||
'SAJSCWNyZWF0ZWRBdIgBASIfCgRUeXBlEgsKB1JFUVVFU1QQABIKCgZVUERBVEUQAUIICgZfa2'
|
||||
'V5SWRCBgoEX2tleUIMCgpfY3JlYXRlZEF0GqkBCglGbGFtZVN5bmMSIgoMZmxhbWVDb3VudGVy'
|
||||
'GAEgASgDUgxmbGFtZUNvdW50ZXISNgoWbGFzdEZsYW1lQ291bnRlckNoYW5nZRgCIAEoA1IWbG'
|
||||
'FzdEZsYW1lQ291bnRlckNoYW5nZRIeCgpiZXN0RnJpZW5kGAMgASgIUgpiZXN0RnJpZW5kEiAK'
|
||||
'C2ZvcmNlVXBkYXRlGAQgASgIUgtmb3JjZVVwZGF0ZUIKCghfZ3JvdXBJZEIPCg1faXNEaXJlY3'
|
||||
'RDaGF0QhcKFV9zZW5kZXJQcm9maWxlQ291bnRlckIQCg5fbWVzc2FnZVVwZGF0ZUIICgZfbWVk'
|
||||
'aWFCDgoMX21lZGlhVXBkYXRlQhAKDl9jb250YWN0VXBkYXRlQhEKD19jb250YWN0UmVxdWVzdE'
|
||||
'IMCgpfZmxhbWVTeW5jQgsKCV9wdXNoS2V5c0ILCglfcmVhY3Rpb25CDgoMX3RleHRNZXNzYWdl'
|
||||
'Qg4KDF9ncm91cENyZWF0ZUIMCgpfZ3JvdXBKb2luQg4KDF9ncm91cFVwZGF0ZUIXChVfcmVzZW'
|
||||
'5kR3JvdXBQdWJsaWNLZXlCEQoPX2Vycm9yX21lc3NhZ2VzQhoKGF9hZGRpdGlvbmFsX2RhdGFf'
|
||||
'bWVzc2FnZQ==');
|
||||
'bERhdGFNZXNzYWdliAEBElEKEHR5cGluZ19pbmRpY2F0b3IYFCABKAsyIS5FbmNyeXB0ZWRDb2'
|
||||
'50ZW50LlR5cGluZ0luZGljYXRvckgSUg90eXBpbmdJbmRpY2F0b3KIAQEa8AEKDUVycm9yTWVz'
|
||||
'c2FnZXMSOAoEdHlwZRgBIAEoDjIkLkVuY3J5cHRlZENvbnRlbnQuRXJyb3JNZXNzYWdlcy5UeX'
|
||||
'BlUgR0eXBlEiwKEnJlbGF0ZWRfcmVjZWlwdF9pZBgCIAEoCVIQcmVsYXRlZFJlY2VpcHRJZCJ3'
|
||||
'CgRUeXBlEjwKOEVSUk9SX1BST0NFU1NJTkdfTUVTU0FHRV9DUkVBVEVEX0FDQ09VTlRfUkVRVU'
|
||||
'VTVF9JTlNURUFEEAASGAoUVU5LTk9XTl9NRVNTQUdFX1RZUEUQAhIXChNTRVNTSU9OX09VVF9P'
|
||||
'Rl9TWU5DEAMaUQoLR3JvdXBDcmVhdGUSGgoIc3RhdGVLZXkYAyABKAxSCHN0YXRlS2V5EiYKDm'
|
||||
'dyb3VwUHVibGljS2V5GAQgASgMUg5ncm91cFB1YmxpY0tleRozCglHcm91cEpvaW4SJgoOZ3Jv'
|
||||
'dXBQdWJsaWNLZXkYASABKAxSDmdyb3VwUHVibGljS2V5GhYKFFJlc2VuZEdyb3VwUHVibGljS2'
|
||||
'V5GrYCCgtHcm91cFVwZGF0ZRIoCg9ncm91cEFjdGlvblR5cGUYASABKAlSD2dyb3VwQWN0aW9u'
|
||||
'VHlwZRIxChFhZmZlY3RlZENvbnRhY3RJZBgCIAEoA0gAUhFhZmZlY3RlZENvbnRhY3RJZIgBAR'
|
||||
'InCgxuZXdHcm91cE5hbWUYAyABKAlIAVIMbmV3R3JvdXBOYW1liAEBElMKIm5ld0RlbGV0ZU1l'
|
||||
'c3NhZ2VzQWZ0ZXJNaWxsaXNlY29uZHMYBCABKANIAlIibmV3RGVsZXRlTWVzc2FnZXNBZnRlck'
|
||||
'1pbGxpc2Vjb25kc4gBAUIUChJfYWZmZWN0ZWRDb250YWN0SWRCDwoNX25ld0dyb3VwTmFtZUIl'
|
||||
'CiNfbmV3RGVsZXRlTWVzc2FnZXNBZnRlck1pbGxpc2Vjb25kcxqpAQoLVGV4dE1lc3NhZ2USKA'
|
||||
'oPc2VuZGVyTWVzc2FnZUlkGAEgASgJUg9zZW5kZXJNZXNzYWdlSWQSEgoEdGV4dBgCIAEoCVIE'
|
||||
'dGV4dBIcCgl0aW1lc3RhbXAYAyABKANSCXRpbWVzdGFtcBIrCg5xdW90ZU1lc3NhZ2VJZBgEIA'
|
||||
'EoCUgAUg5xdW90ZU1lc3NhZ2VJZIgBAUIRCg9fcXVvdGVNZXNzYWdlSWQazgEKFUFkZGl0aW9u'
|
||||
'YWxEYXRhTWVzc2FnZRIqChFzZW5kZXJfbWVzc2FnZV9pZBgBIAEoCVIPc2VuZGVyTWVzc2FnZU'
|
||||
'lkEhwKCXRpbWVzdGFtcBgCIAEoA1IJdGltZXN0YW1wEhIKBHR5cGUYAyABKAlSBHR5cGUSOwoX'
|
||||
'YWRkaXRpb25hbF9tZXNzYWdlX2RhdGEYBCABKAxIAFIVYWRkaXRpb25hbE1lc3NhZ2VEYXRhiA'
|
||||
'EBQhoKGF9hZGRpdGlvbmFsX21lc3NhZ2VfZGF0YRpiCghSZWFjdGlvbhIoCg90YXJnZXRNZXNz'
|
||||
'YWdlSWQYASABKAlSD3RhcmdldE1lc3NhZ2VJZBIUCgVlbW9qaRgCIAEoCVIFZW1vamkSFgoGcm'
|
||||
'Vtb3ZlGAMgASgIUgZyZW1vdmUatwIKDU1lc3NhZ2VVcGRhdGUSOAoEdHlwZRgBIAEoDjIkLkVu'
|
||||
'Y3J5cHRlZENvbnRlbnQuTWVzc2FnZVVwZGF0ZS5UeXBlUgR0eXBlEi0KD3NlbmRlck1lc3NhZ2'
|
||||
'VJZBgCIAEoCUgAUg9zZW5kZXJNZXNzYWdlSWSIAQESOgoYbXVsdGlwbGVUYXJnZXRNZXNzYWdl'
|
||||
'SWRzGAMgAygJUhhtdWx0aXBsZVRhcmdldE1lc3NhZ2VJZHMSFwoEdGV4dBgEIAEoCUgBUgR0ZX'
|
||||
'h0iAEBEhwKCXRpbWVzdGFtcBgFIAEoA1IJdGltZXN0YW1wIi0KBFR5cGUSCgoGREVMRVRFEAAS'
|
||||
'DQoJRURJVF9URVhUEAESCgoGT1BFTkVEEAJCEgoQX3NlbmRlck1lc3NhZ2VJZEIHCgVfdGV4dB'
|
||||
'rwBQoFTWVkaWESKAoPc2VuZGVyTWVzc2FnZUlkGAEgASgJUg9zZW5kZXJNZXNzYWdlSWQSMAoE'
|
||||
'dHlwZRgCIAEoDjIcLkVuY3J5cHRlZENvbnRlbnQuTWVkaWEuVHlwZVIEdHlwZRJDChpkaXNwbG'
|
||||
'F5TGltaXRJbk1pbGxpc2Vjb25kcxgDIAEoA0gAUhpkaXNwbGF5TGltaXRJbk1pbGxpc2Vjb25k'
|
||||
'c4gBARI2ChZyZXF1aXJlc0F1dGhlbnRpY2F0aW9uGAQgASgIUhZyZXF1aXJlc0F1dGhlbnRpY2'
|
||||
'F0aW9uEhwKCXRpbWVzdGFtcBgFIAEoA1IJdGltZXN0YW1wEisKDnF1b3RlTWVzc2FnZUlkGAYg'
|
||||
'ASgJSAFSDnF1b3RlTWVzc2FnZUlkiAEBEikKDWRvd25sb2FkVG9rZW4YByABKAxIAlINZG93bm'
|
||||
'xvYWRUb2tlbogBARIpCg1lbmNyeXB0aW9uS2V5GAggASgMSANSDWVuY3J5cHRpb25LZXmIAQES'
|
||||
'KQoNZW5jcnlwdGlvbk1hYxgJIAEoDEgEUg1lbmNyeXB0aW9uTWFjiAEBEi0KD2VuY3J5cHRpb2'
|
||||
'5Ob25jZRgKIAEoDEgFUg9lbmNyeXB0aW9uTm9uY2WIAQESOwoXYWRkaXRpb25hbF9tZXNzYWdl'
|
||||
'X2RhdGEYCyABKAxIBlIVYWRkaXRpb25hbE1lc3NhZ2VEYXRhiAEBIj4KBFR5cGUSDAoIUkVVUE'
|
||||
'xPQUQQABIJCgVJTUFHRRABEgkKBVZJREVPEAISBwoDR0lGEAMSCQoFQVVESU8QBEIdChtfZGlz'
|
||||
'cGxheUxpbWl0SW5NaWxsaXNlY29uZHNCEQoPX3F1b3RlTWVzc2FnZUlkQhAKDl9kb3dubG9hZF'
|
||||
'Rva2VuQhAKDl9lbmNyeXB0aW9uS2V5QhAKDl9lbmNyeXB0aW9uTWFjQhIKEF9lbmNyeXB0aW9u'
|
||||
'Tm9uY2VCGgoYX2FkZGl0aW9uYWxfbWVzc2FnZV9kYXRhGqcBCgtNZWRpYVVwZGF0ZRI2CgR0eX'
|
||||
'BlGAEgASgOMiIuRW5jcnlwdGVkQ29udGVudC5NZWRpYVVwZGF0ZS5UeXBlUgR0eXBlEigKD3Rh'
|
||||
'cmdldE1lc3NhZ2VJZBgCIAEoCVIPdGFyZ2V0TWVzc2FnZUlkIjYKBFR5cGUSDAoIUkVPUEVORU'
|
||||
'QQABIKCgZTVE9SRUQQARIUChBERUNSWVBUSU9OX0VSUk9SEAIaeAoOQ29udGFjdFJlcXVlc3QS'
|
||||
'OQoEdHlwZRgBIAEoDjIlLkVuY3J5cHRlZENvbnRlbnQuQ29udGFjdFJlcXVlc3QuVHlwZVIEdH'
|
||||
'lwZSIrCgRUeXBlEgsKB1JFUVVFU1QQABIKCgZSRUpFQ1QQARIKCgZBQ0NFUFQQAhqeAgoNQ29u'
|
||||
'dGFjdFVwZGF0ZRI4CgR0eXBlGAEgASgOMiQuRW5jcnlwdGVkQ29udGVudC5Db250YWN0VXBkYX'
|
||||
'RlLlR5cGVSBHR5cGUSNQoTYXZhdGFyU3ZnQ29tcHJlc3NlZBgCIAEoDEgAUhNhdmF0YXJTdmdD'
|
||||
'b21wcmVzc2VkiAEBEh8KCHVzZXJuYW1lGAMgASgJSAFSCHVzZXJuYW1liAEBEiUKC2Rpc3BsYX'
|
||||
'lOYW1lGAQgASgJSAJSC2Rpc3BsYXlOYW1liAEBIh8KBFR5cGUSCwoHUkVRVUVTVBAAEgoKBlVQ'
|
||||
'REFURRABQhYKFF9hdmF0YXJTdmdDb21wcmVzc2VkQgsKCV91c2VybmFtZUIOCgxfZGlzcGxheU'
|
||||
'5hbWUa1QEKCFB1c2hLZXlzEjMKBHR5cGUYASABKA4yHy5FbmNyeXB0ZWRDb250ZW50LlB1c2hL'
|
||||
'ZXlzLlR5cGVSBHR5cGUSGQoFa2V5SWQYAiABKANIAFIFa2V5SWSIAQESFQoDa2V5GAMgASgMSA'
|
||||
'FSA2tleYgBARIhCgljcmVhdGVkQXQYBCABKANIAlIJY3JlYXRlZEF0iAEBIh8KBFR5cGUSCwoH'
|
||||
'UkVRVUVTVBAAEgoKBlVQREFURRABQggKBl9rZXlJZEIGCgRfa2V5QgwKCl9jcmVhdGVkQXQaqQ'
|
||||
'EKCUZsYW1lU3luYxIiCgxmbGFtZUNvdW50ZXIYASABKANSDGZsYW1lQ291bnRlchI2ChZsYXN0'
|
||||
'RmxhbWVDb3VudGVyQ2hhbmdlGAIgASgDUhZsYXN0RmxhbWVDb3VudGVyQ2hhbmdlEh4KCmJlc3'
|
||||
'RGcmllbmQYAyABKAhSCmJlc3RGcmllbmQSIAoLZm9yY2VVcGRhdGUYBCABKAhSC2ZvcmNlVXBk'
|
||||
'YXRlGk0KD1R5cGluZ0luZGljYXRvchIbCglpc190eXBpbmcYASABKAhSCGlzVHlwaW5nEh0KCm'
|
||||
'NyZWF0ZWRfYXQYAiABKANSCWNyZWF0ZWRBdEIKCghfZ3JvdXBJZEIPCg1faXNEaXJlY3RDaGF0'
|
||||
'QhcKFV9zZW5kZXJQcm9maWxlQ291bnRlckIQCg5fbWVzc2FnZVVwZGF0ZUIICgZfbWVkaWFCDg'
|
||||
'oMX21lZGlhVXBkYXRlQhAKDl9jb250YWN0VXBkYXRlQhEKD19jb250YWN0UmVxdWVzdEIMCgpf'
|
||||
'ZmxhbWVTeW5jQgsKCV9wdXNoS2V5c0ILCglfcmVhY3Rpb25CDgoMX3RleHRNZXNzYWdlQg4KDF'
|
||||
'9ncm91cENyZWF0ZUIMCgpfZ3JvdXBKb2luQg4KDF9ncm91cFVwZGF0ZUIXChVfcmVzZW5kR3Jv'
|
||||
'dXBQdWJsaWNLZXlCEQoPX2Vycm9yX21lc3NhZ2VzQhoKGF9hZGRpdGlvbmFsX2RhdGFfbWVzc2'
|
||||
'FnZUITChFfdHlwaW5nX2luZGljYXRvcg==');
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ message EncryptedContent {
|
|||
optional ResendGroupPublicKey resendGroupPublicKey = 17;
|
||||
optional ErrorMessages error_messages = 18;
|
||||
optional AdditionalDataMessage additional_data_message = 19;
|
||||
optional TypingIndicator typing_indicator = 20;
|
||||
|
||||
message ErrorMessages {
|
||||
enum Type {
|
||||
|
|
@ -194,4 +195,9 @@ message EncryptedContent {
|
|||
bool forceUpdate = 4;
|
||||
}
|
||||
|
||||
message TypingIndicator {
|
||||
bool is_typing = 1;
|
||||
int64 created_at = 2;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import 'package:twonly/src/database/tables/groups.table.dart';
|
|||
import 'package:twonly/src/database/twonly.db.dart';
|
||||
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
||||
import 'package:twonly/src/services/api/messages.dart';
|
||||
import 'package:twonly/src/services/api/utils.dart';
|
||||
import 'package:twonly/src/services/group.services.dart';
|
||||
import 'package:twonly/src/utils/log.dart';
|
||||
|
||||
|
|
@ -137,8 +138,9 @@ Future<void> handleGroupUpdate(
|
|||
GroupHistoriesCompanion(
|
||||
groupId: Value(groupId),
|
||||
type: Value(actionType),
|
||||
newDeleteMessagesAfterMilliseconds:
|
||||
Value(update.newDeleteMessagesAfterMilliseconds.toInt()),
|
||||
newDeleteMessagesAfterMilliseconds: Value(
|
||||
update.newDeleteMessagesAfterMilliseconds.toInt(),
|
||||
),
|
||||
contactId: Value(fromUserId),
|
||||
),
|
||||
);
|
||||
|
|
@ -146,8 +148,9 @@ Future<void> handleGroupUpdate(
|
|||
await twonlyDB.groupsDao.updateGroup(
|
||||
group.groupId,
|
||||
GroupsCompanion(
|
||||
deleteMessagesAfterMilliseconds:
|
||||
Value(update.newDeleteMessagesAfterMilliseconds.toInt()),
|
||||
deleteMessagesAfterMilliseconds: Value(
|
||||
update.newDeleteMessagesAfterMilliseconds.toInt(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -221,3 +224,24 @@ Future<void> handleResendGroupPublicKey(
|
|||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> handleTypingIndicator(
|
||||
int fromUserId,
|
||||
String groupId,
|
||||
EncryptedContent_TypingIndicator indicator,
|
||||
) async {
|
||||
var lastTypeIndicator = const Value<DateTime?>.absent();
|
||||
|
||||
if (indicator.isTyping) {
|
||||
lastTypeIndicator = Value(fromTimestamp(indicator.createdAt));
|
||||
}
|
||||
|
||||
await twonlyDB.groupsDao.updateMember(
|
||||
groupId,
|
||||
fromUserId,
|
||||
GroupMembersCompanion(
|
||||
lastChatOpened: Value(fromTimestamp(indicator.createdAt)),
|
||||
lastTypeIndicator: lastTypeIndicator,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -300,6 +300,7 @@ Future<void> sendCipherTextToGroup(
|
|||
String groupId,
|
||||
pb.EncryptedContent encryptedContent, {
|
||||
String? messageId,
|
||||
bool onlySendIfNoReceiptsAreOpen = false,
|
||||
}) async {
|
||||
final groupMembers = await twonlyDB.groupsDao.getGroupNonLeftMembers(groupId);
|
||||
|
||||
|
|
@ -313,6 +314,7 @@ Future<void> sendCipherTextToGroup(
|
|||
encryptedContent,
|
||||
messageId: messageId,
|
||||
blocking: false,
|
||||
onlySendIfNoReceiptsAreOpen: onlySendIfNoReceiptsAreOpen,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -323,7 +325,17 @@ Future<(Uint8List, Uint8List?)?> sendCipherText(
|
|||
bool onlyReturnEncryptedData = false,
|
||||
bool blocking = true,
|
||||
String? messageId,
|
||||
bool onlySendIfNoReceiptsAreOpen = false,
|
||||
}) async {
|
||||
if (onlySendIfNoReceiptsAreOpen) {
|
||||
if (await twonlyDB.receiptsDao.getReceiptCountForContact(
|
||||
contactId,
|
||||
) >
|
||||
0) {
|
||||
// this prevents that this message is send in case the receiver is not online
|
||||
return null;
|
||||
}
|
||||
}
|
||||
encryptedContent.senderProfileCounter = Int64(gUser.avatarCounter);
|
||||
|
||||
final response = pb.Message()
|
||||
|
|
@ -353,6 +365,20 @@ Future<(Uint8List, Uint8List?)?> sendCipherText(
|
|||
return null;
|
||||
}
|
||||
|
||||
Future<void> sendTypingIndication(String groupId, bool isTyping) async {
|
||||
if (!gUser.typingIndicators) return;
|
||||
await sendCipherTextToGroup(
|
||||
groupId,
|
||||
pb.EncryptedContent(
|
||||
typingIndicator: pb.EncryptedContent_TypingIndicator(
|
||||
isTyping: isTyping,
|
||||
createdAt: Int64(clock.now().millisecondsSinceEpoch),
|
||||
),
|
||||
),
|
||||
onlySendIfNoReceiptsAreOpen: true,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> notifyContactAboutOpeningMessage(
|
||||
int contactId,
|
||||
List<String> messageOtherIds,
|
||||
|
|
|
|||
|
|
@ -83,7 +83,6 @@ Future<void> handleClient2ClientMessage(NewMessage newMessage) async {
|
|||
|
||||
final isDuplicated = await protectReceiptCheck.protect(() async {
|
||||
if (await twonlyDB.receiptsDao.isDuplicated(receiptId)) {
|
||||
Log.warn('Got duplicated message from the server.');
|
||||
return true;
|
||||
}
|
||||
await twonlyDB.receiptsDao.gotReceipt(receiptId);
|
||||
|
|
@ -450,5 +449,13 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
|
|||
return (null, null);
|
||||
}
|
||||
|
||||
if (content.hasTypingIndicator()) {
|
||||
await handleTypingIndicator(
|
||||
fromUserId,
|
||||
content.groupId,
|
||||
content.typingIndicator,
|
||||
);
|
||||
}
|
||||
|
||||
return (null, null);
|
||||
}
|
||||
|
|
|
|||
6
lib/src/themes/colors.dart
Normal file
6
lib/src/themes/colors.dart
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import 'dart:ui';
|
||||
|
||||
class DefaultColors {
|
||||
static const messageSelf = Color.fromARGB(255, 58, 136, 102);
|
||||
static const messageOther = Color.fromARGB(233, 68, 137, 255);
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@ import 'package:twonly/src/database/twonly.db.dart';
|
|||
import 'package:twonly/src/model/memory_item.model.dart';
|
||||
import 'package:twonly/src/services/api/messages.dart';
|
||||
import 'package:twonly/src/services/notifications/background.notifications.dart';
|
||||
import 'package:twonly/src/themes/colors.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/blink.component.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/chat_group_action.dart';
|
||||
|
|
@ -21,11 +22,11 @@ import 'package:twonly/src/views/chats/chat_messages_components/chat_list_entry.
|
|||
import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_date_chip.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/message_input.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/response_container.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/typing_indicator.dart';
|
||||
import 'package:twonly/src/views/components/avatar_icon.component.dart';
|
||||
import 'package:twonly/src/views/components/flame.dart';
|
||||
import 'package:twonly/src/views/components/verified_shield.dart';
|
||||
|
||||
/// Displays detailed information about a SampleItem.
|
||||
class ChatMessagesView extends StatefulWidget {
|
||||
const ChatMessagesView(this.groupId, {super.key});
|
||||
|
||||
|
|
@ -57,6 +58,8 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
|||
int? focusedScrollItem;
|
||||
bool _receiverDeletedAccount = false;
|
||||
|
||||
Timer? _nextTypingIndicator;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
|
@ -70,6 +73,7 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
|||
messageSub.cancel();
|
||||
contactSub?.cancel();
|
||||
groupActionsSub?.cancel();
|
||||
_nextTypingIndicator?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
|
@ -117,6 +121,15 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
|||
if (groupContacts.length == 1) {
|
||||
_receiverDeletedAccount = groupContacts.first.accountDeleted;
|
||||
}
|
||||
|
||||
if (gUser.typingIndicators) {
|
||||
unawaited(sendTypingIndication(widget.groupId, false));
|
||||
_nextTypingIndicator = Timer.periodic(const Duration(seconds: 8), (
|
||||
_,
|
||||
) async {
|
||||
await sendTypingIndication(widget.groupId, false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setMessages(
|
||||
|
|
@ -270,9 +283,15 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
|||
Expanded(
|
||||
child: ScrollablePositionedList.builder(
|
||||
reverse: true,
|
||||
itemCount: messages.length + 1,
|
||||
itemCount: messages.length + 1 + 1,
|
||||
itemScrollController: itemScrollController,
|
||||
itemBuilder: (context, i) {
|
||||
if (i == 0) {
|
||||
return gUser.typingIndicators
|
||||
? TypingIndicator(group: group)
|
||||
: Container();
|
||||
}
|
||||
i -= 1;
|
||||
if (i == messages.length) {
|
||||
return const Padding(
|
||||
padding: EdgeInsetsGeometry.only(top: 10),
|
||||
|
|
@ -344,6 +363,7 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
|||
],
|
||||
),
|
||||
),
|
||||
|
||||
if (!group.leftGroup && !_receiverDeletedAccount)
|
||||
MessageInput(
|
||||
group: group,
|
||||
|
|
@ -365,10 +385,8 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
|
|||
}
|
||||
}
|
||||
|
||||
Color getMessageColor(Message message) {
|
||||
return (message.senderId == null)
|
||||
? const Color.fromARGB(255, 58, 136, 102)
|
||||
: const Color.fromARGB(233, 68, 137, 255);
|
||||
Color getMessageColor(bool isOther) {
|
||||
return isOther ? DefaultColors.messageSelf : DefaultColors.messageOther;
|
||||
}
|
||||
|
||||
class ChatItem {
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ BubbleInfo getBubbleInfo(
|
|||
final info = BubbleInfo()
|
||||
..text = message.content ?? ''
|
||||
..textColor = Colors.white
|
||||
..color = getMessageColor(message)
|
||||
..color = getMessageColor(message.senderId != null)
|
||||
..displayTime = !combineTextMessageWithNext(message, nextMessage)
|
||||
..displayUserName = '';
|
||||
|
||||
|
|
@ -35,12 +35,14 @@ BubbleInfo getBubbleInfo(
|
|||
userIdToContact != null &&
|
||||
userIdToContact[message.senderId] != null) {
|
||||
if (prevMessage == null) {
|
||||
info.displayUserName =
|
||||
getContactDisplayName(userIdToContact[message.senderId]!);
|
||||
info.displayUserName = getContactDisplayName(
|
||||
userIdToContact[message.senderId]!,
|
||||
);
|
||||
} else {
|
||||
if (!combineTextMessageWithNext(prevMessage, message)) {
|
||||
info.displayUserName =
|
||||
getContactDisplayName(userIdToContact[message.senderId]!);
|
||||
info.displayUserName = getContactDisplayName(
|
||||
userIdToContact[message.senderId]!,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -50,7 +52,7 @@ BubbleInfo getBubbleInfo(
|
|||
|
||||
info.expanded = false;
|
||||
if (message.quotesMessageId == null) {
|
||||
info.color = getMessageColor(message);
|
||||
info.color = getMessageColor(message.senderId != null);
|
||||
}
|
||||
if (message.isDeletedFromSender) {
|
||||
info
|
||||
|
|
@ -88,8 +90,9 @@ bool combineTextMessageWithNext(Message message, Message? nextMessage) {
|
|||
if (nextMessage.type == MessageType.text.name &&
|
||||
message.type == MessageType.text.name) {
|
||||
if (!EmojiAnimation.supported(nextMessage.content!)) {
|
||||
final diff =
|
||||
nextMessage.createdAt.difference(message.createdAt).inMinutes;
|
||||
final diff = nextMessage.createdAt
|
||||
.difference(message.createdAt)
|
||||
.inMinutes;
|
||||
if (diff <= 1) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ class _MessageInputState extends State<MessageInput> {
|
|||
double _cancelSlideOffset = 0;
|
||||
Offset _recordingOffset = Offset.zero;
|
||||
RecordingState _recordingState = RecordingState.none;
|
||||
Timer? _nextTypingIndicator;
|
||||
|
||||
Future<void> _sendMessage() async {
|
||||
if (_textFieldController.text == '') return;
|
||||
|
|
@ -71,6 +72,15 @@ class _MessageInputState extends State<MessageInput> {
|
|||
_textFieldController.text = widget.group.draftMessage!;
|
||||
}
|
||||
widget.textFieldFocus.addListener(_handleTextFocusChange);
|
||||
if (gUser.typingIndicators) {
|
||||
_nextTypingIndicator = Timer.periodic(const Duration(seconds: 5), (
|
||||
_,
|
||||
) async {
|
||||
if (widget.textFieldFocus.hasFocus) {
|
||||
await sendTypingIndication(widget.group.groupId, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
_initializeControllers();
|
||||
}
|
||||
|
||||
|
|
@ -79,6 +89,7 @@ class _MessageInputState extends State<MessageInput> {
|
|||
widget.textFieldFocus.removeListener(_handleTextFocusChange);
|
||||
widget.textFieldFocus.dispose();
|
||||
recorderController.dispose();
|
||||
_nextTypingIndicator?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
|
@ -250,8 +261,9 @@ class _MessageInputState extends State<MessageInput> {
|
|||
await twonlyDB.groupsDao.updateGroup(
|
||||
widget.group.groupId,
|
||||
GroupsCompanion(
|
||||
draftMessage:
|
||||
Value(_textFieldController.text),
|
||||
draftMessage: Value(
|
||||
_textFieldController.text,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
|
@ -362,10 +374,12 @@ class _MessageInputState extends State<MessageInput> {
|
|||
}
|
||||
|
||||
setState(() {
|
||||
final a = _recordingOffset.dx -
|
||||
final a =
|
||||
_recordingOffset.dx -
|
||||
details.localPosition.dx;
|
||||
if (a > 0 && a <= 90) {
|
||||
_cancelSlideOffset = _recordingOffset.dx -
|
||||
_cancelSlideOffset =
|
||||
_recordingOffset.dx -
|
||||
details.localPosition.dx;
|
||||
}
|
||||
});
|
||||
|
|
@ -448,7 +462,8 @@ class _MessageInputState extends State<MessageInput> {
|
|||
),
|
||||
child: FaIcon(
|
||||
size: 20,
|
||||
color: (_recordingState ==
|
||||
color:
|
||||
(_recordingState ==
|
||||
RecordingState.recording)
|
||||
? Colors.white
|
||||
: null,
|
||||
|
|
@ -475,8 +490,9 @@ class _MessageInputState extends State<MessageInput> {
|
|||
color: context.color.primary,
|
||||
FontAwesomeIcons.solidPaperPlane,
|
||||
),
|
||||
onPressed:
|
||||
_audioRecordingLock ? _stopAudioRecording : _sendMessage,
|
||||
onPressed: _audioRecordingLock
|
||||
? _stopAudioRecording
|
||||
: _sendMessage,
|
||||
)
|
||||
else
|
||||
IconButton(
|
||||
|
|
@ -505,8 +521,9 @@ class _MessageInputState extends State<MessageInput> {
|
|||
// middle: EmojiPickerItem.emojiView,
|
||||
bottom: EmojiPickerItem.categoryBar,
|
||||
),
|
||||
emojiTextStyle:
|
||||
TextStyle(fontSize: 24 * (Platform.isIOS ? 1.2 : 1)),
|
||||
emojiTextStyle: TextStyle(
|
||||
fontSize: 24 * (Platform.isIOS ? 1.2 : 1),
|
||||
),
|
||||
emojiViewConfig: EmojiViewConfig(
|
||||
backgroundColor: context.color.surfaceContainer,
|
||||
recentsLimit: 40,
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ class _ResponseContainerState extends State<ResponseContainer> {
|
|||
maxWidth: MediaQuery.of(context).size.width * 0.8,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: getMessageColor(widget.msg),
|
||||
color: getMessageColor(widget.msg.senderId != null),
|
||||
borderRadius: widget.borderRadius,
|
||||
),
|
||||
child: Column(
|
||||
|
|
@ -192,7 +192,7 @@ class _ResponsePreviewState extends State<ResponsePreview> {
|
|||
// _username = _message!.senderId.toString();
|
||||
}
|
||||
|
||||
color = getMessageColor(_message!);
|
||||
color = getMessageColor(_message!.senderId != null);
|
||||
|
||||
if (!_message!.mediaStored) {
|
||||
return Container(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,200 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/constants/routes.keys.dart';
|
||||
import 'package:twonly/src/database/twonly.db.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages.view.dart';
|
||||
import 'package:twonly/src/views/components/avatar_icon.component.dart';
|
||||
|
||||
class TypingIndicator extends StatefulWidget {
|
||||
const TypingIndicator({required this.group, super.key});
|
||||
|
||||
final Group group;
|
||||
|
||||
@override
|
||||
State<TypingIndicator> createState() => _TypingIndicatorState();
|
||||
}
|
||||
|
||||
class _TypingIndicatorState extends State<TypingIndicator>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late AnimationController _controller;
|
||||
late List<Animation<double>> _animations;
|
||||
|
||||
List<GroupMember> _groupMembers = [];
|
||||
|
||||
late StreamSubscription<List<(Contact, GroupMember)>> membersSub;
|
||||
|
||||
late Timer _periodicUpdate;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_periodicUpdate = Timer.periodic(const Duration(seconds: 1), (_) {
|
||||
filterOpenUsers(_groupMembers);
|
||||
});
|
||||
|
||||
final membersStream = twonlyDB.groupsDao.watchGroupMembers(
|
||||
widget.group.groupId,
|
||||
);
|
||||
membersSub = membersStream.listen((update) {
|
||||
filterOpenUsers(update.map((m) => m.$2).toList());
|
||||
});
|
||||
|
||||
_controller = AnimationController(
|
||||
duration: const Duration(milliseconds: 1000),
|
||||
vsync: this,
|
||||
)..repeat();
|
||||
|
||||
_animations = List.generate(3, (index) {
|
||||
final start = index * 0.2;
|
||||
final end = start + 0.6;
|
||||
|
||||
return TweenSequence<double>([
|
||||
// First half: Animate from 0.5 to 1.0
|
||||
TweenSequenceItem(
|
||||
tween: Tween<double>(
|
||||
begin: 0.5,
|
||||
end: 1,
|
||||
).chain(CurveTween(curve: Curves.easeInOut)),
|
||||
weight: 50,
|
||||
),
|
||||
// Second half: Animate back from 1.0 to 0.5
|
||||
TweenSequenceItem(
|
||||
tween: Tween<double>(
|
||||
begin: 1,
|
||||
end: 0.5,
|
||||
).chain(CurveTween(curve: Curves.easeInOut)),
|
||||
weight: 50,
|
||||
),
|
||||
]).animate(
|
||||
CurvedAnimation(
|
||||
parent: _controller,
|
||||
curve: Interval(start, end),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void filterOpenUsers(List<GroupMember> input) {
|
||||
setState(() {
|
||||
_groupMembers = input.where(hasChatOpen).toList();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
membersSub.cancel();
|
||||
_periodicUpdate.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
bool isTyping(GroupMember member) {
|
||||
return member.lastTypeIndicator != null &&
|
||||
DateTime.now()
|
||||
.difference(
|
||||
member.lastTypeIndicator!,
|
||||
)
|
||||
.inSeconds <=
|
||||
12;
|
||||
}
|
||||
|
||||
bool hasChatOpen(GroupMember member) {
|
||||
return member.lastChatOpened != null &&
|
||||
DateTime.now()
|
||||
.difference(
|
||||
member.lastChatOpened!,
|
||||
)
|
||||
.inSeconds <=
|
||||
12;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_groupMembers.isEmpty) return Container();
|
||||
|
||||
return Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Column(
|
||||
children: _groupMembers
|
||||
.map(
|
||||
(member) => Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (!widget.group.isDirectChat)
|
||||
GestureDetector(
|
||||
onTap: () => context.push(
|
||||
Routes.profileContact(member.contactId),
|
||||
),
|
||||
child: AvatarIcon(
|
||||
contactId: member.contactId,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: getMessageColor(true),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: List.generate(
|
||||
3,
|
||||
(index) => _AnimatedDot(
|
||||
isTyping: isTyping(member),
|
||||
animation: _animations[index],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(child: Container()),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AnimatedDot extends AnimatedWidget {
|
||||
const _AnimatedDot({
|
||||
required this.isTyping,
|
||||
required Animation<double> animation,
|
||||
}) : super(listenable: animation);
|
||||
|
||||
final bool isTyping;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final animation = listenable as Animation<double>;
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 2),
|
||||
child: Opacity(
|
||||
opacity: isTyping ? animation.value : 0.5,
|
||||
child: Transform.scale(
|
||||
scale: isTyping ? 1 + (0.5 * animation.value) : 1,
|
||||
child: Container(
|
||||
width: 8,
|
||||
height: 8,
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -63,8 +63,9 @@ class _RetransmissionDataViewState extends State<RetransmissionDataView> {
|
|||
}
|
||||
|
||||
Future<void> initAsync() async {
|
||||
subscriptionContacts =
|
||||
twonlyDB.contactsDao.watchAllContacts().listen((updated) {
|
||||
subscriptionContacts = twonlyDB.contactsDao.watchAllContacts().listen((
|
||||
updated,
|
||||
) {
|
||||
for (final contact in updated) {
|
||||
contacts[contact.userId] = contact;
|
||||
}
|
||||
|
|
@ -73,8 +74,9 @@ class _RetransmissionDataViewState extends State<RetransmissionDataView> {
|
|||
}
|
||||
setState(() {});
|
||||
});
|
||||
subscriptionRetransmission =
|
||||
twonlyDB.receiptsDao.watchAll().listen((updated) {
|
||||
subscriptionRetransmission = twonlyDB.receiptsDao.watchAll().listen((
|
||||
updated,
|
||||
) {
|
||||
retransmissions = updated;
|
||||
if (contacts.isNotEmpty) {
|
||||
messages = RetransMsg.fromRaw(retransmissions, contacts);
|
||||
|
|
@ -93,6 +95,7 @@ class _RetransmissionDataViewState extends State<RetransmissionDataView> {
|
|||
children: [
|
||||
Expanded(
|
||||
child: ListView(
|
||||
reverse: true,
|
||||
children: messages
|
||||
.map(
|
||||
(retrans) => ListTile(
|
||||
|
|
@ -118,8 +121,9 @@ class _RetransmissionDataViewState extends State<RetransmissionDataView> {
|
|||
retrans.receipt.contactId,
|
||||
retrans.receipt.messageId,
|
||||
pb.EncryptedContent.fromBuffer(
|
||||
pb.Message.fromBuffer(retrans.receipt.message)
|
||||
.encryptedContent,
|
||||
pb.Message.fromBuffer(
|
||||
retrans.receipt.message,
|
||||
).encryptedContent,
|
||||
),
|
||||
),
|
||||
builder: (d, a) {
|
||||
|
|
|
|||
|
|
@ -32,6 +32,14 @@ class _PrivacyViewState extends State<PrivacyView> {
|
|||
setState(() {});
|
||||
}
|
||||
|
||||
Future<void> toggleTypingIndicators() async {
|
||||
await updateUserdata((u) {
|
||||
u.typingIndicators = !u.typingIndicators;
|
||||
return u;
|
||||
});
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
|
|
@ -58,6 +66,24 @@ class _PrivacyViewState extends State<PrivacyView> {
|
|||
),
|
||||
onTap: () => context.push(Routes.settingsPrivacyBlockUsers),
|
||||
),
|
||||
ListTile(
|
||||
title: Text(context.lang.contactVerifyNumberTitle),
|
||||
onTap: () async {
|
||||
await context.push(Routes.settingsPublicProfile);
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
const Divider(),
|
||||
ListTile(
|
||||
title: Text(context.lang.settingsTypingIndication),
|
||||
subtitle: Text(context.lang.settingsTypingIndicationSubtitle),
|
||||
onTap: toggleTypingIndicators,
|
||||
trailing: Switch(
|
||||
value: gUser.typingIndicators,
|
||||
onChanged: (a) => toggleTypingIndicators(),
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
ListTile(
|
||||
title: Text(context.lang.settingsScreenLock),
|
||||
subtitle: Text(context.lang.settingsScreenLockSubtitle),
|
||||
|
|
@ -67,13 +93,6 @@ class _PrivacyViewState extends State<PrivacyView> {
|
|||
onChanged: (a) => toggleAuthRequirementOnStartup(),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: Text(context.lang.contactVerifyNumberTitle),
|
||||
onTap: () async {
|
||||
await context.push(Routes.settingsPublicProfile);
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ class _UnlockTwonlyViewState extends State<UnlockTwonlyView> {
|
|||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// _unlockTwonly();
|
||||
_unlockTwonly();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import 'schema_v7.dart' as v7;
|
|||
import 'schema_v8.dart' as v8;
|
||||
import 'schema_v9.dart' as v9;
|
||||
import 'schema_v10.dart' as v10;
|
||||
import 'schema_v11.dart' as v11;
|
||||
|
||||
class GeneratedHelper implements SchemaInstantiationHelper {
|
||||
@override
|
||||
|
|
@ -39,10 +40,12 @@ class GeneratedHelper implements SchemaInstantiationHelper {
|
|||
return v9.DatabaseAtV9(db);
|
||||
case 10:
|
||||
return v10.DatabaseAtV10(db);
|
||||
case 11:
|
||||
return v11.DatabaseAtV11(db);
|
||||
default:
|
||||
throw MissingSchemaException(version, versions);
|
||||
}
|
||||
}
|
||||
|
||||
static const versions = const [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
static const versions = const [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
|
||||
}
|
||||
|
|
|
|||
7406
test/drift/twonly_db/generated/schema_v11.dart
Normal file
7406
test/drift/twonly_db/generated/schema_v11.dart
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue