mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-05-24 23:52:11 +00:00
first working state
This commit is contained in:
parent
93d5f682fc
commit
693c74df46
34 changed files with 14259 additions and 326 deletions
|
|
@ -16,9 +16,11 @@ analyzer:
|
||||||
- "lib/src/model/protobuf/**"
|
- "lib/src/model/protobuf/**"
|
||||||
- "lib/src/model/protobuf/api/websocket/**"
|
- "lib/src/model/protobuf/api/websocket/**"
|
||||||
- "lib/generated/**"
|
- "lib/generated/**"
|
||||||
|
- "lib/core/**"
|
||||||
|
- "lib/src/localization/**"
|
||||||
- "dependencies/**"
|
- "dependencies/**"
|
||||||
- "pubspec.yaml"
|
- "pubspec.yaml"
|
||||||
- "*.arb"
|
- "**.arb"
|
||||||
- "test/drift/**"
|
- "test/drift/**"
|
||||||
- "**.g.dart"
|
- "**.g.dart"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,31 @@
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:integration_test/integration_test.dart';
|
import 'package:integration_test/integration_test.dart';
|
||||||
import 'package:twonly/core/frb_generated.dart';
|
import 'package:twonly/core/frb_generated.dart';
|
||||||
|
import 'package:twonly/globals.dart';
|
||||||
|
import 'package:twonly/src/services/background/callback_dispatcher.background.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||||
setUpAll(() async => RustLib.init());
|
setUpAll(() async => RustLib.init());
|
||||||
// testWidgets('Can call rust function', (tester) async {
|
|
||||||
// await tester.pumpWidget(const MyApp());
|
test('Can initialize twonlyDB and connect to api server', () async {
|
||||||
// expect(find.textContaining('Result: `Hello, Tom!`'), findsOneWidget);
|
// Initialize global variables
|
||||||
// });
|
await initBackgroundExecution();
|
||||||
|
|
||||||
|
// Try to connect to the API server
|
||||||
|
final connected = await apiService.connect();
|
||||||
|
|
||||||
|
// Print out the result or test it
|
||||||
|
expect(connected, isA<bool>());
|
||||||
|
|
||||||
|
// We can also check if it's connected
|
||||||
|
// Depending on your test environment, this might be true or false
|
||||||
|
// if the server is unreachable without further setup
|
||||||
|
// expect(apiService.isConnected, isA<bool>());
|
||||||
|
|
||||||
|
// Close the connection after the test
|
||||||
|
if (apiService.isConnected) {
|
||||||
|
await apiService.close(() {});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart'
|
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart'
|
||||||
show Curve, IdentityKey;
|
show Curve, IdentityKey;
|
||||||
|
|
@ -45,7 +46,7 @@ class UserDiscoveryCallbacks {
|
||||||
) async {
|
) async {
|
||||||
final storedPublicKey = await getPublicKeyFromContact(contactId);
|
final storedPublicKey = await getPublicKeyFromContact(contactId);
|
||||||
if (storedPublicKey != null) {
|
if (storedPublicKey != null) {
|
||||||
return storedPublicKey == pubKey;
|
return storedPublicKey.equals(pubKey);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -138,6 +138,7 @@ class ContactsDao extends DatabaseAccessor<TwonlyDB> with _$ContactsDaoMixin {
|
||||||
return (select(contacts)..where(
|
return (select(contacts)..where(
|
||||||
(t) =>
|
(t) =>
|
||||||
t.userDiscoveryVersion.isNotNull() &
|
t.userDiscoveryVersion.isNotNull() &
|
||||||
|
t.userDiscoveryExcluded.equals(false) &
|
||||||
t.mediaSendCounter.isBiggerOrEqualValue(
|
t.mediaSendCounter.isBiggerOrEqualValue(
|
||||||
gUser.minimumRequiredImagesExchanged,
|
gUser.minimumRequiredImagesExchanged,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -311,4 +311,19 @@ class GroupsDao extends DatabaseAccessor<TwonlyDB> with _$GroupsDaoMixin {
|
||||||
))
|
))
|
||||||
.write(GroupsCompanion(lastMessageExchange: Value(newLastMessage)));
|
.write(GroupsCompanion(lastMessageExchange: Value(newLastMessage)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Stream<List<Group>> watchNonDirectGroupsForMember(int contactId) {
|
||||||
|
final query =
|
||||||
|
select(groups).join([
|
||||||
|
innerJoin(
|
||||||
|
groupMembers,
|
||||||
|
groupMembers.groupId.equalsExp(groups.groupId),
|
||||||
|
),
|
||||||
|
])..where(
|
||||||
|
groups.isDirectChat.equals(false) &
|
||||||
|
groupMembers.contactId.equals(contactId),
|
||||||
|
);
|
||||||
|
|
||||||
|
return query.map((row) => row.readTable(groups)).watch();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,20 @@
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:twonly/src/database/tables/contacts.table.dart';
|
||||||
import 'package:twonly/src/database/tables/user_discovery.table.dart';
|
import 'package:twonly/src/database/tables/user_discovery.table.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
|
|
||||||
part 'user_discovery.dao.g.dart';
|
part 'user_discovery.dao.g.dart';
|
||||||
|
|
||||||
|
typedef AnnouncedUsersWithRelations =
|
||||||
|
Map<UserDiscoveryAnnouncedUser, List<(Contact, DateTime?)>>;
|
||||||
|
|
||||||
@DriftAccessor(
|
@DriftAccessor(
|
||||||
tables: [
|
tables: [
|
||||||
UserDiscoveryAnnouncedUsers,
|
UserDiscoveryAnnouncedUsers,
|
||||||
UserDiscoveryUserRelations,
|
UserDiscoveryUserRelations,
|
||||||
UserDiscoveryOwnPromotions,
|
UserDiscoveryOwnPromotions,
|
||||||
UserDiscoveryShares,
|
UserDiscoveryShares,
|
||||||
|
Contacts,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
class UserDiscoveryDao extends DatabaseAccessor<TwonlyDB>
|
class UserDiscoveryDao extends DatabaseAccessor<TwonlyDB>
|
||||||
|
|
@ -46,8 +51,7 @@ class UserDiscoveryDao extends DatabaseAccessor<TwonlyDB>
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<UserDiscoveryAnnouncedUser, List<(int, DateTime?)>>>
|
Stream<AnnouncedUsersWithRelations> watchAllAnnouncedUsersWithRelations() {
|
||||||
getAnnouncedUsersWithRelations() async {
|
|
||||||
final query = select(userDiscoveryAnnouncedUsers).join([
|
final query = select(userDiscoveryAnnouncedUsers).join([
|
||||||
innerJoin(
|
innerJoin(
|
||||||
userDiscoveryUserRelations,
|
userDiscoveryUserRelations,
|
||||||
|
|
@ -55,28 +59,89 @@ class UserDiscoveryDao extends DatabaseAccessor<TwonlyDB>
|
||||||
userDiscoveryAnnouncedUsers.announcedUserId,
|
userDiscoveryAnnouncedUsers.announcedUserId,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
]);
|
innerJoin(
|
||||||
|
contacts,
|
||||||
|
contacts.userId.equalsExp(
|
||||||
|
userDiscoveryUserRelations.fromContactId,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
])..where(userDiscoveryAnnouncedUsers.username.isNotNull());
|
||||||
|
|
||||||
final rows = await query.get();
|
return query.watch().map((rows) {
|
||||||
|
// ignore: omit_local_variable_types
|
||||||
|
final AnnouncedUsersWithRelations results = {};
|
||||||
|
|
||||||
final results = <UserDiscoveryAnnouncedUser, List<(int, DateTime?)>>{};
|
for (final row in rows) {
|
||||||
|
final user = row.readTable(userDiscoveryAnnouncedUsers);
|
||||||
|
final relation = row.readTable(userDiscoveryUserRelations);
|
||||||
|
final contact = row.readTable(contacts);
|
||||||
|
|
||||||
for (final row in rows) {
|
final relationData = (
|
||||||
final user = row.readTable(userDiscoveryAnnouncedUsers);
|
contact,
|
||||||
final relation = row.readTable(userDiscoveryUserRelations);
|
relation.publicKeyVerifiedTimestamp,
|
||||||
|
);
|
||||||
|
|
||||||
final relationData = (
|
if (!results.containsKey(user)) {
|
||||||
relation.fromContactId,
|
results[user] = [];
|
||||||
relation.publicKeyVerifiedTimestamp,
|
}
|
||||||
);
|
results[user]!.add(relationData);
|
||||||
|
|
||||||
if (!results.containsKey(user)) {
|
|
||||||
results[user] = [];
|
|
||||||
}
|
}
|
||||||
results[user]!.add(relationData);
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<AnnouncedUsersWithRelations> watchNewAnnouncedUsersWithRelations() {
|
||||||
|
final announcedContact = alias(contacts, 'announcedContact');
|
||||||
|
final query =
|
||||||
|
select(userDiscoveryAnnouncedUsers).join([
|
||||||
|
innerJoin(
|
||||||
|
userDiscoveryUserRelations,
|
||||||
|
userDiscoveryUserRelations.announcedUserId.equalsExp(
|
||||||
|
userDiscoveryAnnouncedUsers.announcedUserId,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
innerJoin(
|
||||||
|
contacts,
|
||||||
|
contacts.userId.equalsExp(
|
||||||
|
userDiscoveryUserRelations.fromContactId,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
leftOuterJoin(
|
||||||
|
announcedContact,
|
||||||
|
announcedContact.userId.equalsExp(
|
||||||
|
userDiscoveryAnnouncedUsers.announcedUserId,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
])..where(
|
||||||
|
userDiscoveryAnnouncedUsers.username.isNotNull() &
|
||||||
|
userDiscoveryAnnouncedUsers.isHidden.equals(false) &
|
||||||
|
(announcedContact.userId.isNull() |
|
||||||
|
announcedContact.deletedByUser.equals(true)),
|
||||||
|
);
|
||||||
|
|
||||||
|
return query.watch().map((rows) {
|
||||||
|
// ignore: omit_local_variable_types
|
||||||
|
final AnnouncedUsersWithRelations results = {};
|
||||||
|
|
||||||
|
for (final row in rows) {
|
||||||
|
final user = row.readTable(userDiscoveryAnnouncedUsers);
|
||||||
|
final relation = row.readTable(userDiscoveryUserRelations);
|
||||||
|
final contact = row.readTable(contacts);
|
||||||
|
|
||||||
|
final relationData = (
|
||||||
|
contact,
|
||||||
|
relation.publicKeyVerifiedTimestamp,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!results.containsKey(user)) {
|
||||||
|
results[user] = [];
|
||||||
|
}
|
||||||
|
results[user]!.add(relationData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream<int> watchNewAnnouncementsWithDataCount() {
|
Stream<int> watchNewAnnouncementsWithDataCount() {
|
||||||
|
|
@ -94,6 +159,20 @@ class UserDiscoveryDao extends DatabaseAccessor<TwonlyDB>
|
||||||
return query.watchSingle().map((row) => row.read(countExp) ?? 0);
|
return query.watchSingle().map((row) => row.read(countExp) ?? 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> markAllValidAnnouncedUsersAsShown() async {
|
||||||
|
await (update(userDiscoveryAnnouncedUsers)..where(
|
||||||
|
(t) =>
|
||||||
|
t.username.isNotNull() &
|
||||||
|
t.wasShownToTheUser.equals(false) &
|
||||||
|
t.isHidden.equals(false),
|
||||||
|
))
|
||||||
|
.write(
|
||||||
|
const UserDiscoveryAnnouncedUsersCompanion(
|
||||||
|
wasShownToTheUser: Value(true),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> updateAnnouncedUser(
|
Future<void> updateAnnouncedUser(
|
||||||
int announcedUserId,
|
int announcedUserId,
|
||||||
UserDiscoveryAnnouncedUsersCompanion updatedValues,
|
UserDiscoveryAnnouncedUsersCompanion updatedValues,
|
||||||
|
|
|
||||||
2727
lib/src/database/schemas/twonly_db/drift_schema_v15.json
Normal file
2727
lib/src/database/schemas/twonly_db/drift_schema_v15.json
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -26,6 +26,9 @@ class Contacts extends Table {
|
||||||
// contact_versions: HashMap<UserID, Vec<u8>>,
|
// contact_versions: HashMap<UserID, Vec<u8>>,
|
||||||
BlobColumn get userDiscoveryVersion => blob().nullable()();
|
BlobColumn get userDiscoveryVersion => blob().nullable()();
|
||||||
|
|
||||||
|
BoolColumn get userDiscoveryExcluded =>
|
||||||
|
boolean().withDefault(const Constant(false))();
|
||||||
|
|
||||||
IntColumn get mediaSendCounter => integer().withDefault(const Constant(0))();
|
IntColumn get mediaSendCounter => integer().withDefault(const Constant(0))();
|
||||||
IntColumn get mediaReceivedCounter =>
|
IntColumn get mediaReceivedCounter =>
|
||||||
integer().withDefault(const Constant(0))();
|
integer().withDefault(const Constant(0))();
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ class TwonlyDB extends _$TwonlyDB {
|
||||||
TwonlyDB.forTesting(DatabaseConnection super.connection);
|
TwonlyDB.forTesting(DatabaseConnection super.connection);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get schemaVersion => 14;
|
int get schemaVersion => 15;
|
||||||
|
|
||||||
static QueryExecutor _openConnection() {
|
static QueryExecutor _openConnection() {
|
||||||
return driftDatabase(
|
return driftDatabase(
|
||||||
|
|
@ -108,7 +108,6 @@ class TwonlyDB extends _$TwonlyDB {
|
||||||
},
|
},
|
||||||
from3To4: (m, schema) async {
|
from3To4: (m, schema) async {
|
||||||
await m.alterTable(
|
await m.alterTable(
|
||||||
// ignore: experimental_member_use
|
|
||||||
TableMigration(
|
TableMigration(
|
||||||
schema.groupHistories,
|
schema.groupHistories,
|
||||||
columnTransformer: {
|
columnTransformer: {
|
||||||
|
|
@ -141,9 +140,7 @@ class TwonlyDB extends _$TwonlyDB {
|
||||||
await m.deleteTable('signal_contact_pre_keys');
|
await m.deleteTable('signal_contact_pre_keys');
|
||||||
await m.deleteTable('signal_contact_signed_pre_keys');
|
await m.deleteTable('signal_contact_signed_pre_keys');
|
||||||
// For message_actions
|
// For message_actions
|
||||||
// ignore: experimental_member_use
|
|
||||||
await m.alterTable(TableMigration(schema.messageHistories));
|
await m.alterTable(TableMigration(schema.messageHistories));
|
||||||
// ignore: experimental_member_use
|
|
||||||
await m.alterTable(TableMigration(schema.messageActions));
|
await m.alterTable(TableMigration(schema.messageActions));
|
||||||
},
|
},
|
||||||
from8To9: (m, schema) async {
|
from8To9: (m, schema) async {
|
||||||
|
|
@ -205,6 +202,12 @@ class TwonlyDB extends _$TwonlyDB {
|
||||||
schema.userDiscoveryAnnouncedUsers.username,
|
schema.userDiscoveryAnnouncedUsers.username,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
from14To15: (m, schema) async {
|
||||||
|
await m.addColumn(
|
||||||
|
schema.contacts,
|
||||||
|
schema.contacts.userDiscoveryExcluded,
|
||||||
|
);
|
||||||
|
},
|
||||||
)(m, from, to);
|
)(m, from, to);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -185,6 +185,21 @@ class $ContactsTable extends Contacts with TableInfo<$ContactsTable, Contact> {
|
||||||
type: DriftSqlType.blob,
|
type: DriftSqlType.blob,
|
||||||
requiredDuringInsert: false,
|
requiredDuringInsert: false,
|
||||||
);
|
);
|
||||||
|
static const VerificationMeta _userDiscoveryExcludedMeta =
|
||||||
|
const VerificationMeta('userDiscoveryExcluded');
|
||||||
|
@override
|
||||||
|
late final GeneratedColumn<bool> userDiscoveryExcluded =
|
||||||
|
GeneratedColumn<bool>(
|
||||||
|
'user_discovery_excluded',
|
||||||
|
aliasedName,
|
||||||
|
false,
|
||||||
|
type: DriftSqlType.bool,
|
||||||
|
requiredDuringInsert: false,
|
||||||
|
defaultConstraints: GeneratedColumn.constraintIsAlways(
|
||||||
|
'CHECK ("user_discovery_excluded" IN (0, 1))',
|
||||||
|
),
|
||||||
|
defaultValue: const Constant(false),
|
||||||
|
);
|
||||||
static const VerificationMeta _mediaSendCounterMeta = const VerificationMeta(
|
static const VerificationMeta _mediaSendCounterMeta = const VerificationMeta(
|
||||||
'mediaSendCounter',
|
'mediaSendCounter',
|
||||||
);
|
);
|
||||||
|
|
@ -224,6 +239,7 @@ class $ContactsTable extends Contacts with TableInfo<$ContactsTable, Contact> {
|
||||||
accountDeleted,
|
accountDeleted,
|
||||||
createdAt,
|
createdAt,
|
||||||
userDiscoveryVersion,
|
userDiscoveryVersion,
|
||||||
|
userDiscoveryExcluded,
|
||||||
mediaSendCounter,
|
mediaSendCounter,
|
||||||
mediaReceivedCounter,
|
mediaReceivedCounter,
|
||||||
];
|
];
|
||||||
|
|
@ -343,6 +359,15 @@ class $ContactsTable extends Contacts with TableInfo<$ContactsTable, Contact> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (data.containsKey('user_discovery_excluded')) {
|
||||||
|
context.handle(
|
||||||
|
_userDiscoveryExcludedMeta,
|
||||||
|
userDiscoveryExcluded.isAcceptableOrUnknown(
|
||||||
|
data['user_discovery_excluded']!,
|
||||||
|
_userDiscoveryExcludedMeta,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
if (data.containsKey('media_send_counter')) {
|
if (data.containsKey('media_send_counter')) {
|
||||||
context.handle(
|
context.handle(
|
||||||
_mediaSendCounterMeta,
|
_mediaSendCounterMeta,
|
||||||
|
|
@ -426,6 +451,10 @@ class $ContactsTable extends Contacts with TableInfo<$ContactsTable, Contact> {
|
||||||
DriftSqlType.blob,
|
DriftSqlType.blob,
|
||||||
data['${effectivePrefix}user_discovery_version'],
|
data['${effectivePrefix}user_discovery_version'],
|
||||||
),
|
),
|
||||||
|
userDiscoveryExcluded: attachedDatabase.typeMapping.read(
|
||||||
|
DriftSqlType.bool,
|
||||||
|
data['${effectivePrefix}user_discovery_excluded'],
|
||||||
|
)!,
|
||||||
mediaSendCounter: attachedDatabase.typeMapping.read(
|
mediaSendCounter: attachedDatabase.typeMapping.read(
|
||||||
DriftSqlType.int,
|
DriftSqlType.int,
|
||||||
data['${effectivePrefix}media_send_counter'],
|
data['${effectivePrefix}media_send_counter'],
|
||||||
|
|
@ -458,6 +487,7 @@ class Contact extends DataClass implements Insertable<Contact> {
|
||||||
final bool accountDeleted;
|
final bool accountDeleted;
|
||||||
final DateTime createdAt;
|
final DateTime createdAt;
|
||||||
final Uint8List? userDiscoveryVersion;
|
final Uint8List? userDiscoveryVersion;
|
||||||
|
final bool userDiscoveryExcluded;
|
||||||
final int mediaSendCounter;
|
final int mediaSendCounter;
|
||||||
final int mediaReceivedCounter;
|
final int mediaReceivedCounter;
|
||||||
const Contact({
|
const Contact({
|
||||||
|
|
@ -475,6 +505,7 @@ class Contact extends DataClass implements Insertable<Contact> {
|
||||||
required this.accountDeleted,
|
required this.accountDeleted,
|
||||||
required this.createdAt,
|
required this.createdAt,
|
||||||
this.userDiscoveryVersion,
|
this.userDiscoveryVersion,
|
||||||
|
required this.userDiscoveryExcluded,
|
||||||
required this.mediaSendCounter,
|
required this.mediaSendCounter,
|
||||||
required this.mediaReceivedCounter,
|
required this.mediaReceivedCounter,
|
||||||
});
|
});
|
||||||
|
|
@ -503,6 +534,7 @@ class Contact extends DataClass implements Insertable<Contact> {
|
||||||
if (!nullToAbsent || userDiscoveryVersion != null) {
|
if (!nullToAbsent || userDiscoveryVersion != null) {
|
||||||
map['user_discovery_version'] = Variable<Uint8List>(userDiscoveryVersion);
|
map['user_discovery_version'] = Variable<Uint8List>(userDiscoveryVersion);
|
||||||
}
|
}
|
||||||
|
map['user_discovery_excluded'] = Variable<bool>(userDiscoveryExcluded);
|
||||||
map['media_send_counter'] = Variable<int>(mediaSendCounter);
|
map['media_send_counter'] = Variable<int>(mediaSendCounter);
|
||||||
map['media_received_counter'] = Variable<int>(mediaReceivedCounter);
|
map['media_received_counter'] = Variable<int>(mediaReceivedCounter);
|
||||||
return map;
|
return map;
|
||||||
|
|
@ -532,6 +564,7 @@ class Contact extends DataClass implements Insertable<Contact> {
|
||||||
userDiscoveryVersion: userDiscoveryVersion == null && nullToAbsent
|
userDiscoveryVersion: userDiscoveryVersion == null && nullToAbsent
|
||||||
? const Value.absent()
|
? const Value.absent()
|
||||||
: Value(userDiscoveryVersion),
|
: Value(userDiscoveryVersion),
|
||||||
|
userDiscoveryExcluded: Value(userDiscoveryExcluded),
|
||||||
mediaSendCounter: Value(mediaSendCounter),
|
mediaSendCounter: Value(mediaSendCounter),
|
||||||
mediaReceivedCounter: Value(mediaReceivedCounter),
|
mediaReceivedCounter: Value(mediaReceivedCounter),
|
||||||
);
|
);
|
||||||
|
|
@ -563,6 +596,9 @@ class Contact extends DataClass implements Insertable<Contact> {
|
||||||
userDiscoveryVersion: serializer.fromJson<Uint8List?>(
|
userDiscoveryVersion: serializer.fromJson<Uint8List?>(
|
||||||
json['userDiscoveryVersion'],
|
json['userDiscoveryVersion'],
|
||||||
),
|
),
|
||||||
|
userDiscoveryExcluded: serializer.fromJson<bool>(
|
||||||
|
json['userDiscoveryExcluded'],
|
||||||
|
),
|
||||||
mediaSendCounter: serializer.fromJson<int>(json['mediaSendCounter']),
|
mediaSendCounter: serializer.fromJson<int>(json['mediaSendCounter']),
|
||||||
mediaReceivedCounter: serializer.fromJson<int>(
|
mediaReceivedCounter: serializer.fromJson<int>(
|
||||||
json['mediaReceivedCounter'],
|
json['mediaReceivedCounter'],
|
||||||
|
|
@ -589,6 +625,7 @@ class Contact extends DataClass implements Insertable<Contact> {
|
||||||
'userDiscoveryVersion': serializer.toJson<Uint8List?>(
|
'userDiscoveryVersion': serializer.toJson<Uint8List?>(
|
||||||
userDiscoveryVersion,
|
userDiscoveryVersion,
|
||||||
),
|
),
|
||||||
|
'userDiscoveryExcluded': serializer.toJson<bool>(userDiscoveryExcluded),
|
||||||
'mediaSendCounter': serializer.toJson<int>(mediaSendCounter),
|
'mediaSendCounter': serializer.toJson<int>(mediaSendCounter),
|
||||||
'mediaReceivedCounter': serializer.toJson<int>(mediaReceivedCounter),
|
'mediaReceivedCounter': serializer.toJson<int>(mediaReceivedCounter),
|
||||||
};
|
};
|
||||||
|
|
@ -609,6 +646,7 @@ class Contact extends DataClass implements Insertable<Contact> {
|
||||||
bool? accountDeleted,
|
bool? accountDeleted,
|
||||||
DateTime? createdAt,
|
DateTime? createdAt,
|
||||||
Value<Uint8List?> userDiscoveryVersion = const Value.absent(),
|
Value<Uint8List?> userDiscoveryVersion = const Value.absent(),
|
||||||
|
bool? userDiscoveryExcluded,
|
||||||
int? mediaSendCounter,
|
int? mediaSendCounter,
|
||||||
int? mediaReceivedCounter,
|
int? mediaReceivedCounter,
|
||||||
}) => Contact(
|
}) => Contact(
|
||||||
|
|
@ -630,6 +668,7 @@ class Contact extends DataClass implements Insertable<Contact> {
|
||||||
userDiscoveryVersion: userDiscoveryVersion.present
|
userDiscoveryVersion: userDiscoveryVersion.present
|
||||||
? userDiscoveryVersion.value
|
? userDiscoveryVersion.value
|
||||||
: this.userDiscoveryVersion,
|
: this.userDiscoveryVersion,
|
||||||
|
userDiscoveryExcluded: userDiscoveryExcluded ?? this.userDiscoveryExcluded,
|
||||||
mediaSendCounter: mediaSendCounter ?? this.mediaSendCounter,
|
mediaSendCounter: mediaSendCounter ?? this.mediaSendCounter,
|
||||||
mediaReceivedCounter: mediaReceivedCounter ?? this.mediaReceivedCounter,
|
mediaReceivedCounter: mediaReceivedCounter ?? this.mediaReceivedCounter,
|
||||||
);
|
);
|
||||||
|
|
@ -661,6 +700,9 @@ class Contact extends DataClass implements Insertable<Contact> {
|
||||||
userDiscoveryVersion: data.userDiscoveryVersion.present
|
userDiscoveryVersion: data.userDiscoveryVersion.present
|
||||||
? data.userDiscoveryVersion.value
|
? data.userDiscoveryVersion.value
|
||||||
: this.userDiscoveryVersion,
|
: this.userDiscoveryVersion,
|
||||||
|
userDiscoveryExcluded: data.userDiscoveryExcluded.present
|
||||||
|
? data.userDiscoveryExcluded.value
|
||||||
|
: this.userDiscoveryExcluded,
|
||||||
mediaSendCounter: data.mediaSendCounter.present
|
mediaSendCounter: data.mediaSendCounter.present
|
||||||
? data.mediaSendCounter.value
|
? data.mediaSendCounter.value
|
||||||
: this.mediaSendCounter,
|
: this.mediaSendCounter,
|
||||||
|
|
@ -687,6 +729,7 @@ class Contact extends DataClass implements Insertable<Contact> {
|
||||||
..write('accountDeleted: $accountDeleted, ')
|
..write('accountDeleted: $accountDeleted, ')
|
||||||
..write('createdAt: $createdAt, ')
|
..write('createdAt: $createdAt, ')
|
||||||
..write('userDiscoveryVersion: $userDiscoveryVersion, ')
|
..write('userDiscoveryVersion: $userDiscoveryVersion, ')
|
||||||
|
..write('userDiscoveryExcluded: $userDiscoveryExcluded, ')
|
||||||
..write('mediaSendCounter: $mediaSendCounter, ')
|
..write('mediaSendCounter: $mediaSendCounter, ')
|
||||||
..write('mediaReceivedCounter: $mediaReceivedCounter')
|
..write('mediaReceivedCounter: $mediaReceivedCounter')
|
||||||
..write(')'))
|
..write(')'))
|
||||||
|
|
@ -709,6 +752,7 @@ class Contact extends DataClass implements Insertable<Contact> {
|
||||||
accountDeleted,
|
accountDeleted,
|
||||||
createdAt,
|
createdAt,
|
||||||
$driftBlobEquality.hash(userDiscoveryVersion),
|
$driftBlobEquality.hash(userDiscoveryVersion),
|
||||||
|
userDiscoveryExcluded,
|
||||||
mediaSendCounter,
|
mediaSendCounter,
|
||||||
mediaReceivedCounter,
|
mediaReceivedCounter,
|
||||||
);
|
);
|
||||||
|
|
@ -736,6 +780,7 @@ class Contact extends DataClass implements Insertable<Contact> {
|
||||||
other.userDiscoveryVersion,
|
other.userDiscoveryVersion,
|
||||||
this.userDiscoveryVersion,
|
this.userDiscoveryVersion,
|
||||||
) &&
|
) &&
|
||||||
|
other.userDiscoveryExcluded == this.userDiscoveryExcluded &&
|
||||||
other.mediaSendCounter == this.mediaSendCounter &&
|
other.mediaSendCounter == this.mediaSendCounter &&
|
||||||
other.mediaReceivedCounter == this.mediaReceivedCounter);
|
other.mediaReceivedCounter == this.mediaReceivedCounter);
|
||||||
}
|
}
|
||||||
|
|
@ -755,6 +800,7 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
|
||||||
final Value<bool> accountDeleted;
|
final Value<bool> accountDeleted;
|
||||||
final Value<DateTime> createdAt;
|
final Value<DateTime> createdAt;
|
||||||
final Value<Uint8List?> userDiscoveryVersion;
|
final Value<Uint8List?> userDiscoveryVersion;
|
||||||
|
final Value<bool> userDiscoveryExcluded;
|
||||||
final Value<int> mediaSendCounter;
|
final Value<int> mediaSendCounter;
|
||||||
final Value<int> mediaReceivedCounter;
|
final Value<int> mediaReceivedCounter;
|
||||||
const ContactsCompanion({
|
const ContactsCompanion({
|
||||||
|
|
@ -772,6 +818,7 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
|
||||||
this.accountDeleted = const Value.absent(),
|
this.accountDeleted = const Value.absent(),
|
||||||
this.createdAt = const Value.absent(),
|
this.createdAt = const Value.absent(),
|
||||||
this.userDiscoveryVersion = const Value.absent(),
|
this.userDiscoveryVersion = const Value.absent(),
|
||||||
|
this.userDiscoveryExcluded = const Value.absent(),
|
||||||
this.mediaSendCounter = const Value.absent(),
|
this.mediaSendCounter = const Value.absent(),
|
||||||
this.mediaReceivedCounter = const Value.absent(),
|
this.mediaReceivedCounter = const Value.absent(),
|
||||||
});
|
});
|
||||||
|
|
@ -790,6 +837,7 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
|
||||||
this.accountDeleted = const Value.absent(),
|
this.accountDeleted = const Value.absent(),
|
||||||
this.createdAt = const Value.absent(),
|
this.createdAt = const Value.absent(),
|
||||||
this.userDiscoveryVersion = const Value.absent(),
|
this.userDiscoveryVersion = const Value.absent(),
|
||||||
|
this.userDiscoveryExcluded = const Value.absent(),
|
||||||
this.mediaSendCounter = const Value.absent(),
|
this.mediaSendCounter = const Value.absent(),
|
||||||
this.mediaReceivedCounter = const Value.absent(),
|
this.mediaReceivedCounter = const Value.absent(),
|
||||||
}) : username = Value(username);
|
}) : username = Value(username);
|
||||||
|
|
@ -808,6 +856,7 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
|
||||||
Expression<bool>? accountDeleted,
|
Expression<bool>? accountDeleted,
|
||||||
Expression<DateTime>? createdAt,
|
Expression<DateTime>? createdAt,
|
||||||
Expression<Uint8List>? userDiscoveryVersion,
|
Expression<Uint8List>? userDiscoveryVersion,
|
||||||
|
Expression<bool>? userDiscoveryExcluded,
|
||||||
Expression<int>? mediaSendCounter,
|
Expression<int>? mediaSendCounter,
|
||||||
Expression<int>? mediaReceivedCounter,
|
Expression<int>? mediaReceivedCounter,
|
||||||
}) {
|
}) {
|
||||||
|
|
@ -829,6 +878,8 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
|
||||||
if (createdAt != null) 'created_at': createdAt,
|
if (createdAt != null) 'created_at': createdAt,
|
||||||
if (userDiscoveryVersion != null)
|
if (userDiscoveryVersion != null)
|
||||||
'user_discovery_version': userDiscoveryVersion,
|
'user_discovery_version': userDiscoveryVersion,
|
||||||
|
if (userDiscoveryExcluded != null)
|
||||||
|
'user_discovery_excluded': userDiscoveryExcluded,
|
||||||
if (mediaSendCounter != null) 'media_send_counter': mediaSendCounter,
|
if (mediaSendCounter != null) 'media_send_counter': mediaSendCounter,
|
||||||
if (mediaReceivedCounter != null)
|
if (mediaReceivedCounter != null)
|
||||||
'media_received_counter': mediaReceivedCounter,
|
'media_received_counter': mediaReceivedCounter,
|
||||||
|
|
@ -850,6 +901,7 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
|
||||||
Value<bool>? accountDeleted,
|
Value<bool>? accountDeleted,
|
||||||
Value<DateTime>? createdAt,
|
Value<DateTime>? createdAt,
|
||||||
Value<Uint8List?>? userDiscoveryVersion,
|
Value<Uint8List?>? userDiscoveryVersion,
|
||||||
|
Value<bool>? userDiscoveryExcluded,
|
||||||
Value<int>? mediaSendCounter,
|
Value<int>? mediaSendCounter,
|
||||||
Value<int>? mediaReceivedCounter,
|
Value<int>? mediaReceivedCounter,
|
||||||
}) {
|
}) {
|
||||||
|
|
@ -868,6 +920,8 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
|
||||||
accountDeleted: accountDeleted ?? this.accountDeleted,
|
accountDeleted: accountDeleted ?? this.accountDeleted,
|
||||||
createdAt: createdAt ?? this.createdAt,
|
createdAt: createdAt ?? this.createdAt,
|
||||||
userDiscoveryVersion: userDiscoveryVersion ?? this.userDiscoveryVersion,
|
userDiscoveryVersion: userDiscoveryVersion ?? this.userDiscoveryVersion,
|
||||||
|
userDiscoveryExcluded:
|
||||||
|
userDiscoveryExcluded ?? this.userDiscoveryExcluded,
|
||||||
mediaSendCounter: mediaSendCounter ?? this.mediaSendCounter,
|
mediaSendCounter: mediaSendCounter ?? this.mediaSendCounter,
|
||||||
mediaReceivedCounter: mediaReceivedCounter ?? this.mediaReceivedCounter,
|
mediaReceivedCounter: mediaReceivedCounter ?? this.mediaReceivedCounter,
|
||||||
);
|
);
|
||||||
|
|
@ -922,6 +976,11 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
|
||||||
userDiscoveryVersion.value,
|
userDiscoveryVersion.value,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (userDiscoveryExcluded.present) {
|
||||||
|
map['user_discovery_excluded'] = Variable<bool>(
|
||||||
|
userDiscoveryExcluded.value,
|
||||||
|
);
|
||||||
|
}
|
||||||
if (mediaSendCounter.present) {
|
if (mediaSendCounter.present) {
|
||||||
map['media_send_counter'] = Variable<int>(mediaSendCounter.value);
|
map['media_send_counter'] = Variable<int>(mediaSendCounter.value);
|
||||||
}
|
}
|
||||||
|
|
@ -948,6 +1007,7 @@ class ContactsCompanion extends UpdateCompanion<Contact> {
|
||||||
..write('accountDeleted: $accountDeleted, ')
|
..write('accountDeleted: $accountDeleted, ')
|
||||||
..write('createdAt: $createdAt, ')
|
..write('createdAt: $createdAt, ')
|
||||||
..write('userDiscoveryVersion: $userDiscoveryVersion, ')
|
..write('userDiscoveryVersion: $userDiscoveryVersion, ')
|
||||||
|
..write('userDiscoveryExcluded: $userDiscoveryExcluded, ')
|
||||||
..write('mediaSendCounter: $mediaSendCounter, ')
|
..write('mediaSendCounter: $mediaSendCounter, ')
|
||||||
..write('mediaReceivedCounter: $mediaReceivedCounter')
|
..write('mediaReceivedCounter: $mediaReceivedCounter')
|
||||||
..write(')'))
|
..write(')'))
|
||||||
|
|
@ -11497,6 +11557,7 @@ typedef $$ContactsTableCreateCompanionBuilder =
|
||||||
Value<bool> accountDeleted,
|
Value<bool> accountDeleted,
|
||||||
Value<DateTime> createdAt,
|
Value<DateTime> createdAt,
|
||||||
Value<Uint8List?> userDiscoveryVersion,
|
Value<Uint8List?> userDiscoveryVersion,
|
||||||
|
Value<bool> userDiscoveryExcluded,
|
||||||
Value<int> mediaSendCounter,
|
Value<int> mediaSendCounter,
|
||||||
Value<int> mediaReceivedCounter,
|
Value<int> mediaReceivedCounter,
|
||||||
});
|
});
|
||||||
|
|
@ -11516,6 +11577,7 @@ typedef $$ContactsTableUpdateCompanionBuilder =
|
||||||
Value<bool> accountDeleted,
|
Value<bool> accountDeleted,
|
||||||
Value<DateTime> createdAt,
|
Value<DateTime> createdAt,
|
||||||
Value<Uint8List?> userDiscoveryVersion,
|
Value<Uint8List?> userDiscoveryVersion,
|
||||||
|
Value<bool> userDiscoveryExcluded,
|
||||||
Value<int> mediaSendCounter,
|
Value<int> mediaSendCounter,
|
||||||
Value<int> mediaReceivedCounter,
|
Value<int> mediaReceivedCounter,
|
||||||
});
|
});
|
||||||
|
|
@ -11892,6 +11954,11 @@ class $$ContactsTableFilterComposer
|
||||||
builder: (column) => ColumnFilters(column),
|
builder: (column) => ColumnFilters(column),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ColumnFilters<bool> get userDiscoveryExcluded => $composableBuilder(
|
||||||
|
column: $table.userDiscoveryExcluded,
|
||||||
|
builder: (column) => ColumnFilters(column),
|
||||||
|
);
|
||||||
|
|
||||||
ColumnFilters<int> get mediaSendCounter => $composableBuilder(
|
ColumnFilters<int> get mediaSendCounter => $composableBuilder(
|
||||||
column: $table.mediaSendCounter,
|
column: $table.mediaSendCounter,
|
||||||
builder: (column) => ColumnFilters(column),
|
builder: (column) => ColumnFilters(column),
|
||||||
|
|
@ -12290,6 +12357,11 @@ class $$ContactsTableOrderingComposer
|
||||||
builder: (column) => ColumnOrderings(column),
|
builder: (column) => ColumnOrderings(column),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ColumnOrderings<bool> get userDiscoveryExcluded => $composableBuilder(
|
||||||
|
column: $table.userDiscoveryExcluded,
|
||||||
|
builder: (column) => ColumnOrderings(column),
|
||||||
|
);
|
||||||
|
|
||||||
ColumnOrderings<int> get mediaSendCounter => $composableBuilder(
|
ColumnOrderings<int> get mediaSendCounter => $composableBuilder(
|
||||||
column: $table.mediaSendCounter,
|
column: $table.mediaSendCounter,
|
||||||
builder: (column) => ColumnOrderings(column),
|
builder: (column) => ColumnOrderings(column),
|
||||||
|
|
@ -12364,6 +12436,11 @@ class $$ContactsTableAnnotationComposer
|
||||||
builder: (column) => column,
|
builder: (column) => column,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
GeneratedColumn<bool> get userDiscoveryExcluded => $composableBuilder(
|
||||||
|
column: $table.userDiscoveryExcluded,
|
||||||
|
builder: (column) => column,
|
||||||
|
);
|
||||||
|
|
||||||
GeneratedColumn<int> get mediaSendCounter => $composableBuilder(
|
GeneratedColumn<int> get mediaSendCounter => $composableBuilder(
|
||||||
column: $table.mediaSendCounter,
|
column: $table.mediaSendCounter,
|
||||||
builder: (column) => column,
|
builder: (column) => column,
|
||||||
|
|
@ -12743,6 +12820,7 @@ class $$ContactsTableTableManager
|
||||||
Value<bool> accountDeleted = const Value.absent(),
|
Value<bool> accountDeleted = const Value.absent(),
|
||||||
Value<DateTime> createdAt = const Value.absent(),
|
Value<DateTime> createdAt = const Value.absent(),
|
||||||
Value<Uint8List?> userDiscoveryVersion = const Value.absent(),
|
Value<Uint8List?> userDiscoveryVersion = const Value.absent(),
|
||||||
|
Value<bool> userDiscoveryExcluded = const Value.absent(),
|
||||||
Value<int> mediaSendCounter = const Value.absent(),
|
Value<int> mediaSendCounter = const Value.absent(),
|
||||||
Value<int> mediaReceivedCounter = const Value.absent(),
|
Value<int> mediaReceivedCounter = const Value.absent(),
|
||||||
}) => ContactsCompanion(
|
}) => ContactsCompanion(
|
||||||
|
|
@ -12760,6 +12838,7 @@ class $$ContactsTableTableManager
|
||||||
accountDeleted: accountDeleted,
|
accountDeleted: accountDeleted,
|
||||||
createdAt: createdAt,
|
createdAt: createdAt,
|
||||||
userDiscoveryVersion: userDiscoveryVersion,
|
userDiscoveryVersion: userDiscoveryVersion,
|
||||||
|
userDiscoveryExcluded: userDiscoveryExcluded,
|
||||||
mediaSendCounter: mediaSendCounter,
|
mediaSendCounter: mediaSendCounter,
|
||||||
mediaReceivedCounter: mediaReceivedCounter,
|
mediaReceivedCounter: mediaReceivedCounter,
|
||||||
),
|
),
|
||||||
|
|
@ -12779,6 +12858,7 @@ class $$ContactsTableTableManager
|
||||||
Value<bool> accountDeleted = const Value.absent(),
|
Value<bool> accountDeleted = const Value.absent(),
|
||||||
Value<DateTime> createdAt = const Value.absent(),
|
Value<DateTime> createdAt = const Value.absent(),
|
||||||
Value<Uint8List?> userDiscoveryVersion = const Value.absent(),
|
Value<Uint8List?> userDiscoveryVersion = const Value.absent(),
|
||||||
|
Value<bool> userDiscoveryExcluded = const Value.absent(),
|
||||||
Value<int> mediaSendCounter = const Value.absent(),
|
Value<int> mediaSendCounter = const Value.absent(),
|
||||||
Value<int> mediaReceivedCounter = const Value.absent(),
|
Value<int> mediaReceivedCounter = const Value.absent(),
|
||||||
}) => ContactsCompanion.insert(
|
}) => ContactsCompanion.insert(
|
||||||
|
|
@ -12796,6 +12876,7 @@ class $$ContactsTableTableManager
|
||||||
accountDeleted: accountDeleted,
|
accountDeleted: accountDeleted,
|
||||||
createdAt: createdAt,
|
createdAt: createdAt,
|
||||||
userDiscoveryVersion: userDiscoveryVersion,
|
userDiscoveryVersion: userDiscoveryVersion,
|
||||||
|
userDiscoveryExcluded: userDiscoveryExcluded,
|
||||||
mediaSendCounter: mediaSendCounter,
|
mediaSendCounter: mediaSendCounter,
|
||||||
mediaReceivedCounter: mediaReceivedCounter,
|
mediaReceivedCounter: mediaReceivedCounter,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -7380,6 +7380,461 @@ i1.GeneratedColumn<int> _column_232(String aliasedName) =>
|
||||||
$customConstraints: 'NOT NULL DEFAULT 0 CHECK (is_hidden IN (0, 1))',
|
$customConstraints: 'NOT NULL DEFAULT 0 CHECK (is_hidden IN (0, 1))',
|
||||||
defaultValue: const i1.CustomExpression('0'),
|
defaultValue: const i1.CustomExpression('0'),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final class Schema15 extends i0.VersionedSchema {
|
||||||
|
Schema15({required super.database}) : super(version: 15);
|
||||||
|
@override
|
||||||
|
late final List<i1.DatabaseSchemaEntity> entities = [
|
||||||
|
contacts,
|
||||||
|
groups,
|
||||||
|
mediaFiles,
|
||||||
|
messages,
|
||||||
|
messageHistories,
|
||||||
|
reactions,
|
||||||
|
groupMembers,
|
||||||
|
receipts,
|
||||||
|
receivedReceipts,
|
||||||
|
signalIdentityKeyStores,
|
||||||
|
signalPreKeyStores,
|
||||||
|
signalSenderKeyStores,
|
||||||
|
signalSessionStores,
|
||||||
|
messageActions,
|
||||||
|
groupHistories,
|
||||||
|
keyVerifications,
|
||||||
|
verificationTokens,
|
||||||
|
userDiscoveryAnnouncedUsers,
|
||||||
|
userDiscoveryUserRelations,
|
||||||
|
userDiscoveryOtherPromotions,
|
||||||
|
userDiscoveryOwnPromotions,
|
||||||
|
userDiscoveryShares,
|
||||||
|
];
|
||||||
|
late final Shape49 contacts = Shape49(
|
||||||
|
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,
|
||||||
|
_column_211,
|
||||||
|
_column_233,
|
||||||
|
_column_228,
|
||||||
|
_column_229,
|
||||||
|
],
|
||||||
|
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,
|
||||||
|
);
|
||||||
|
late final Shape40 keyVerifications = Shape40(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'key_verifications',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: ['PRIMARY KEY(contact_id)'],
|
||||||
|
columns: [_column_183, _column_144, _column_118],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null,
|
||||||
|
);
|
||||||
|
late final Shape41 verificationTokens = Shape41(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'verification_tokens',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [],
|
||||||
|
columns: [_column_212, _column_213, _column_118],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null,
|
||||||
|
);
|
||||||
|
late final Shape48 userDiscoveryAnnouncedUsers = Shape48(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'user_discovery_announced_users',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: ['PRIMARY KEY(announced_user_id)'],
|
||||||
|
columns: [
|
||||||
|
_column_214,
|
||||||
|
_column_215,
|
||||||
|
_column_216,
|
||||||
|
_column_230,
|
||||||
|
_column_231,
|
||||||
|
_column_232,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null,
|
||||||
|
);
|
||||||
|
late final Shape43 userDiscoveryUserRelations = Shape43(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'user_discovery_user_relations',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: ['PRIMARY KEY(announced_user_id, from_contact_id)'],
|
||||||
|
columns: [_column_217, _column_218, _column_219],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null,
|
||||||
|
);
|
||||||
|
late final Shape44 userDiscoveryOtherPromotions = Shape44(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'user_discovery_other_promotions',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: ['PRIMARY KEY(from_contact_id, promotion_id)'],
|
||||||
|
columns: [
|
||||||
|
_column_218,
|
||||||
|
_column_220,
|
||||||
|
_column_221,
|
||||||
|
_column_222,
|
||||||
|
_column_223,
|
||||||
|
_column_219,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null,
|
||||||
|
);
|
||||||
|
late final Shape45 userDiscoveryOwnPromotions = Shape45(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'user_discovery_own_promotions',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [],
|
||||||
|
columns: [_column_224, _column_183, _column_225],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null,
|
||||||
|
);
|
||||||
|
late final Shape46 userDiscoveryShares = Shape46(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'user_discovery_shares',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [],
|
||||||
|
columns: [_column_226, _column_227, _column_175],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Shape49 extends i0.VersionedTable {
|
||||||
|
Shape49({required super.source, required super.alias}) : super.aliased();
|
||||||
|
i1.GeneratedColumn<int> get userId =>
|
||||||
|
columnsByName['user_id']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<String> get username =>
|
||||||
|
columnsByName['username']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<String> get displayName =>
|
||||||
|
columnsByName['display_name']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<String> get nickName =>
|
||||||
|
columnsByName['nick_name']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<i2.Uint8List> get avatarSvgCompressed =>
|
||||||
|
columnsByName['avatar_svg_compressed']!
|
||||||
|
as i1.GeneratedColumn<i2.Uint8List>;
|
||||||
|
i1.GeneratedColumn<int> get senderProfileCounter =>
|
||||||
|
columnsByName['sender_profile_counter']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<int> get accepted =>
|
||||||
|
columnsByName['accepted']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<int> get deletedByUser =>
|
||||||
|
columnsByName['deleted_by_user']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<int> get requested =>
|
||||||
|
columnsByName['requested']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<int> get blocked =>
|
||||||
|
columnsByName['blocked']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<int> get verified =>
|
||||||
|
columnsByName['verified']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<int> get accountDeleted =>
|
||||||
|
columnsByName['account_deleted']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<int> get createdAt =>
|
||||||
|
columnsByName['created_at']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<i2.Uint8List> get userDiscoveryVersion =>
|
||||||
|
columnsByName['user_discovery_version']!
|
||||||
|
as i1.GeneratedColumn<i2.Uint8List>;
|
||||||
|
i1.GeneratedColumn<int> get userDiscoveryExcluded =>
|
||||||
|
columnsByName['user_discovery_excluded']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<int> get mediaSendCounter =>
|
||||||
|
columnsByName['media_send_counter']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<int> get mediaReceivedCounter =>
|
||||||
|
columnsByName['media_received_counter']! as i1.GeneratedColumn<int>;
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.GeneratedColumn<int> _column_233(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<int>(
|
||||||
|
'user_discovery_excluded',
|
||||||
|
aliasedName,
|
||||||
|
false,
|
||||||
|
type: i1.DriftSqlType.int,
|
||||||
|
$customConstraints:
|
||||||
|
'NOT NULL DEFAULT 0 CHECK (user_discovery_excluded IN (0, 1))',
|
||||||
|
defaultValue: const i1.CustomExpression('0'),
|
||||||
|
);
|
||||||
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,
|
||||||
|
|
@ -7394,6 +7849,7 @@ i0.MigrationStepWithVersion migrationSteps({
|
||||||
required Future<void> Function(i1.Migrator m, Schema12 schema) from11To12,
|
required Future<void> Function(i1.Migrator m, Schema12 schema) from11To12,
|
||||||
required Future<void> Function(i1.Migrator m, Schema13 schema) from12To13,
|
required Future<void> Function(i1.Migrator m, Schema13 schema) from12To13,
|
||||||
required Future<void> Function(i1.Migrator m, Schema14 schema) from13To14,
|
required Future<void> Function(i1.Migrator m, Schema14 schema) from13To14,
|
||||||
|
required Future<void> Function(i1.Migrator m, Schema15 schema) from14To15,
|
||||||
}) {
|
}) {
|
||||||
return (currentVersion, database) async {
|
return (currentVersion, database) async {
|
||||||
switch (currentVersion) {
|
switch (currentVersion) {
|
||||||
|
|
@ -7462,6 +7918,11 @@ i0.MigrationStepWithVersion migrationSteps({
|
||||||
final migrator = i1.Migrator(database, schema);
|
final migrator = i1.Migrator(database, schema);
|
||||||
await from13To14(migrator, schema);
|
await from13To14(migrator, schema);
|
||||||
return 14;
|
return 14;
|
||||||
|
case 14:
|
||||||
|
final schema = Schema15(database: database);
|
||||||
|
final migrator = i1.Migrator(database, schema);
|
||||||
|
await from14To15(migrator, schema);
|
||||||
|
return 15;
|
||||||
default:
|
default:
|
||||||
throw ArgumentError.value('Unknown migration from $currentVersion');
|
throw ArgumentError.value('Unknown migration from $currentVersion');
|
||||||
}
|
}
|
||||||
|
|
@ -7482,6 +7943,7 @@ i1.OnUpgrade stepByStep({
|
||||||
required Future<void> Function(i1.Migrator m, Schema12 schema) from11To12,
|
required Future<void> Function(i1.Migrator m, Schema12 schema) from11To12,
|
||||||
required Future<void> Function(i1.Migrator m, Schema13 schema) from12To13,
|
required Future<void> Function(i1.Migrator m, Schema13 schema) from12To13,
|
||||||
required Future<void> Function(i1.Migrator m, Schema14 schema) from13To14,
|
required Future<void> Function(i1.Migrator m, Schema14 schema) from13To14,
|
||||||
|
required Future<void> Function(i1.Migrator m, Schema15 schema) from14To15,
|
||||||
}) => i0.VersionedSchema.stepByStepHelper(
|
}) => i0.VersionedSchema.stepByStepHelper(
|
||||||
step: migrationSteps(
|
step: migrationSteps(
|
||||||
from1To2: from1To2,
|
from1To2: from1To2,
|
||||||
|
|
@ -7497,5 +7959,6 @@ i1.OnUpgrade stepByStep({
|
||||||
from11To12: from11To12,
|
from11To12: from11To12,
|
||||||
from12To13: from12To13,
|
from12To13: from12To13,
|
||||||
from13To14: from13To14,
|
from13To14: from13To14,
|
||||||
|
from14To15: from14To15,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -397,27 +397,9 @@ abstract class AppLocalizations {
|
||||||
/// No description provided for @searchUserNamePending.
|
/// No description provided for @searchUserNamePending.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Pending'**
|
/// **'Request pending'**
|
||||||
String get searchUserNamePending;
|
String get searchUserNamePending;
|
||||||
|
|
||||||
/// No description provided for @searchUserNameBlockUserTooltip.
|
|
||||||
///
|
|
||||||
/// In en, this message translates to:
|
|
||||||
/// **'Block the user without informing.'**
|
|
||||||
String get searchUserNameBlockUserTooltip;
|
|
||||||
|
|
||||||
/// No description provided for @searchUserNameRejectUserTooltip.
|
|
||||||
///
|
|
||||||
/// In en, this message translates to:
|
|
||||||
/// **'Reject the request and let the requester know.'**
|
|
||||||
String get searchUserNameRejectUserTooltip;
|
|
||||||
|
|
||||||
/// No description provided for @searchUserNameArchiveUserTooltip.
|
|
||||||
///
|
|
||||||
/// In en, this message translates to:
|
|
||||||
/// **'Archive the user. He will appear again as soon as he accepts your request.'**
|
|
||||||
String get searchUserNameArchiveUserTooltip;
|
|
||||||
|
|
||||||
/// No description provided for @searchUsernameNotFound.
|
/// No description provided for @searchUsernameNotFound.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|
@ -433,7 +415,7 @@ abstract class AppLocalizations {
|
||||||
/// No description provided for @searchUsernameNewFollowerTitle.
|
/// No description provided for @searchUsernameNewFollowerTitle.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Follow requests'**
|
/// **'Open requests'**
|
||||||
String get searchUsernameNewFollowerTitle;
|
String get searchUsernameNewFollowerTitle;
|
||||||
|
|
||||||
/// No description provided for @searchUsernameQrCodeBtn.
|
/// No description provided for @searchUsernameQrCodeBtn.
|
||||||
|
|
@ -3147,6 +3129,198 @@ abstract class AppLocalizations {
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Scan / Show QR'**
|
/// **'Scan / Show QR'**
|
||||||
String get scanQrOrShow;
|
String get scanQrOrShow;
|
||||||
|
|
||||||
|
/// No description provided for @contactActionBlock.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Block'**
|
||||||
|
String get contactActionBlock;
|
||||||
|
|
||||||
|
/// No description provided for @contactActionAccept.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Accept'**
|
||||||
|
String get contactActionAccept;
|
||||||
|
|
||||||
|
/// No description provided for @userDiscoverySettingsMinImages.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Choose the minimum number of images you must have exchanged with a person before you securely share your friends with them.'**
|
||||||
|
String get userDiscoverySettingsMinImages;
|
||||||
|
|
||||||
|
/// No description provided for @userDiscoverySettingsMutualFriends.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Choose how many mutual friends a person must have for you to be suggested to them.'**
|
||||||
|
String get userDiscoverySettingsMutualFriends;
|
||||||
|
|
||||||
|
/// No description provided for @userDiscoverySettingsApply.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Apply changes'**
|
||||||
|
String get userDiscoverySettingsApply;
|
||||||
|
|
||||||
|
/// No description provided for @userDiscoveryEnabledDisableWarning.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'If you disable the \"Find friends\" feature, you will no longer see suggestions. You will also stop sharing your friends with new contacts.'**
|
||||||
|
String get userDiscoveryEnabledDisableWarning;
|
||||||
|
|
||||||
|
/// No description provided for @userDiscoveryEnabledChangeSettings.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Change settings'**
|
||||||
|
String get userDiscoveryEnabledChangeSettings;
|
||||||
|
|
||||||
|
/// No description provided for @userDiscoveryEnabledFaq.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'In our FAQ we explain how the \"Find friends\" feature works.'**
|
||||||
|
String get userDiscoveryEnabledFaq;
|
||||||
|
|
||||||
|
/// No description provided for @userDiscoveryDisabledIntro.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'twonly doesn\'t use phone numbers, so we suggest friends based on mutual contacts instead – securely and privately.'**
|
||||||
|
String get userDiscoveryDisabledIntro;
|
||||||
|
|
||||||
|
/// No description provided for @userDiscoveryDisabledInvisible.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Your friend list is *completely invisible to strangers*. Only your friends can see parts of it – and only those people with whom they have *mutual friends* themselves.'**
|
||||||
|
String get userDiscoveryDisabledInvisible;
|
||||||
|
|
||||||
|
/// No description provided for @userDiscoveryDisabledDecide.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Decide for yourself who can see your friends. You can change your mind at any time or hide specific people.'**
|
||||||
|
String get userDiscoveryDisabledDecide;
|
||||||
|
|
||||||
|
/// No description provided for @userDiscoverySettingsTitle.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Find friends'**
|
||||||
|
String get userDiscoverySettingsTitle;
|
||||||
|
|
||||||
|
/// No description provided for @userDiscoverySettingsMinImagesTitle.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Number of shared images'**
|
||||||
|
String get userDiscoverySettingsMinImagesTitle;
|
||||||
|
|
||||||
|
/// No description provided for @userDiscoverySettingsMutualFriendsTitle.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Number of mutual friends'**
|
||||||
|
String get userDiscoverySettingsMutualFriendsTitle;
|
||||||
|
|
||||||
|
/// No description provided for @userDiscoveryDisabledYouHaveControl.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'You are in control'**
|
||||||
|
String get userDiscoveryDisabledYouHaveControl;
|
||||||
|
|
||||||
|
/// No description provided for @userDiscoveryDisabledEnableWithDefault.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Enable with default settings'**
|
||||||
|
String get userDiscoveryDisabledEnableWithDefault;
|
||||||
|
|
||||||
|
/// No description provided for @userDiscoveryDisabledCustomizeSettings.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Customize settings'**
|
||||||
|
String get userDiscoveryDisabledCustomizeSettings;
|
||||||
|
|
||||||
|
/// No description provided for @userDiscoveryDisabledLearnMore.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Learn more'**
|
||||||
|
String get userDiscoveryDisabledLearnMore;
|
||||||
|
|
||||||
|
/// No description provided for @userDiscoveryEnabledDialogTitle.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Really disable?'**
|
||||||
|
String get userDiscoveryEnabledDialogTitle;
|
||||||
|
|
||||||
|
/// No description provided for @userDiscoveryEnabledFriendsShared.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Friends you share'**
|
||||||
|
String get userDiscoveryEnabledFriendsShared;
|
||||||
|
|
||||||
|
/// No description provided for @userDiscoveryEnabledFriendsSharedDesc.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'You only share friends who have also activated this feature and who have reached the threshold you set.'**
|
||||||
|
String get userDiscoveryEnabledFriendsSharedDesc;
|
||||||
|
|
||||||
|
/// No description provided for @userDiscoveryEnabledNoFriendsShared.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'You are not sharing anyone yet.'**
|
||||||
|
String get userDiscoveryEnabledNoFriendsShared;
|
||||||
|
|
||||||
|
/// No description provided for @userDiscoveryActionDisable.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Disable'**
|
||||||
|
String get userDiscoveryActionDisable;
|
||||||
|
|
||||||
|
/// No description provided for @friendSuggestionsTitle.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Friend suggestions'**
|
||||||
|
String get friendSuggestionsTitle;
|
||||||
|
|
||||||
|
/// No description provided for @andWord.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'and'**
|
||||||
|
String get andWord;
|
||||||
|
|
||||||
|
/// No description provided for @friendSuggestionsFriendsWith.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Friends with {friends}.'**
|
||||||
|
String friendSuggestionsFriendsWith(Object friends);
|
||||||
|
|
||||||
|
/// No description provided for @friendSuggestionsGroupMemberIn.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **' Group member in {groups}.'**
|
||||||
|
String friendSuggestionsGroupMemberIn(Object groups);
|
||||||
|
|
||||||
|
/// No description provided for @friendSuggestionsRequest.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Request'**
|
||||||
|
String get friendSuggestionsRequest;
|
||||||
|
|
||||||
|
/// No description provided for @contactUserDiscoveryImagesLeft.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'{imagesLeft} more images are needed until your friends are shared with {username}.'**
|
||||||
|
String contactUserDiscoveryImagesLeft(Object imagesLeft, Object username);
|
||||||
|
|
||||||
|
/// No description provided for @userDiscoveryEnabledVersion.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Version: {version}'**
|
||||||
|
String userDiscoveryEnabledVersion(Object version);
|
||||||
|
|
||||||
|
/// No description provided for @userDiscoveryEnabledYourVersion.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Your version: {version}'**
|
||||||
|
String userDiscoveryEnabledYourVersion(Object version);
|
||||||
|
|
||||||
|
/// No description provided for @userDiscoveryEnabledStopSharing.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Stop sharing'**
|
||||||
|
String get userDiscoveryEnabledStopSharing;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AppLocalizationsDelegate
|
class _AppLocalizationsDelegate
|
||||||
|
|
|
||||||
|
|
@ -172,19 +172,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
String get selectSubscription => 'Abo auswählen';
|
String get selectSubscription => 'Abo auswählen';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get searchUserNamePending => 'Ausstehend';
|
String get searchUserNamePending => 'Anfrage ausstehend';
|
||||||
|
|
||||||
@override
|
|
||||||
String get searchUserNameBlockUserTooltip =>
|
|
||||||
'Benutzer ohne Benachrichtigung blockieren.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get searchUserNameRejectUserTooltip =>
|
|
||||||
'Die Anfrage ablehnen und den Anfragenden informieren.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get searchUserNameArchiveUserTooltip =>
|
|
||||||
'Benutzer archivieren. Du wirst informiert sobald er deine Anfrage akzeptiert.';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get searchUsernameNotFound => 'Benutzername nicht gefunden';
|
String get searchUsernameNotFound => 'Benutzername nicht gefunden';
|
||||||
|
|
@ -195,7 +183,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get searchUsernameNewFollowerTitle => 'Folgeanfragen';
|
String get searchUsernameNewFollowerTitle => 'Offene Anfragen';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get searchUsernameQrCodeBtn => 'QR-Code scannen';
|
String get searchUsernameQrCodeBtn => 'QR-Code scannen';
|
||||||
|
|
@ -1764,4 +1752,122 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get scanQrOrShow => 'QR scannen / anzeigen';
|
String get scanQrOrShow => 'QR scannen / anzeigen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get contactActionBlock => 'Blockieren';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get contactActionAccept => 'Annehmen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoverySettingsMinImages =>
|
||||||
|
'Wähle die Mindestanzahl an Bildern, die du mit einer Person ausgetauscht haben musst, bevor du ihr deine Freunde sicher teilst.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoverySettingsMutualFriends =>
|
||||||
|
'Wähle aus, wie viele gemeinsame Freunde eine Person haben muss, damit du ihr vorgeschlagen wirst.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoverySettingsApply => 'Änderungen übernehmen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryEnabledDisableWarning =>
|
||||||
|
'Wenn du das Feature „Freunde finden“ deaktivierst, werden dir keine Vorschläge mehr angezeigt. Du teilst neuen Kontakten dann auch nicht mehr deine Freunde.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryEnabledChangeSettings => 'Einstellungen ändern';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryEnabledFaq =>
|
||||||
|
'In unserem FAQ erklären wir dir wie das Feature \"Freunde finden\" funktioniert.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryDisabledIntro =>
|
||||||
|
'twonly verzichten auf Telefonnummern, daher schlagen wir dir Freunde stattdessen über gemeinsame Kontakte vor – sicher und privat.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryDisabledInvisible =>
|
||||||
|
'Deine Freundesliste ist für *Fremde komplett unsichtbar*. Nur deine Freunde können Teile davon sehen – und zwar nur die Personen, mit denen sie selbst *gemeinsame Freunde* haben.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryDisabledDecide =>
|
||||||
|
'Entscheide selbst, wer deine Freunde sehen darf. Du kannst deine Meinung jederzeit ändern oder bestimmte Personen verstecken.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoverySettingsTitle => 'Freunde finden';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoverySettingsMinImagesTitle =>
|
||||||
|
'Anzahl an geteilten Bildern';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoverySettingsMutualFriendsTitle =>
|
||||||
|
'Anzahl an gemeinsame Freunde';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryDisabledYouHaveControl => 'Du hast die Kontrolle';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryDisabledEnableWithDefault =>
|
||||||
|
'Mit Standardeinstellungen aktivieren';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryDisabledCustomizeSettings => 'Einstellungen anpassen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryDisabledLearnMore => 'Mehr erfahren';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryEnabledDialogTitle => 'Wirklich deaktivieren?';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryEnabledFriendsShared => 'Freunde die du teilst';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryEnabledFriendsSharedDesc =>
|
||||||
|
'Du teilst nur Freunde, die diese Funktion ebenfalls aktiviert haben und die den von dir festgelegten Schwellenwert erreicht haben.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryEnabledNoFriendsShared =>
|
||||||
|
'Bisher teilst du noch niemanden.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryActionDisable => 'Deaktivieren';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get friendSuggestionsTitle => 'Freundschaftsvorschläge';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get andWord => 'und';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String friendSuggestionsFriendsWith(Object friends) {
|
||||||
|
return 'Befreundet mit $friends.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String friendSuggestionsGroupMemberIn(Object groups) {
|
||||||
|
return ' Gruppenmitglied in $groups.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get friendSuggestionsRequest => 'Anfragen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String contactUserDiscoveryImagesLeft(Object imagesLeft, Object username) {
|
||||||
|
return 'Es fehlen noch $imagesLeft Bilder bis deine Freunde mit $username geteilt werden.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String userDiscoveryEnabledVersion(Object version) {
|
||||||
|
return 'Version: $version';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String userDiscoveryEnabledYourVersion(Object version) {
|
||||||
|
return 'Deine Version: $version';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryEnabledStopSharing => 'Nicht mehr teilen';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -171,19 +171,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
String get selectSubscription => 'Select subscription';
|
String get selectSubscription => 'Select subscription';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get searchUserNamePending => 'Pending';
|
String get searchUserNamePending => 'Request pending';
|
||||||
|
|
||||||
@override
|
|
||||||
String get searchUserNameBlockUserTooltip =>
|
|
||||||
'Block the user without informing.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get searchUserNameRejectUserTooltip =>
|
|
||||||
'Reject the request and let the requester know.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get searchUserNameArchiveUserTooltip =>
|
|
||||||
'Archive the user. He will appear again as soon as he accepts your request.';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get searchUsernameNotFound => 'Username not found';
|
String get searchUsernameNotFound => 'Username not found';
|
||||||
|
|
@ -194,7 +182,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get searchUsernameNewFollowerTitle => 'Follow requests';
|
String get searchUsernameNewFollowerTitle => 'Open requests';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get searchUsernameQrCodeBtn => 'Scan QR code';
|
String get searchUsernameQrCodeBtn => 'Scan QR code';
|
||||||
|
|
@ -1752,4 +1740,121 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get scanQrOrShow => 'Scan / Show QR';
|
String get scanQrOrShow => 'Scan / Show QR';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get contactActionBlock => 'Block';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get contactActionAccept => 'Accept';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoverySettingsMinImages =>
|
||||||
|
'Choose the minimum number of images you must have exchanged with a person before you securely share your friends with them.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoverySettingsMutualFriends =>
|
||||||
|
'Choose how many mutual friends a person must have for you to be suggested to them.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoverySettingsApply => 'Apply changes';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryEnabledDisableWarning =>
|
||||||
|
'If you disable the \"Find friends\" feature, you will no longer see suggestions. You will also stop sharing your friends with new contacts.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryEnabledChangeSettings => 'Change settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryEnabledFaq =>
|
||||||
|
'In our FAQ we explain how the \"Find friends\" feature works.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryDisabledIntro =>
|
||||||
|
'twonly doesn\'t use phone numbers, so we suggest friends based on mutual contacts instead – securely and privately.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryDisabledInvisible =>
|
||||||
|
'Your friend list is *completely invisible to strangers*. Only your friends can see parts of it – and only those people with whom they have *mutual friends* themselves.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryDisabledDecide =>
|
||||||
|
'Decide for yourself who can see your friends. You can change your mind at any time or hide specific people.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoverySettingsTitle => 'Find friends';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoverySettingsMinImagesTitle => 'Number of shared images';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoverySettingsMutualFriendsTitle =>
|
||||||
|
'Number of mutual friends';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryDisabledYouHaveControl => 'You are in control';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryDisabledEnableWithDefault =>
|
||||||
|
'Enable with default settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryDisabledCustomizeSettings => 'Customize settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryDisabledLearnMore => 'Learn more';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryEnabledDialogTitle => 'Really disable?';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryEnabledFriendsShared => 'Friends you share';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryEnabledFriendsSharedDesc =>
|
||||||
|
'You only share friends who have also activated this feature and who have reached the threshold you set.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryEnabledNoFriendsShared =>
|
||||||
|
'You are not sharing anyone yet.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryActionDisable => 'Disable';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get friendSuggestionsTitle => 'Friend suggestions';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get andWord => 'and';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String friendSuggestionsFriendsWith(Object friends) {
|
||||||
|
return 'Friends with $friends.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String friendSuggestionsGroupMemberIn(Object groups) {
|
||||||
|
return ' Group member in $groups.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get friendSuggestionsRequest => 'Request';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String contactUserDiscoveryImagesLeft(Object imagesLeft, Object username) {
|
||||||
|
return '$imagesLeft more images are needed until your friends are shared with $username.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String userDiscoveryEnabledVersion(Object version) {
|
||||||
|
return 'Version: $version';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String userDiscoveryEnabledYourVersion(Object version) {
|
||||||
|
return 'Your version: $version';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryEnabledStopSharing => 'Stop sharing';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -171,19 +171,7 @@ class AppLocalizationsSv extends AppLocalizations {
|
||||||
String get selectSubscription => 'Select subscription';
|
String get selectSubscription => 'Select subscription';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get searchUserNamePending => 'Pending';
|
String get searchUserNamePending => 'Request pending';
|
||||||
|
|
||||||
@override
|
|
||||||
String get searchUserNameBlockUserTooltip =>
|
|
||||||
'Block the user without informing.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get searchUserNameRejectUserTooltip =>
|
|
||||||
'Reject the request and let the requester know.';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get searchUserNameArchiveUserTooltip =>
|
|
||||||
'Archive the user. He will appear again as soon as he accepts your request.';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get searchUsernameNotFound => 'Username not found';
|
String get searchUsernameNotFound => 'Username not found';
|
||||||
|
|
@ -194,7 +182,7 @@ class AppLocalizationsSv extends AppLocalizations {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get searchUsernameNewFollowerTitle => 'Follow requests';
|
String get searchUsernameNewFollowerTitle => 'Open requests';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get searchUsernameQrCodeBtn => 'Scan QR code';
|
String get searchUsernameQrCodeBtn => 'Scan QR code';
|
||||||
|
|
@ -1752,4 +1740,121 @@ class AppLocalizationsSv extends AppLocalizations {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get scanQrOrShow => 'Scan / Show QR';
|
String get scanQrOrShow => 'Scan / Show QR';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get contactActionBlock => 'Block';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get contactActionAccept => 'Accept';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoverySettingsMinImages =>
|
||||||
|
'Choose the minimum number of images you must have exchanged with a person before you securely share your friends with them.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoverySettingsMutualFriends =>
|
||||||
|
'Choose how many mutual friends a person must have for you to be suggested to them.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoverySettingsApply => 'Apply changes';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryEnabledDisableWarning =>
|
||||||
|
'If you disable the \"Find friends\" feature, you will no longer see suggestions. You will also stop sharing your friends with new contacts.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryEnabledChangeSettings => 'Change settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryEnabledFaq =>
|
||||||
|
'In our FAQ we explain how the \"Find friends\" feature works.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryDisabledIntro =>
|
||||||
|
'twonly doesn\'t use phone numbers, so we suggest friends based on mutual contacts instead – securely and privately.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryDisabledInvisible =>
|
||||||
|
'Your friend list is *completely invisible to strangers*. Only your friends can see parts of it – and only those people with whom they have *mutual friends* themselves.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryDisabledDecide =>
|
||||||
|
'Decide for yourself who can see your friends. You can change your mind at any time or hide specific people.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoverySettingsTitle => 'Find friends';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoverySettingsMinImagesTitle => 'Number of shared images';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoverySettingsMutualFriendsTitle =>
|
||||||
|
'Number of mutual friends';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryDisabledYouHaveControl => 'You are in control';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryDisabledEnableWithDefault =>
|
||||||
|
'Enable with default settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryDisabledCustomizeSettings => 'Customize settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryDisabledLearnMore => 'Learn more';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryEnabledDialogTitle => 'Really disable?';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryEnabledFriendsShared => 'Friends you share';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryEnabledFriendsSharedDesc =>
|
||||||
|
'You only share friends who have also activated this feature and who have reached the threshold you set.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryEnabledNoFriendsShared =>
|
||||||
|
'You are not sharing anyone yet.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryActionDisable => 'Disable';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get friendSuggestionsTitle => 'Friend suggestions';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get andWord => 'and';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String friendSuggestionsFriendsWith(Object friends) {
|
||||||
|
return 'Friends with $friends.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String friendSuggestionsGroupMemberIn(Object groups) {
|
||||||
|
return ' Group member in $groups.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get friendSuggestionsRequest => 'Request';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String contactUserDiscoveryImagesLeft(Object imagesLeft, Object username) {
|
||||||
|
return '$imagesLeft more images are needed until your friends are shared with $username.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String userDiscoveryEnabledVersion(Object version) {
|
||||||
|
return 'Version: $version';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String userDiscoveryEnabledYourVersion(Object version) {
|
||||||
|
return 'Your version: $version';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get userDiscoveryEnabledStopSharing => 'Stop sharing';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -117,15 +117,20 @@ Future<void> handleContactUpdate(
|
||||||
|
|
||||||
case EncryptedContent_ContactUpdate_Type.UPDATE:
|
case EncryptedContent_ContactUpdate_Type.UPDATE:
|
||||||
Log.info('Got a contact update $fromUserId');
|
Log.info('Got a contact update $fromUserId');
|
||||||
if (contactUpdate.hasAvatarSvgCompressed() &&
|
Uint8List? avatarSvgCompressed;
|
||||||
contactUpdate.hasDisplayName() &&
|
if (contactUpdate.hasAvatarSvgCompressed()) {
|
||||||
|
avatarSvgCompressed = Uint8List.fromList(
|
||||||
|
contactUpdate.avatarSvgCompressed,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (contactUpdate.hasDisplayName() &&
|
||||||
contactUpdate.hasUsername() &&
|
contactUpdate.hasUsername() &&
|
||||||
senderProfileCounter != null) {
|
senderProfileCounter != null) {
|
||||||
await twonlyDB.contactsDao.updateContact(
|
await twonlyDB.contactsDao.updateContact(
|
||||||
fromUserId,
|
fromUserId,
|
||||||
ContactsCompanion(
|
ContactsCompanion(
|
||||||
avatarSvgCompressed: Value(
|
avatarSvgCompressed: Value(
|
||||||
Uint8List.fromList(contactUpdate.avatarSvgCompressed),
|
avatarSvgCompressed,
|
||||||
),
|
),
|
||||||
displayName: Value(contactUpdate.displayName),
|
displayName: Value(contactUpdate.displayName),
|
||||||
username: Value(contactUpdate.username),
|
username: Value(contactUpdate.username),
|
||||||
|
|
@ -180,6 +185,7 @@ Future<int?> checkForProfileUpdate(
|
||||||
.getSingleOrNull();
|
.getSingleOrNull();
|
||||||
if (contact != null) {
|
if (contact != null) {
|
||||||
if (contact.senderProfileCounter < senderProfileCounter) {
|
if (contact.senderProfileCounter < senderProfileCounter) {
|
||||||
|
Log.info('${contact.senderProfileCounter} < $senderProfileCounter');
|
||||||
await sendCipherText(
|
await sendCipherText(
|
||||||
fromUserId,
|
fromUserId,
|
||||||
EncryptedContent(
|
EncryptedContent(
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ Future<void> checkForUserDiscoveryChanges(
|
||||||
);
|
);
|
||||||
|
|
||||||
if (currentVersion != null) {
|
if (currentVersion != null) {
|
||||||
|
Log.info('Having old version from contact. Requesting new version.');
|
||||||
await sendCipherText(
|
await sendCipherText(
|
||||||
fromUserId,
|
fromUserId,
|
||||||
EncryptedContent(
|
EncryptedContent(
|
||||||
|
|
@ -31,6 +32,8 @@ Future<void> handleUserDiscoveryRequest(
|
||||||
int fromUserId,
|
int fromUserId,
|
||||||
EncryptedContent_UserDiscoveryRequest request,
|
EncryptedContent_UserDiscoveryRequest request,
|
||||||
) async {
|
) async {
|
||||||
|
Log.info('Got a user discovery request');
|
||||||
|
|
||||||
if (!gUser.isUserDiscoveryEnabled) {
|
if (!gUser.isUserDiscoveryEnabled) {
|
||||||
Log.warn('Got a user discovery request while it is disabled');
|
Log.warn('Got a user discovery request while it is disabled');
|
||||||
return;
|
return;
|
||||||
|
|
@ -38,9 +41,10 @@ Future<void> handleUserDiscoveryRequest(
|
||||||
final contact = await twonlyDB.contactsDao.getContactById(fromUserId);
|
final contact = await twonlyDB.contactsDao.getContactById(fromUserId);
|
||||||
if (contact == null) return;
|
if (contact == null) return;
|
||||||
|
|
||||||
if (contact.mediaSendCounter < gUser.minimumRequiredImagesExchanged) {
|
if (contact.mediaSendCounter < gUser.minimumRequiredImagesExchanged ||
|
||||||
|
contact.userDiscoveryExcluded) {
|
||||||
Log.warn(
|
Log.warn(
|
||||||
'Got a request to update user discovery, but mediaSendCounter (${contact.mediaSendCounter}) < ${gUser.minimumRequiredImagesExchanged}',
|
'Got a request to update user discovery, but mediaSendCounter (${contact.mediaSendCounter}) < ${gUser.minimumRequiredImagesExchanged} or user is excluded ${contact.userDiscoveryExcluded}',
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -50,6 +54,7 @@ Future<void> handleUserDiscoveryRequest(
|
||||||
request.currentVersion,
|
request.currentVersion,
|
||||||
);
|
);
|
||||||
if (newMessages != null && newMessages.isNotEmpty) {
|
if (newMessages != null && newMessages.isNotEmpty) {
|
||||||
|
Log.info('Sending ${newMessages.length} user discovery messages');
|
||||||
await sendCipherText(
|
await sendCipherText(
|
||||||
fromUserId,
|
fromUserId,
|
||||||
EncryptedContent(
|
EncryptedContent(
|
||||||
|
|
@ -71,6 +76,7 @@ Future<void> handleUserDiscoveryUpdate(
|
||||||
Log.warn('Got a user discovery update while it is disabled');
|
Log.warn('Got a user discovery update while it is disabled');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Log.info('Got ${update.messages.length} user discovery messages');
|
||||||
await UserDiscoveryService.handleNewMessages(
|
await UserDiscoveryService.handleNewMessages(
|
||||||
fromUserId,
|
fromUserId,
|
||||||
update.messages.map(Uint8List.fromList).toList(),
|
update.messages.map(Uint8List.fromList).toList(),
|
||||||
|
|
|
||||||
|
|
@ -347,10 +347,11 @@ Future<(Uint8List, Uint8List?)?> sendCipherText(
|
||||||
}
|
}
|
||||||
encryptedContent.senderProfileCounter = Int64(gUser.avatarCounter);
|
encryptedContent.senderProfileCounter = Int64(gUser.avatarCounter);
|
||||||
|
|
||||||
if (gUser.isUserDiscoveryEnabled) {
|
if (gUser.isUserDiscoveryEnabled && messageId != null) {
|
||||||
final contact = await twonlyDB.contactsDao.getContactById(contactId);
|
final contact = await twonlyDB.contactsDao.getContactById(contactId);
|
||||||
if (contact != null &&
|
if (contact != null &&
|
||||||
contact.mediaSendCounter >= gUser.minimumRequiredImagesExchanged) {
|
contact.mediaSendCounter >= gUser.minimumRequiredImagesExchanged &&
|
||||||
|
!contact.userDiscoveryExcluded) {
|
||||||
final version = await UserDiscoveryService.getCurrentVersion();
|
final version = await UserDiscoveryService.getCurrentVersion();
|
||||||
if (version != null) {
|
if (version != null) {
|
||||||
encryptedContent.senderUserDiscoveryVersion = version;
|
encryptedContent.senderUserDiscoveryVersion = version;
|
||||||
|
|
|
||||||
|
|
@ -355,7 +355,10 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
|
||||||
final contact = await twonlyDB.contactsDao
|
final contact = await twonlyDB.contactsDao
|
||||||
.getContactByUserId(fromUserId)
|
.getContactByUserId(fromUserId)
|
||||||
.getSingleOrNull();
|
.getSingleOrNull();
|
||||||
if (contact == null || contact.deletedByUser) {
|
Log.info(
|
||||||
|
'Contact exists?: ${contact != null} Is deleted? ${contact?.deletedByUser} Accepted? (${contact?.accepted})',
|
||||||
|
);
|
||||||
|
if (contact == null || !contact.accepted || contact.deletedByUser) {
|
||||||
await handleNewContactRequest(fromUserId);
|
await handleNewContactRequest(fromUserId);
|
||||||
Log.error(
|
Log.error(
|
||||||
'User tries to send message to direct chat while the user does not exists !',
|
'User tries to send message to direct chat while the user does not exists !',
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:twonly/core/bridge/wrapper/user_discovery.dart';
|
import 'package:twonly/core/bridge/wrapper/user_discovery.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
|
|
@ -18,8 +20,14 @@ class UserDiscoveryService {
|
||||||
announcedUser.announcedUserId,
|
announcedUser.announcedUserId,
|
||||||
);
|
);
|
||||||
if (userdata == null) continue;
|
if (userdata == null) continue;
|
||||||
if (userdata.publicIdentityKey !=
|
if (!userdata.publicIdentityKey.equals(
|
||||||
announcedUser.announcedPublicKey.toList()) {
|
announcedUser.announcedPublicKey.toList(),
|
||||||
|
)) {
|
||||||
|
if (kDebugMode) {
|
||||||
|
Log.warn(
|
||||||
|
'${userdata.publicIdentityKey} != ${announcedUser.announcedPublicKey.toList()}',
|
||||||
|
);
|
||||||
|
}
|
||||||
Log.error(
|
Log.error(
|
||||||
'Server delivered a different public key then received from the announcement.',
|
'Server delivered a different public key then received from the announcement.',
|
||||||
);
|
);
|
||||||
|
|
@ -74,6 +82,21 @@ class UserDiscoveryService {
|
||||||
return UserDiscoveryVersion.fromBuffer(version);
|
return UserDiscoveryVersion.fromBuffer(version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<UserDiscoveryVersion?> getContactVersionTyped(
|
||||||
|
int contactId,
|
||||||
|
) async {
|
||||||
|
final contact = await twonlyDB.contactsDao.getContactById(contactId);
|
||||||
|
if (contact == null || contact.userDiscoveryVersion == null) return null;
|
||||||
|
return UserDiscoveryVersion.fromBuffer(contact.userDiscoveryVersion!);
|
||||||
|
}
|
||||||
|
|
||||||
|
static UserDiscoveryVersion? getContactVersionTypedFromContact(
|
||||||
|
Contact contact,
|
||||||
|
) {
|
||||||
|
if (contact.userDiscoveryVersion == null) return null;
|
||||||
|
return UserDiscoveryVersion.fromBuffer(contact.userDiscoveryVersion!);
|
||||||
|
}
|
||||||
|
|
||||||
static Future<Uint8List?> shouldRequestNewMessages(
|
static Future<Uint8List?> shouldRequestNewMessages(
|
||||||
int fromUserId,
|
int fromUserId,
|
||||||
List<int> receivedVersion,
|
List<int> receivedVersion,
|
||||||
|
|
|
||||||
|
|
@ -405,13 +405,15 @@ Future<List<int>> sha256File(File file) async {
|
||||||
return sha256Sink.events.single.bytes;
|
return sha256Sink.events.single.bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<TextSpan> formattedText(String input) {
|
List<TextSpan> formattedText(BuildContext context, String input) {
|
||||||
// Pattern to find text between asterisks
|
// Access the current theme's text color
|
||||||
final regex = RegExp(r'\*(.*?)\*');
|
// Defaulting to bodyMedium color, but you can use labelLarge, displaySmall, etc.
|
||||||
final List<TextSpan> spans = [];
|
final defaultColor = Theme.of(context).colorScheme.onSurface;
|
||||||
|
|
||||||
// Track the current position in the string
|
final regex = RegExp(r'\*(.*?)\*');
|
||||||
int lastMatchEnd = 0;
|
final spans = <TextSpan>[];
|
||||||
|
|
||||||
|
var lastMatchEnd = 0;
|
||||||
|
|
||||||
for (final match in regex.allMatches(input)) {
|
for (final match in regex.allMatches(input)) {
|
||||||
// Add text before the match (Normal style)
|
// Add text before the match (Normal style)
|
||||||
|
|
@ -419,17 +421,18 @@ List<TextSpan> formattedText(String input) {
|
||||||
spans.add(
|
spans.add(
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: input.substring(lastMatchEnd, match.start),
|
text: input.substring(lastMatchEnd, match.start),
|
||||||
|
style: TextStyle(color: defaultColor),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the matched text (Bold style)
|
// Add the matched text (Bold style)
|
||||||
// match.group(1) is the text without the asterisks
|
|
||||||
spans.add(
|
spans.add(
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: match.group(1),
|
text: match.group(1),
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
|
color: defaultColor, // Ensures bold text also uses the theme color
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -442,9 +445,16 @@ List<TextSpan> formattedText(String input) {
|
||||||
spans.add(
|
spans.add(
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: input.substring(lastMatchEnd),
|
text: input.substring(lastMatchEnd),
|
||||||
|
style: TextStyle(color: defaultColor),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return spans;
|
return spans;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String joinWithAnd(List<String> items, String andWord) {
|
||||||
|
if (items.isEmpty) return '';
|
||||||
|
if (items.length == 1) return items.first;
|
||||||
|
return '${items.sublist(0, items.length - 1).join(', ')} $andWord ${items.last}';
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,13 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/constants/routes.keys.dart';
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
import 'package:twonly/src/database/daos/user_discovery.dao.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.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/api/utils.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
import 'package:twonly/src/views/chats/add_new_user_components/friend_suggestions.dart';
|
||||||
|
import 'package:twonly/src/views/chats/add_new_user_components/open_requests_list.dart';
|
||||||
import 'package:twonly/src/views/components/alert_dialog.dart';
|
import 'package:twonly/src/views/components/alert_dialog.dart';
|
||||||
import 'package:twonly/src/views/components/avatar_icon.component.dart';
|
|
||||||
import 'package:twonly/src/views/components/headline.dart';
|
|
||||||
|
|
||||||
class AddNewUserView extends StatefulWidget {
|
class AddNewUserView extends StatefulWidget {
|
||||||
const AddNewUserView({
|
const AddNewUserView({
|
||||||
|
|
@ -32,47 +30,76 @@ class AddNewUserView extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SearchUsernameView extends State<AddNewUserView> {
|
class _SearchUsernameView extends State<AddNewUserView> {
|
||||||
final TextEditingController searchUserName = TextEditingController();
|
final TextEditingController _usernameController = TextEditingController();
|
||||||
bool _isLoading = false;
|
bool _isLoading = false;
|
||||||
bool hasRequestedUsers = false;
|
bool hasRequestedUsers = false;
|
||||||
|
|
||||||
List<Contact> contacts = [];
|
List<Contact> _openRequestsContacts = [];
|
||||||
late StreamSubscription<List<Contact>> contactsStream;
|
late StreamSubscription<List<Contact>> _contactsStream;
|
||||||
|
|
||||||
|
AnnouncedUsersWithRelations _newAnnouncedUsers = {};
|
||||||
|
late StreamSubscription<AnnouncedUsersWithRelations> _newAnnouncedUsersStream;
|
||||||
|
|
||||||
|
AnnouncedUsersWithRelations _allAnnouncedUsers = {};
|
||||||
|
late StreamSubscription<AnnouncedUsersWithRelations> _allAnnouncedUsersStream;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
contactsStream = twonlyDB.contactsDao.watchNotAcceptedContacts().listen(
|
_contactsStream = twonlyDB.contactsDao.watchNotAcceptedContacts().listen(
|
||||||
(update) => setState(() {
|
(update) {
|
||||||
contacts = update;
|
if (mounted) {
|
||||||
}),
|
setState(() {
|
||||||
|
_openRequestsContacts = update;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
_newAnnouncedUsersStream = twonlyDB.userDiscoveryDao
|
||||||
|
.watchNewAnnouncedUsersWithRelations()
|
||||||
|
.listen((update) {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
_newAnnouncedUsers = update;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_allAnnouncedUsersStream = twonlyDB.userDiscoveryDao
|
||||||
|
.watchAllAnnouncedUsersWithRelations()
|
||||||
|
.listen((update) {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
_allAnnouncedUsers = update;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (widget.username != null) {
|
if (widget.username != null) {
|
||||||
searchUserName.text = widget.username!;
|
_usernameController.text = widget.username!;
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
_addNewUser(context);
|
_requestNewUserByUsername(widget.username!);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
twonlyDB.userDiscoveryDao.markAllValidAnnouncedUsersAsShown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
unawaited(contactsStream.cancel());
|
_contactsStream.cancel();
|
||||||
|
_newAnnouncedUsersStream.cancel();
|
||||||
|
_allAnnouncedUsersStream.cancel();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _addNewUser(BuildContext context) async {
|
Future<void> _requestNewUserByUsername(String username) async {
|
||||||
if (gUser.username == searchUserName.text) {
|
if (gUser.username == username) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoading = true;
|
_isLoading = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
final userdata = await apiService.getUserData(searchUserName.text);
|
final userdata = await apiService.getUserData(username);
|
||||||
if (!context.mounted) return;
|
if (!mounted) return;
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
|
|
@ -82,24 +109,22 @@ class _SearchUsernameView extends State<AddNewUserView> {
|
||||||
await showAlertDialog(
|
await showAlertDialog(
|
||||||
context,
|
context,
|
||||||
context.lang.searchUsernameNotFound,
|
context.lang.searchUsernameNotFound,
|
||||||
context.lang.searchUsernameNotFoundBody(searchUserName.text),
|
context.lang.searchUsernameNotFoundBody(username),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final addUser = await showAlertDialog(
|
final addUser = await showAlertDialog(
|
||||||
context,
|
context,
|
||||||
context.lang.userFound(searchUserName.text),
|
context.lang.userFound(username),
|
||||||
context.lang.userFoundBody,
|
context.lang.userFoundBody,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!addUser || !context.mounted) {
|
if (!addUser || !mounted) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final added = await twonlyDB.contactsDao.insertOnConflictUpdate(
|
final added = await twonlyDB.contactsDao.insertOnConflictUpdate(
|
||||||
ContactsCompanion(
|
ContactsCompanion(
|
||||||
username: Value(searchUserName.text),
|
username: Value(username),
|
||||||
userId: Value(userdata.userId.toInt()),
|
userId: Value(userdata.userId.toInt()),
|
||||||
requested: const Value(false),
|
requested: const Value(false),
|
||||||
blocked: const Value(false),
|
blocked: const Value(false),
|
||||||
|
|
@ -114,7 +139,7 @@ class _SearchUsernameView extends State<AddNewUserView> {
|
||||||
if (added > 0) await importSignalContactAndCreateRequest(userdata);
|
if (added > 0) await importSignalContactAndCreateRequest(userdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
InputDecoration getInputDecoration(String hintText) {
|
InputDecoration _getInputDecoration(String hintText) {
|
||||||
return InputDecoration(
|
return InputDecoration(
|
||||||
hintText: hintText,
|
hintText: hintText,
|
||||||
focusedBorder: OutlineInputBorder(
|
focusedBorder: OutlineInputBorder(
|
||||||
|
|
@ -146,14 +171,16 @@ class _SearchUsernameView extends State<AddNewUserView> {
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TextField(
|
child: TextField(
|
||||||
onSubmitted: (_) async {
|
onSubmitted: _requestNewUserByUsername,
|
||||||
await _addNewUser(context);
|
|
||||||
},
|
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
searchUserName.text = value.toLowerCase();
|
_usernameController.text = value.toLowerCase();
|
||||||
searchUserName.selection = TextSelection.fromPosition(
|
_usernameController.selection =
|
||||||
TextPosition(offset: searchUserName.text.length),
|
TextSelection.fromPosition(
|
||||||
);
|
TextPosition(
|
||||||
|
offset: _usernameController.text.length,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
setState(() {});
|
||||||
},
|
},
|
||||||
inputFormatters: [
|
inputFormatters: [
|
||||||
LengthLimitingTextInputFormatter(12),
|
LengthLimitingTextInputFormatter(12),
|
||||||
|
|
@ -161,8 +188,8 @@ class _SearchUsernameView extends State<AddNewUserView> {
|
||||||
RegExp('[a-z0-9A-Z._]'),
|
RegExp('[a-z0-9A-Z._]'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
controller: searchUserName,
|
controller: _usernameController,
|
||||||
decoration: getInputDecoration(
|
decoration: _getInputDecoration(
|
||||||
context.lang.searchUsernameInput,
|
context.lang.searchUsernameInput,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -173,18 +200,24 @@ class _SearchUsernameView extends State<AddNewUserView> {
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 20,
|
height: 20,
|
||||||
),
|
),
|
||||||
OutlinedButton.icon(
|
|
||||||
onPressed: () => context.push(Routes.settingsPublicProfile),
|
|
||||||
icon: const FaIcon(FontAwesomeIcons.qrcode),
|
|
||||||
label: Text(context.lang.scanQrOrShow),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
if (contacts.isNotEmpty)
|
|
||||||
HeadLineComponent(
|
|
||||||
context.lang.searchUsernameNewFollowerTitle,
|
|
||||||
),
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ContactsListView(contacts),
|
child: ListView(
|
||||||
|
children: [
|
||||||
|
Center(
|
||||||
|
child: OutlinedButton.icon(
|
||||||
|
onPressed: () =>
|
||||||
|
context.push(Routes.settingsPublicProfile),
|
||||||
|
icon: const FaIcon(FontAwesomeIcons.qrcode),
|
||||||
|
label: Text(context.lang.scanQrOrShow),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
OpenRequestsList(
|
||||||
|
contacts: _openRequestsContacts,
|
||||||
|
relations: _allAnnouncedUsers,
|
||||||
|
),
|
||||||
|
FriendSuggestions(_newAnnouncedUsers),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
@ -193,9 +226,9 @@ class _SearchUsernameView extends State<AddNewUserView> {
|
||||||
floatingActionButton: Padding(
|
floatingActionButton: Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 30),
|
padding: const EdgeInsets.only(bottom: 30),
|
||||||
child: FloatingActionButton(
|
child: FloatingActionButton(
|
||||||
onPressed: _isLoading || searchUserName.text.isEmpty
|
onPressed: _isLoading || _usernameController.text.isEmpty
|
||||||
? null
|
? null
|
||||||
: () async => _addNewUser(context),
|
: () => _requestNewUserByUsername(_usernameController.text),
|
||||||
child: _isLoading
|
child: _isLoading
|
||||||
? const Center(child: CircularProgressIndicator())
|
? const Center(child: CircularProgressIndicator())
|
||||||
: const FaIcon(FontAwesomeIcons.magnifyingGlassPlus),
|
: const FaIcon(FontAwesomeIcons.magnifyingGlassPlus),
|
||||||
|
|
@ -204,114 +237,3 @@ class _SearchUsernameView extends State<AddNewUserView> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ContactsListView extends StatelessWidget {
|
|
||||||
const ContactsListView(this.contacts, {super.key});
|
|
||||||
final List<Contact> contacts;
|
|
||||||
|
|
||||||
List<Widget> sendRequestActions(BuildContext context, Contact contact) {
|
|
||||||
return [
|
|
||||||
Tooltip(
|
|
||||||
message: context.lang.searchUserNameArchiveUserTooltip,
|
|
||||||
child: IconButton(
|
|
||||||
icon: const FaIcon(Icons.archive_outlined, size: 15),
|
|
||||||
onPressed: () async {
|
|
||||||
const update = ContactsCompanion(deletedByUser: Value(true));
|
|
||||||
await twonlyDB.contactsDao.updateContact(contact.userId, update);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(context.lang.searchUserNamePending),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Widget> requestedActions(BuildContext context, Contact contact) {
|
|
||||||
return [
|
|
||||||
Tooltip(
|
|
||||||
message: context.lang.searchUserNameBlockUserTooltip,
|
|
||||||
child: IconButton(
|
|
||||||
icon: const Icon(
|
|
||||||
Icons.person_off_rounded,
|
|
||||||
color: Color.fromARGB(164, 244, 67, 54),
|
|
||||||
),
|
|
||||||
onPressed: () async {
|
|
||||||
const update = ContactsCompanion(blocked: Value(true));
|
|
||||||
await twonlyDB.contactsDao.updateContact(contact.userId, update);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Tooltip(
|
|
||||||
message: context.lang.searchUserNameRejectUserTooltip,
|
|
||||||
child: IconButton(
|
|
||||||
icon: const Icon(Icons.close, color: Colors.red),
|
|
||||||
onPressed: () async {
|
|
||||||
await sendCipherText(
|
|
||||||
contact.userId,
|
|
||||||
EncryptedContent(
|
|
||||||
contactRequest: EncryptedContent_ContactRequest(
|
|
||||||
type: EncryptedContent_ContactRequest_Type.REJECT,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
await twonlyDB.contactsDao.updateContact(
|
|
||||||
contact.userId,
|
|
||||||
const ContactsCompanion(
|
|
||||||
accepted: Value(false),
|
|
||||||
requested: Value(false),
|
|
||||||
deletedByUser: Value(true),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.check, color: Colors.green),
|
|
||||||
onPressed: () async {
|
|
||||||
await twonlyDB.contactsDao.updateContact(
|
|
||||||
contact.userId,
|
|
||||||
const ContactsCompanion(
|
|
||||||
accepted: Value(true),
|
|
||||||
requested: Value(false),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
await twonlyDB.groupsDao.createNewDirectChat(
|
|
||||||
contact.userId,
|
|
||||||
GroupsCompanion(
|
|
||||||
groupName: Value(getContactDisplayName(contact)),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
await sendCipherText(
|
|
||||||
contact.userId,
|
|
||||||
EncryptedContent(
|
|
||||||
contactRequest: EncryptedContent_ContactRequest(
|
|
||||||
type: EncryptedContent_ContactRequest_Type.ACCEPT,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
await sendContactMyProfileData(contact.userId);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return ListView.builder(
|
|
||||||
itemCount: contacts.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final contact = contacts[index];
|
|
||||||
return ListTile(
|
|
||||||
key: ValueKey(contact.userId),
|
|
||||||
title: Text(substringBy(contact.username, 25)),
|
|
||||||
leading: AvatarIcon(contactId: contact.userId),
|
|
||||||
trailing: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: contact.requested
|
|
||||||
? requestedActions(context, contact)
|
|
||||||
: sendRequestActions(context, contact),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,162 @@
|
||||||
|
import 'package:drift/drift.dart' show Value;
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:twonly/globals.dart';
|
||||||
|
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||||
|
import 'package:twonly/src/database/daos/user_discovery.dao.dart';
|
||||||
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
|
import 'package:twonly/src/services/api/utils.dart';
|
||||||
|
import 'package:twonly/src/themes/light.dart';
|
||||||
|
import 'package:twonly/src/utils/log.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
import 'package:twonly/src/views/components/avatar_icon.component.dart';
|
||||||
|
import 'package:twonly/src/views/components/headline.dart';
|
||||||
|
import 'package:twonly/src/views/groups/group.view.dart';
|
||||||
|
|
||||||
|
List<TextSpan> buildFriendsListText(
|
||||||
|
BuildContext context,
|
||||||
|
List<(Contact, DateTime?)> friends,
|
||||||
|
) {
|
||||||
|
final names = friends.map((f) => '*${getContactDisplayName(f.$1)}*').toList();
|
||||||
|
|
||||||
|
return formattedText(
|
||||||
|
context,
|
||||||
|
context.lang.friendSuggestionsFriendsWith(
|
||||||
|
joinWithAnd(names, context.lang.andWord),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class FriendSuggestions extends StatelessWidget {
|
||||||
|
const FriendSuggestions(this.announcedUsers, {super.key});
|
||||||
|
|
||||||
|
final AnnouncedUsersWithRelations announcedUsers;
|
||||||
|
|
||||||
|
Future<void> _requestAnnouncedUser(
|
||||||
|
BuildContext context,
|
||||||
|
UserDiscoveryAnnouncedUser user,
|
||||||
|
) async {
|
||||||
|
Log.info('Requesting user via friend suggestions');
|
||||||
|
|
||||||
|
final userdata = await apiService.getUserById(user.announcedUserId);
|
||||||
|
|
||||||
|
if (userdata == null) {
|
||||||
|
if (context.mounted) {
|
||||||
|
showNetworkIssue(context);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final added = await twonlyDB.contactsDao.insertOnConflictUpdate(
|
||||||
|
ContactsCompanion(
|
||||||
|
username: Value(user.username!),
|
||||||
|
userId: Value(userdata.userId.toInt()),
|
||||||
|
requested: const Value(false),
|
||||||
|
blocked: const Value(false),
|
||||||
|
deletedByUser: const Value(false),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (added > 0) await importSignalContactAndCreateRequest(userdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _hideAnnouncedUser(int userId) async {
|
||||||
|
await twonlyDB.userDiscoveryDao.updateAnnouncedUser(
|
||||||
|
userId,
|
||||||
|
const UserDiscoveryAnnouncedUsersCompanion(
|
||||||
|
isHidden: Value(true),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (announcedUsers.isEmpty) return Container();
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
HeadLineComponent(
|
||||||
|
context.lang.friendSuggestionsTitle,
|
||||||
|
),
|
||||||
|
...announcedUsers.entries.map((
|
||||||
|
announcedUser,
|
||||||
|
) {
|
||||||
|
final user = announcedUser.key;
|
||||||
|
final friends = announcedUser.value;
|
||||||
|
|
||||||
|
final friendsList = buildFriendsListText(context, friends);
|
||||||
|
|
||||||
|
return ListTile(
|
||||||
|
key: ValueKey(user.announcedUserId),
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
title: Text(substringBy(user.username!, 25)),
|
||||||
|
subtitle: StreamBuilder(
|
||||||
|
stream: twonlyDB.groupsDao.watchNonDirectGroupsForMember(
|
||||||
|
user.announcedUserId,
|
||||||
|
),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
var text = friendsList;
|
||||||
|
if (snapshot.hasData && snapshot.data!.isNotEmpty) {
|
||||||
|
text += formattedText(
|
||||||
|
context,
|
||||||
|
context.lang.friendSuggestionsGroupMemberIn(
|
||||||
|
joinWithAnd(
|
||||||
|
snapshot.data!.map((g) => '*${g.groupName}*').toList(),
|
||||||
|
context.lang.andWord,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
children: text,
|
||||||
|
style: const TextStyle(fontSize: 11),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
leading: const AvatarIcon(
|
||||||
|
fontSize: 17,
|
||||||
|
),
|
||||||
|
trailing: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 26,
|
||||||
|
child: FilledButton(
|
||||||
|
style: FilledButton.styleFrom(
|
||||||
|
padding: const EdgeInsets.only(right: 8, left: 4),
|
||||||
|
).merge(secondaryGreyButtonStyle(context)),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 6),
|
||||||
|
child: FaIcon(FontAwesomeIcons.userPlus, size: 12),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
context.lang.friendSuggestionsRequest,
|
||||||
|
style: const TextStyle(fontSize: 10),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onPressed: () => _requestAnnouncedUser(context, user),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
style: IconButton.styleFrom(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
),
|
||||||
|
constraints: const BoxConstraints(),
|
||||||
|
icon: const Icon(Icons.close, size: 18),
|
||||||
|
onPressed: () => _hideAnnouncedUser(user.announcedUserId),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,189 @@
|
||||||
|
import 'package:drift/drift.dart' hide Column;
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:twonly/globals.dart';
|
||||||
|
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||||
|
import 'package:twonly/src/database/daos/user_discovery.dao.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/themes/light.dart';
|
||||||
|
import 'package:twonly/src/utils/log.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
import 'package:twonly/src/views/chats/add_new_user_components/friend_suggestions.dart';
|
||||||
|
import 'package:twonly/src/views/components/avatar_icon.component.dart';
|
||||||
|
import 'package:twonly/src/views/components/headline.dart';
|
||||||
|
|
||||||
|
class OpenRequestsList extends StatelessWidget {
|
||||||
|
const OpenRequestsList({
|
||||||
|
required this.contacts,
|
||||||
|
required this.relations,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
final List<Contact> contacts;
|
||||||
|
final AnnouncedUsersWithRelations relations;
|
||||||
|
|
||||||
|
List<Widget> sendRequestActions(BuildContext context, Contact contact) {
|
||||||
|
return [
|
||||||
|
Text(context.lang.searchUserNamePending),
|
||||||
|
IconButton(
|
||||||
|
style: IconButton.styleFrom(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
),
|
||||||
|
constraints: const BoxConstraints(),
|
||||||
|
icon: const Icon(Icons.close, size: 18),
|
||||||
|
onPressed: () async {
|
||||||
|
const update = ContactsCompanion(deletedByUser: Value(true));
|
||||||
|
await twonlyDB.contactsDao.updateContact(contact.userId, update);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Widget> requestedActions(BuildContext context, Contact contact) {
|
||||||
|
return [
|
||||||
|
SizedBox(
|
||||||
|
height: 26,
|
||||||
|
child: FilledButton(
|
||||||
|
style: FilledButton.styleFrom(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 4,
|
||||||
|
),
|
||||||
|
).merge(secondaryGreyButtonStyle(context)),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
Icons.person_off_rounded,
|
||||||
|
color: Color.fromARGB(164, 244, 67, 54),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
context.lang.contactActionBlock,
|
||||||
|
style: const TextStyle(fontSize: 10),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onPressed: () async {
|
||||||
|
const update = ContactsCompanion(blocked: Value(true));
|
||||||
|
await twonlyDB.contactsDao.updateContact(contact.userId, update);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 9),
|
||||||
|
SizedBox(
|
||||||
|
height: 26,
|
||||||
|
child: FilledButton(
|
||||||
|
style: FilledButton.styleFrom(
|
||||||
|
padding: const EdgeInsets.only(right: 8, left: 4),
|
||||||
|
).merge(secondaryGreyButtonStyle(context)),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.check, color: Colors.green),
|
||||||
|
Text(
|
||||||
|
context.lang.contactActionAccept,
|
||||||
|
style: const TextStyle(fontSize: 10),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onPressed: () async {
|
||||||
|
await twonlyDB.contactsDao.updateContact(
|
||||||
|
contact.userId,
|
||||||
|
const ContactsCompanion(
|
||||||
|
accepted: Value(true),
|
||||||
|
requested: Value(false),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await twonlyDB.groupsDao.createNewDirectChat(
|
||||||
|
contact.userId,
|
||||||
|
GroupsCompanion(
|
||||||
|
groupName: Value(getContactDisplayName(contact)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await sendCipherText(
|
||||||
|
contact.userId,
|
||||||
|
EncryptedContent(
|
||||||
|
contactRequest: EncryptedContent_ContactRequest(
|
||||||
|
type: EncryptedContent_ContactRequest_Type.ACCEPT,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await sendContactMyProfileData(contact.userId);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
style: IconButton.styleFrom(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
),
|
||||||
|
constraints: const BoxConstraints(),
|
||||||
|
icon: const Icon(Icons.close, size: 18),
|
||||||
|
onPressed: () async {
|
||||||
|
await sendCipherText(
|
||||||
|
contact.userId,
|
||||||
|
EncryptedContent(
|
||||||
|
contactRequest: EncryptedContent_ContactRequest(
|
||||||
|
type: EncryptedContent_ContactRequest_Type.REJECT,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await twonlyDB.contactsDao.updateContact(
|
||||||
|
contact.userId,
|
||||||
|
const ContactsCompanion(
|
||||||
|
accepted: Value(false),
|
||||||
|
requested: Value(false),
|
||||||
|
deletedByUser: Value(true),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (contacts.isEmpty) return Container();
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
HeadLineComponent(
|
||||||
|
context.lang.searchUsernameNewFollowerTitle,
|
||||||
|
),
|
||||||
|
...contacts.map((contact) {
|
||||||
|
Widget? subtitle;
|
||||||
|
|
||||||
|
Log.info('Relations count: ${relations.entries.length}');
|
||||||
|
|
||||||
|
for (final relation in relations.entries) {
|
||||||
|
if (relation.key.announcedUserId == contact.userId) {
|
||||||
|
subtitle = RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
children: buildFriendsListText(context, relation.value),
|
||||||
|
style: const TextStyle(fontSize: 11),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ListTile(
|
||||||
|
key: ValueKey(contact.userId),
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
title: Text(substringBy(contact.username, 25)),
|
||||||
|
subtitle: subtitle,
|
||||||
|
leading: AvatarIcon(
|
||||||
|
contactId: contact.userId,
|
||||||
|
fontSize: 17,
|
||||||
|
),
|
||||||
|
trailing: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: contact.requested
|
||||||
|
? requestedActions(context, contact)
|
||||||
|
: sendRequestActions(context, contact),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -11,6 +11,7 @@ import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/providers/purchases.provider.dart';
|
import 'package:twonly/src/providers/purchases.provider.dart';
|
||||||
import 'package:twonly/src/services/subscription.service.dart';
|
import 'package:twonly/src/services/subscription.service.dart';
|
||||||
|
import 'package:twonly/src/themes/light.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/utils/storage.dart';
|
import 'package:twonly/src/utils/storage.dart';
|
||||||
import 'package:twonly/src/views/chats/chat_list_components/feedback_btn.dart';
|
import 'package:twonly/src/views/chats/chat_list_components/feedback_btn.dart';
|
||||||
|
|
@ -48,6 +49,7 @@ class _ChatListViewState extends State<ChatListView> {
|
||||||
Future<void> initAsync() async {
|
Future<void> initAsync() async {
|
||||||
final stream = twonlyDB.groupsDao.watchGroupsForChatList();
|
final stream = twonlyDB.groupsDao.watchGroupsForChatList();
|
||||||
_contactsSub = stream.listen((groups) {
|
_contactsSub = stream.listen((groups) {
|
||||||
|
if (!mounted) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
_groupsNotPinned = groups
|
_groupsNotPinned = groups
|
||||||
.where((x) => !x.pinned && !x.archived)
|
.where((x) => !x.pinned && !x.archived)
|
||||||
|
|
@ -61,15 +63,17 @@ class _ChatListViewState extends State<ChatListView> {
|
||||||
.watchContactsRequestedCount()
|
.watchContactsRequestedCount()
|
||||||
.listen((update) {
|
.listen((update) {
|
||||||
if (update != null) {
|
if (update != null) {
|
||||||
|
if (!mounted) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
_countContactRequest = update;
|
_countContactRequest = update;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
_countContactRequestStream = twonlyDB.userDiscoveryDao
|
_countAnnouncedStream = twonlyDB.userDiscoveryDao
|
||||||
.watchNewAnnouncementsWithDataCount()
|
.watchNewAnnouncementsWithDataCount()
|
||||||
.listen((update) {
|
.listen((update) {
|
||||||
|
if (!mounted) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
_countAnnouncedUsers = update;
|
_countAnnouncedUsers = update;
|
||||||
});
|
});
|
||||||
|
|
@ -165,8 +169,8 @@ class _ChatListViewState extends State<ChatListView> {
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 40,
|
width: 40,
|
||||||
height: 40,
|
height: 40,
|
||||||
decoration: BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
color: context.color.primary,
|
color: primaryColor,
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -174,6 +178,10 @@ class _ChatListViewState extends State<ChatListView> {
|
||||||
),
|
),
|
||||||
Center(
|
Center(
|
||||||
child: NotificationBadge(
|
child: NotificationBadge(
|
||||||
|
backgroundColor: isDarkMode(context)
|
||||||
|
? Colors.white
|
||||||
|
: Colors.black,
|
||||||
|
textColor: isDarkMode(context) ? Colors.black : Colors.white,
|
||||||
count: (_countAnnouncedUsers + _countContactRequest)
|
count: (_countAnnouncedUsers + _countContactRequest)
|
||||||
.toString(),
|
.toString(),
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
|
|
|
||||||
|
|
@ -41,8 +41,6 @@ class TypingIndicator extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _TypingIndicatorState extends State<TypingIndicator> {
|
class _TypingIndicatorState extends State<TypingIndicator> {
|
||||||
late AnimationController _controller;
|
|
||||||
|
|
||||||
List<GroupMember> _groupMembers = [];
|
List<GroupMember> _groupMembers = [];
|
||||||
|
|
||||||
late StreamSubscription<List<(Contact, GroupMember)>> membersSub;
|
late StreamSubscription<List<(Contact, GroupMember)>> membersSub;
|
||||||
|
|
@ -75,7 +73,6 @@ class _TypingIndicatorState extends State<TypingIndicator> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_controller.dispose();
|
|
||||||
membersSub.cancel();
|
membersSub.cancel();
|
||||||
_periodicUpdate.cancel();
|
_periodicUpdate.cancel();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
|
|
@ -183,6 +180,12 @@ class _AnimatedTypingDotsState extends State<AnimatedTypingDots>
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_controller.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Row(
|
return Row(
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,13 @@ class NotificationBadge extends StatelessWidget {
|
||||||
const NotificationBadge({
|
const NotificationBadge({
|
||||||
required this.count,
|
required this.count,
|
||||||
required this.child,
|
required this.child,
|
||||||
|
this.backgroundColor = Colors.red,
|
||||||
|
this.textColor = Colors.white,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
final String count;
|
final String count;
|
||||||
|
final Color backgroundColor;
|
||||||
|
final Color textColor;
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -23,14 +27,14 @@ class NotificationBadge extends StatelessWidget {
|
||||||
height: 18,
|
height: 18,
|
||||||
width: 18,
|
width: 18,
|
||||||
child: CircleAvatar(
|
child: CircleAvatar(
|
||||||
backgroundColor: Colors.red,
|
backgroundColor: backgroundColor,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Transform.rotate(
|
child: Transform.rotate(
|
||||||
angle: infinity ? 90 * (3.141592653589793 / 180) : 0,
|
angle: infinity ? 90 * (3.141592653589793 / 180) : 0,
|
||||||
child: Text(
|
child: Text(
|
||||||
infinity ? '8' : count,
|
infinity ? '8' : count,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white, // Text color
|
color: textColor,
|
||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -87,14 +87,15 @@ class _ContactViewState extends State<ContactView> {
|
||||||
context.lang.contactRemoveBody,
|
context.lang.contactRemoveBody,
|
||||||
);
|
);
|
||||||
if (remove) {
|
if (remove) {
|
||||||
await twonlyDB.contactsDao.updateContact(
|
await twonlyDB.contactsDao.deleteContactByUserId(contact.userId);
|
||||||
contact.userId,
|
// await twonlyDB.contactsDao.updateContact(
|
||||||
const ContactsCompanion(
|
// contact.userId,
|
||||||
accepted: Value(false),
|
// const ContactsCompanion(
|
||||||
requested: Value(false),
|
// accepted: Value(false),
|
||||||
deletedByUser: Value(true),
|
// requested: Value(false),
|
||||||
),
|
// deletedByUser: Value(true),
|
||||||
);
|
// ),
|
||||||
|
// );
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
Navigator.popUntil(context, (route) => route.isFirst);
|
Navigator.popUntil(context, (route) => route.isFirst);
|
||||||
}
|
}
|
||||||
|
|
@ -221,6 +222,36 @@ class _ContactViewState extends State<ContactView> {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
if (gUser.isUserDiscoveryEnabled)
|
||||||
|
BetterListTile(
|
||||||
|
icon: FontAwesomeIcons.usersViewfinder,
|
||||||
|
text: context.lang.userDiscoverySettingsTitle,
|
||||||
|
subtitle:
|
||||||
|
!contact.userDiscoveryExcluded &&
|
||||||
|
contact.mediaSendCounter <
|
||||||
|
gUser.minimumRequiredImagesExchanged
|
||||||
|
? Text(
|
||||||
|
context.lang.contactUserDiscoveryImagesLeft(
|
||||||
|
gUser.minimumRequiredImagesExchanged -
|
||||||
|
contact.mediaSendCounter,
|
||||||
|
getContactDisplayName(contact),
|
||||||
|
),
|
||||||
|
style: const TextStyle(fontSize: 9),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
trailing: Transform.scale(
|
||||||
|
scale: 0.8,
|
||||||
|
child: Switch(
|
||||||
|
value: !contact.userDiscoveryExcluded,
|
||||||
|
onChanged: (a) async {
|
||||||
|
await twonlyDB.contactsDao.updateContact(
|
||||||
|
contact.userId,
|
||||||
|
ContactsCompanion(userDiscoveryExcluded: Value(!a)),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
BetterListTile(
|
BetterListTile(
|
||||||
icon: FontAwesomeIcons.flag,
|
icon: FontAwesomeIcons.flag,
|
||||||
text: context.lang.reportUser,
|
text: context.lang.reportUser,
|
||||||
|
|
|
||||||
|
|
@ -30,28 +30,29 @@ class _UserDiscoveryDisabledComponentState
|
||||||
child: ListView(
|
child: ListView(
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 45),
|
const SizedBox(height: 45),
|
||||||
const Text(
|
Text(
|
||||||
'twonly verzichten auf Telefonnummern, daher schlagen wir dir Freunde stattdessen über gemeinsame Kontakte vor – sicher und privat.',
|
context.lang.userDiscoveryDisabledIntro,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 30),
|
const SizedBox(height: 30),
|
||||||
RichText(
|
RichText(
|
||||||
text: TextSpan(
|
text: TextSpan(
|
||||||
children: formattedText(
|
children: formattedText(
|
||||||
'Deine Freundesliste ist für *Fremde komplett unsichtbar*. Nur deine Freunde können Teile davon sehen – und zwar nur die Personen, mit denen sie selbst *gemeinsame Freunde* haben.',
|
context,
|
||||||
|
context.lang.userDiscoveryDisabledInvisible,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 35),
|
const SizedBox(height: 35),
|
||||||
const Text(
|
Text(
|
||||||
'Du hast die Kontrolle',
|
context.lang.userDiscoveryDisabledYouHaveControl,
|
||||||
style: TextStyle(fontSize: 17),
|
style: const TextStyle(fontSize: 17),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
const Text(
|
Text(
|
||||||
'Entscheide selbst, wer deine Freunde sehen darf. Du kannst deine Meinung jederzeit ändern oder bestimmte Personen verstecken.',
|
context.lang.userDiscoveryDisabledDecide,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|
@ -61,10 +62,13 @@ class _UserDiscoveryDisabledComponentState
|
||||||
onPressed: initializeUserDiscoveryWithDefaultSettings,
|
onPressed: initializeUserDiscoveryWithDefaultSettings,
|
||||||
style: primaryColorButtonStyle.merge(
|
style: primaryColorButtonStyle.merge(
|
||||||
FilledButton.styleFrom(
|
FilledButton.styleFrom(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 32, vertical: 24),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 32,
|
||||||
|
vertical: 24,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: const Text('Mit Standardeinstellungen aktivieren'),
|
child: Text(context.lang.userDiscoveryDisabledEnableWithDefault),
|
||||||
),
|
),
|
||||||
|
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
|
|
@ -74,7 +78,7 @@ class _UserDiscoveryDisabledComponentState
|
||||||
child: FilledButton(
|
child: FilledButton(
|
||||||
onPressed: () {},
|
onPressed: () {},
|
||||||
style: secondaryGreyButtonStyle(context),
|
style: secondaryGreyButtonStyle(context),
|
||||||
child: const Text('Einstellungen anpassen'),
|
child: Text(context.lang.userDiscoveryDisabledCustomizeSettings),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 15),
|
const SizedBox(height: 15),
|
||||||
|
|
@ -83,7 +87,7 @@ class _UserDiscoveryDisabledComponentState
|
||||||
child: FilledButton(
|
child: FilledButton(
|
||||||
onPressed: () {},
|
onPressed: () {},
|
||||||
style: secondaryGreyButtonStyle(context),
|
style: secondaryGreyButtonStyle(context),
|
||||||
child: const Text('Mehr erfahren'),
|
child: Text(context.lang.userDiscoveryDisabledLearnMore),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,17 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'package:drift/drift.dart' show Value;
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/model/protobuf/client/generated/user_discovery/types.pb.dart';
|
import 'package:twonly/src/model/protobuf/client/generated/user_discovery/types.pb.dart';
|
||||||
import 'package:twonly/src/services/user_discovery.service.dart';
|
import 'package:twonly/src/services/user_discovery.service.dart';
|
||||||
|
import 'package:twonly/src/themes/light.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/components/alert_dialog.dart';
|
import 'package:twonly/src/views/components/alert_dialog.dart';
|
||||||
|
import 'package:twonly/src/views/components/avatar_icon.component.dart';
|
||||||
|
import 'package:twonly/src/views/components/user_context_menu.component.dart';
|
||||||
import 'package:twonly/src/views/settings/privacy/user_discovery/user_discovery_settings.view.dart';
|
import 'package:twonly/src/views/settings/privacy/user_discovery/user_discovery_settings.view.dart';
|
||||||
|
|
||||||
class UserDiscoveryEnabledComponent extends StatefulWidget {
|
class UserDiscoveryEnabledComponent extends StatefulWidget {
|
||||||
|
|
@ -57,8 +62,8 @@ class _UserDiscoveryEnabledComponentState
|
||||||
Future<void> _disableUserDiscovery() async {
|
Future<void> _disableUserDiscovery() async {
|
||||||
final ok = await showAlertDialog(
|
final ok = await showAlertDialog(
|
||||||
context,
|
context,
|
||||||
'Wirklich deaktivieren?',
|
context.lang.userDiscoveryEnabledDialogTitle,
|
||||||
'Wenn du das Feature „Freunde finden“ deaktivierst, werden dir keine Vorschläge mehr angezeigt. Du teilst neuen Kontakten dann auch nicht mehr deine Freunde.',
|
context.lang.userDiscoveryEnabledDisableWarning,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (ok) {
|
if (ok) {
|
||||||
|
|
@ -80,48 +85,98 @@ class _UserDiscoveryEnabledComponentState
|
||||||
backgroundColor: context.color.surfaceContainer,
|
backgroundColor: context.color.surfaceContainer,
|
||||||
collapsedShape: const RoundedRectangleBorder(),
|
collapsedShape: const RoundedRectangleBorder(),
|
||||||
tilePadding: const EdgeInsets.symmetric(horizontal: 17),
|
tilePadding: const EdgeInsets.symmetric(horizontal: 17),
|
||||||
title: const Text('Freunde die du teilst'),
|
title: Text(context.lang.userDiscoveryEnabledFriendsShared),
|
||||||
subtitle: const Text(
|
subtitle: Text(
|
||||||
'Du teilst nur Freunde, die diese Funktion ebenfalls aktiviert haben und die den von dir festgelegten Schwellenwert erreicht haben.',
|
context.lang.userDiscoveryEnabledFriendsSharedDesc,
|
||||||
style: TextStyle(fontSize: 10),
|
style: const TextStyle(fontSize: 10),
|
||||||
),
|
),
|
||||||
children: _contactsGettingAnnounced.isEmpty
|
children: _contactsGettingAnnounced.isEmpty
|
||||||
? [
|
? [
|
||||||
const Padding(
|
Padding(
|
||||||
padding: EdgeInsetsGeometry.symmetric(vertical: 12),
|
padding: const EdgeInsetsGeometry.symmetric(vertical: 12),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Bisher teilst du noch niemanden.',
|
context.lang.userDiscoveryEnabledNoFriendsShared,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
: _contactsGettingAnnounced.map((contact) {
|
: _contactsGettingAnnounced.map((contact) {
|
||||||
return Text(getContactDisplayName(contact));
|
final version =
|
||||||
|
UserDiscoveryService.getContactVersionTypedFromContact(
|
||||||
|
contact,
|
||||||
|
);
|
||||||
|
|
||||||
|
return UserContextMenu(
|
||||||
|
key: ValueKey(contact.userId),
|
||||||
|
contact: contact,
|
||||||
|
child: ListTile(
|
||||||
|
dense: true,
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
minVerticalPadding: 0,
|
||||||
|
title: Text(getContactDisplayName(contact)),
|
||||||
|
leading: AvatarIcon(
|
||||||
|
contactId: contact.userId,
|
||||||
|
fontSize: 17,
|
||||||
|
),
|
||||||
|
subtitle:
|
||||||
|
(version != null &&
|
||||||
|
(gUser.isDeveloper || !kReleaseMode))
|
||||||
|
? Text(
|
||||||
|
context.lang.userDiscoveryEnabledVersion(
|
||||||
|
'${version.announcement}.${version.promotion}',
|
||||||
|
),
|
||||||
|
style: const TextStyle(fontSize: 10),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
trailing: SizedBox(
|
||||||
|
height: 26,
|
||||||
|
child: FilledButton(
|
||||||
|
style: FilledButton.styleFrom(
|
||||||
|
padding: const EdgeInsets.only(right: 8, left: 8),
|
||||||
|
).merge(secondaryGreyButtonStyle(context)),
|
||||||
|
child: Text(
|
||||||
|
context.lang.userDiscoveryEnabledStopSharing,
|
||||||
|
style: const TextStyle(fontSize: 10),
|
||||||
|
),
|
||||||
|
onPressed: () async {
|
||||||
|
await twonlyDB.contactsDao.updateContact(
|
||||||
|
contact.userId,
|
||||||
|
const ContactsCompanion(
|
||||||
|
userDiscoveryExcluded: Value(true),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text('Einstellungen ändern'),
|
title: Text(context.lang.userDiscoveryEnabledChangeSettings),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
await context.navPush(const UserDiscoverySettingsView());
|
await context.navPush(const UserDiscoverySettingsView());
|
||||||
await _initAsync();
|
await _initAsync();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
const ListTile(
|
ListTile(
|
||||||
title: Text('Mehr erfahren'),
|
title: Text(context.lang.userDiscoveryDisabledLearnMore),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
'In unserem FAQ erklären wir dir wie das Feature "Freunde finden" funktioniert.',
|
context.lang.userDiscoveryEnabledFaq,
|
||||||
),
|
),
|
||||||
// onTap: _disableUserDiscovery,
|
// onTap: _disableUserDiscovery,
|
||||||
),
|
),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text('Deaktivieren'),
|
title: Text(context.lang.userDiscoveryActionDisable),
|
||||||
onTap: _disableUserDiscovery,
|
onTap: _disableUserDiscovery,
|
||||||
),
|
),
|
||||||
if (_version != null)
|
if (_version != null && (gUser.isDeveloper || !kReleaseMode))
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(
|
title: Text(
|
||||||
'Your version: ${_version!.announcement}.${_version!.promotion}',
|
context.lang.userDiscoveryEnabledYourVersion(
|
||||||
|
'${_version!.announcement}.${_version!.promotion}',
|
||||||
|
),
|
||||||
style: const TextStyle(color: Colors.grey, fontSize: 13),
|
style: const TextStyle(color: Colors.grey, fontSize: 13),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/services/user_discovery.service.dart';
|
import 'package:twonly/src/services/user_discovery.service.dart';
|
||||||
import 'package:twonly/src/themes/light.dart';
|
import 'package:twonly/src/themes/light.dart';
|
||||||
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/utils/storage.dart';
|
import 'package:twonly/src/utils/storage.dart';
|
||||||
|
|
||||||
class UserDiscoverySettingsView extends StatefulWidget {
|
class UserDiscoverySettingsView extends StatefulWidget {
|
||||||
|
|
@ -50,16 +51,16 @@ class _UserDiscoverySettingsViewState extends State<UserDiscoverySettingsView> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text('Freunde finden'),
|
title: Text(context.lang.userDiscoverySettingsTitle),
|
||||||
),
|
),
|
||||||
body: Padding(
|
body: Padding(
|
||||||
padding: const EdgeInsets.only(top: 10),
|
padding: const EdgeInsets.only(top: 10),
|
||||||
child: ListView(
|
child: ListView(
|
||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text('Anzahl an geteilten Bildern'),
|
title: Text(context.lang.userDiscoverySettingsMinImagesTitle),
|
||||||
subtitle: const Text(
|
subtitle: Text(
|
||||||
'Wähle die Mindestanzahl an Bildern, die du mit einer Person ausgetauscht haben musst, bevor du ihr deine Freunde sicher teilst.',
|
context.lang.userDiscoverySettingsMinImages,
|
||||||
),
|
),
|
||||||
trailing: SizedBox(
|
trailing: SizedBox(
|
||||||
width: 60,
|
width: 60,
|
||||||
|
|
@ -83,9 +84,9 @@ class _UserDiscoverySettingsViewState extends State<UserDiscoverySettingsView> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text('Anzahl an gemeinsame Freunde'),
|
title: Text(context.lang.userDiscoverySettingsMutualFriendsTitle),
|
||||||
subtitle: const Text(
|
subtitle: Text(
|
||||||
'Wähle aus, wie viele gemeinsame Freunde eine Person haben muss, damit du ihr vorgeschlagen wirst.',
|
context.lang.userDiscoverySettingsMutualFriends,
|
||||||
),
|
),
|
||||||
trailing: SizedBox(
|
trailing: SizedBox(
|
||||||
width: 60,
|
width: 60,
|
||||||
|
|
@ -120,13 +121,13 @@ class _UserDiscoverySettingsViewState extends State<UserDiscoverySettingsView> {
|
||||||
onPressed: _saveChanges,
|
onPressed: _saveChanges,
|
||||||
style: primaryColorButtonStyle.merge(
|
style: primaryColorButtonStyle.merge(
|
||||||
FilledButton.styleFrom(
|
FilledButton.styleFrom(
|
||||||
padding: EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 32,
|
horizontal: 32,
|
||||||
vertical: 24,
|
vertical: 24,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: const Text('Änderungen übernehmen'),
|
child: Text(context.lang.userDiscoverySettingsApply),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,9 @@ dependencies:
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
ffi: ^2.0.2
|
ffi: ^2.0.2
|
||||||
ffigen: ^11.0.0
|
ffigen: ^11.0.0
|
||||||
|
flutter_lints: ^2.0.0
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_lints: ^2.0.0
|
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
plugin:
|
plugin:
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import 'schema_v11.dart' as v11;
|
||||||
import 'schema_v12.dart' as v12;
|
import 'schema_v12.dart' as v12;
|
||||||
import 'schema_v13.dart' as v13;
|
import 'schema_v13.dart' as v13;
|
||||||
import 'schema_v14.dart' as v14;
|
import 'schema_v14.dart' as v14;
|
||||||
|
import 'schema_v15.dart' as v15;
|
||||||
|
|
||||||
class GeneratedHelper implements SchemaInstantiationHelper {
|
class GeneratedHelper implements SchemaInstantiationHelper {
|
||||||
@override
|
@override
|
||||||
|
|
@ -51,10 +52,28 @@ class GeneratedHelper implements SchemaInstantiationHelper {
|
||||||
return v13.DatabaseAtV13(db);
|
return v13.DatabaseAtV13(db);
|
||||||
case 14:
|
case 14:
|
||||||
return v14.DatabaseAtV14(db);
|
return v14.DatabaseAtV14(db);
|
||||||
|
case 15:
|
||||||
|
return v15.DatabaseAtV15(db);
|
||||||
default:
|
default:
|
||||||
throw MissingSchemaException(version, versions);
|
throw MissingSchemaException(version, versions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const versions = const [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
|
static const versions = const [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
5,
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
8,
|
||||||
|
9,
|
||||||
|
10,
|
||||||
|
11,
|
||||||
|
12,
|
||||||
|
13,
|
||||||
|
14,
|
||||||
|
15,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
9602
test/drift/twonly_db/generated/schema_v15.dart
Normal file
9602
test/drift/twonly_db/generated/schema_v15.dart
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue