mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-05-25 05:22:13 +00:00
handle scanned qr link via intent
This commit is contained in:
parent
7d09bd7283
commit
95c5d6a4f1
11 changed files with 245 additions and 162 deletions
13
lib/app.dart
13
lib/app.dart
|
|
@ -113,7 +113,6 @@ class AppMainWidget extends StatefulWidget {
|
||||||
|
|
||||||
class _AppMainWidgetState extends State<AppMainWidget> {
|
class _AppMainWidgetState extends State<AppMainWidget> {
|
||||||
bool _isUserCreated = false;
|
bool _isUserCreated = false;
|
||||||
bool _showDatabaseMigration = false;
|
|
||||||
bool _showOnboarding = true;
|
bool _showOnboarding = true;
|
||||||
bool _isLoaded = false;
|
bool _isLoaded = false;
|
||||||
bool _skipBackup = false;
|
bool _skipBackup = false;
|
||||||
|
|
@ -135,18 +134,12 @@ class _AppMainWidgetState extends State<AppMainWidget> {
|
||||||
// do not change in case twonly was already unlocked at some point
|
// do not change in case twonly was already unlocked at some point
|
||||||
_isTwonlyLocked = userService.currentUser.screenLockEnabled;
|
_isTwonlyLocked = userService.currentUser.screenLockEnabled;
|
||||||
}
|
}
|
||||||
if (userService.currentUser.appVersion < 62) {
|
} else {
|
||||||
_showDatabaseMigration = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_isUserCreated && !_showDatabaseMigration) {
|
|
||||||
// This means the user is in the onboarding screen, so start with the Proof of Work.
|
// This means the user is in the onboarding screen, so start with the Proof of Work.
|
||||||
|
|
||||||
final (proof, disabled) = await apiService.getProofOfWork();
|
final (proof, disabled) = await apiService.getProofOfWork();
|
||||||
if (proof != null) {
|
if (proof != null) {
|
||||||
Log.info('Starting with proof of work calculation.');
|
Log.info('Starting with proof of work calculation.');
|
||||||
// Starting with the proof of work.
|
|
||||||
_proofOfWork = (
|
_proofOfWork = (
|
||||||
calculatePoW(proof.prefix, proof.difficulty.toInt()),
|
calculatePoW(proof.prefix, proof.difficulty.toInt()),
|
||||||
false,
|
false,
|
||||||
|
|
@ -169,9 +162,7 @@ class _AppMainWidgetState extends State<AppMainWidget> {
|
||||||
|
|
||||||
late Widget child;
|
late Widget child;
|
||||||
|
|
||||||
if (_showDatabaseMigration) {
|
if (_isUserCreated) {
|
||||||
child = const Center(child: Text('Please reinstall twonly.'));
|
|
||||||
} else if (_isUserCreated) {
|
|
||||||
if (_isTwonlyLocked) {
|
if (_isTwonlyLocked) {
|
||||||
child = UnlockTwonlyView(
|
child = UnlockTwonlyView(
|
||||||
callbackOnSuccess: () => setState(() {
|
callbackOnSuccess: () => setState(() {
|
||||||
|
|
|
||||||
|
|
@ -99,21 +99,6 @@ class ContactsDao extends DatabaseAccessor<TwonlyDB> with _$ContactsDaoMixin {
|
||||||
)..where((t) => t.userId.equals(userid))).watchSingleOrNull();
|
)..where((t) => t.userId.equals(userid))).watchSingleOrNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream<(Contact, bool)?> watchContactAndVerificationState(int userid) {
|
|
||||||
final query = (select(contacts)..where((t) => t.userId.equals(userid))).join([
|
|
||||||
leftOuterJoin(
|
|
||||||
keyVerifications,
|
|
||||||
keyVerifications.contactId.equalsExp(contacts.userId),
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
return query
|
|
||||||
.map((row) => (
|
|
||||||
row.readTable(contacts),
|
|
||||||
row.readTableOrNull(keyVerifications) != null,
|
|
||||||
))
|
|
||||||
.watchSingleOrNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<Contact>> getAllContacts() {
|
Future<List<Contact>> getAllContacts() {
|
||||||
return select(contacts).get();
|
return select(contacts).get();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,18 +47,14 @@ enum VerificationType {
|
||||||
|
|
||||||
@DataClassName('KeyVerification')
|
@DataClassName('KeyVerification')
|
||||||
class KeyVerifications extends Table {
|
class KeyVerifications extends Table {
|
||||||
|
IntColumn get verificationId => integer().autoIncrement()();
|
||||||
IntColumn get contactId => integer().references(
|
IntColumn get contactId => integer().references(
|
||||||
Contacts,
|
Contacts,
|
||||||
#userId,
|
#userId,
|
||||||
onDelete: KeyAction.cascade,
|
onDelete: KeyAction.cascade,
|
||||||
)();
|
)();
|
||||||
|
|
||||||
TextColumn get type => textEnum<VerificationType>()();
|
TextColumn get type => textEnum<VerificationType>()();
|
||||||
|
|
||||||
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
|
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
|
||||||
|
|
||||||
@override
|
|
||||||
Set<Column> get primaryKey => {contactId};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@DataClassName('VerificationToken')
|
@DataClassName('VerificationToken')
|
||||||
|
|
|
||||||
|
|
@ -9041,6 +9041,21 @@ class $KeyVerificationsTable extends KeyVerifications
|
||||||
final GeneratedDatabase attachedDatabase;
|
final GeneratedDatabase attachedDatabase;
|
||||||
final String? _alias;
|
final String? _alias;
|
||||||
$KeyVerificationsTable(this.attachedDatabase, [this._alias]);
|
$KeyVerificationsTable(this.attachedDatabase, [this._alias]);
|
||||||
|
static const VerificationMeta _verificationIdMeta = const VerificationMeta(
|
||||||
|
'verificationId',
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
late final GeneratedColumn<int> verificationId = GeneratedColumn<int>(
|
||||||
|
'verification_id',
|
||||||
|
aliasedName,
|
||||||
|
false,
|
||||||
|
hasAutoIncrement: true,
|
||||||
|
type: DriftSqlType.int,
|
||||||
|
requiredDuringInsert: false,
|
||||||
|
defaultConstraints: GeneratedColumn.constraintIsAlways(
|
||||||
|
'PRIMARY KEY AUTOINCREMENT',
|
||||||
|
),
|
||||||
|
);
|
||||||
static const VerificationMeta _contactIdMeta = const VerificationMeta(
|
static const VerificationMeta _contactIdMeta = const VerificationMeta(
|
||||||
'contactId',
|
'contactId',
|
||||||
);
|
);
|
||||||
|
|
@ -9050,7 +9065,7 @@ class $KeyVerificationsTable extends KeyVerifications
|
||||||
aliasedName,
|
aliasedName,
|
||||||
false,
|
false,
|
||||||
type: DriftSqlType.int,
|
type: DriftSqlType.int,
|
||||||
requiredDuringInsert: false,
|
requiredDuringInsert: true,
|
||||||
defaultConstraints: GeneratedColumn.constraintIsAlways(
|
defaultConstraints: GeneratedColumn.constraintIsAlways(
|
||||||
'REFERENCES contacts (user_id) ON DELETE CASCADE',
|
'REFERENCES contacts (user_id) ON DELETE CASCADE',
|
||||||
),
|
),
|
||||||
|
|
@ -9077,7 +9092,12 @@ class $KeyVerificationsTable extends KeyVerifications
|
||||||
defaultValue: currentDateAndTime,
|
defaultValue: currentDateAndTime,
|
||||||
);
|
);
|
||||||
@override
|
@override
|
||||||
List<GeneratedColumn> get $columns => [contactId, type, createdAt];
|
List<GeneratedColumn> get $columns => [
|
||||||
|
verificationId,
|
||||||
|
contactId,
|
||||||
|
type,
|
||||||
|
createdAt,
|
||||||
|
];
|
||||||
@override
|
@override
|
||||||
String get aliasedName => _alias ?? actualTableName;
|
String get aliasedName => _alias ?? actualTableName;
|
||||||
@override
|
@override
|
||||||
|
|
@ -9090,11 +9110,22 @@ class $KeyVerificationsTable extends KeyVerifications
|
||||||
}) {
|
}) {
|
||||||
final context = VerificationContext();
|
final context = VerificationContext();
|
||||||
final data = instance.toColumns(true);
|
final data = instance.toColumns(true);
|
||||||
|
if (data.containsKey('verification_id')) {
|
||||||
|
context.handle(
|
||||||
|
_verificationIdMeta,
|
||||||
|
verificationId.isAcceptableOrUnknown(
|
||||||
|
data['verification_id']!,
|
||||||
|
_verificationIdMeta,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
if (data.containsKey('contact_id')) {
|
if (data.containsKey('contact_id')) {
|
||||||
context.handle(
|
context.handle(
|
||||||
_contactIdMeta,
|
_contactIdMeta,
|
||||||
contactId.isAcceptableOrUnknown(data['contact_id']!, _contactIdMeta),
|
contactId.isAcceptableOrUnknown(data['contact_id']!, _contactIdMeta),
|
||||||
);
|
);
|
||||||
|
} else if (isInserting) {
|
||||||
|
context.missing(_contactIdMeta);
|
||||||
}
|
}
|
||||||
if (data.containsKey('created_at')) {
|
if (data.containsKey('created_at')) {
|
||||||
context.handle(
|
context.handle(
|
||||||
|
|
@ -9106,11 +9137,15 @@ class $KeyVerificationsTable extends KeyVerifications
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Set<GeneratedColumn> get $primaryKey => {contactId};
|
Set<GeneratedColumn> get $primaryKey => {verificationId};
|
||||||
@override
|
@override
|
||||||
KeyVerification map(Map<String, dynamic> data, {String? tablePrefix}) {
|
KeyVerification map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||||
return KeyVerification(
|
return KeyVerification(
|
||||||
|
verificationId: attachedDatabase.typeMapping.read(
|
||||||
|
DriftSqlType.int,
|
||||||
|
data['${effectivePrefix}verification_id'],
|
||||||
|
)!,
|
||||||
contactId: attachedDatabase.typeMapping.read(
|
contactId: attachedDatabase.typeMapping.read(
|
||||||
DriftSqlType.int,
|
DriftSqlType.int,
|
||||||
data['${effectivePrefix}contact_id'],
|
data['${effectivePrefix}contact_id'],
|
||||||
|
|
@ -9138,10 +9173,12 @@ class $KeyVerificationsTable extends KeyVerifications
|
||||||
}
|
}
|
||||||
|
|
||||||
class KeyVerification extends DataClass implements Insertable<KeyVerification> {
|
class KeyVerification extends DataClass implements Insertable<KeyVerification> {
|
||||||
|
final int verificationId;
|
||||||
final int contactId;
|
final int contactId;
|
||||||
final VerificationType type;
|
final VerificationType type;
|
||||||
final DateTime createdAt;
|
final DateTime createdAt;
|
||||||
const KeyVerification({
|
const KeyVerification({
|
||||||
|
required this.verificationId,
|
||||||
required this.contactId,
|
required this.contactId,
|
||||||
required this.type,
|
required this.type,
|
||||||
required this.createdAt,
|
required this.createdAt,
|
||||||
|
|
@ -9149,6 +9186,7 @@ class KeyVerification extends DataClass implements Insertable<KeyVerification> {
|
||||||
@override
|
@override
|
||||||
Map<String, Expression> toColumns(bool nullToAbsent) {
|
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||||
final map = <String, Expression>{};
|
final map = <String, Expression>{};
|
||||||
|
map['verification_id'] = Variable<int>(verificationId);
|
||||||
map['contact_id'] = Variable<int>(contactId);
|
map['contact_id'] = Variable<int>(contactId);
|
||||||
{
|
{
|
||||||
map['type'] = Variable<String>(
|
map['type'] = Variable<String>(
|
||||||
|
|
@ -9161,6 +9199,7 @@ class KeyVerification extends DataClass implements Insertable<KeyVerification> {
|
||||||
|
|
||||||
KeyVerificationsCompanion toCompanion(bool nullToAbsent) {
|
KeyVerificationsCompanion toCompanion(bool nullToAbsent) {
|
||||||
return KeyVerificationsCompanion(
|
return KeyVerificationsCompanion(
|
||||||
|
verificationId: Value(verificationId),
|
||||||
contactId: Value(contactId),
|
contactId: Value(contactId),
|
||||||
type: Value(type),
|
type: Value(type),
|
||||||
createdAt: Value(createdAt),
|
createdAt: Value(createdAt),
|
||||||
|
|
@ -9173,6 +9212,7 @@ class KeyVerification extends DataClass implements Insertable<KeyVerification> {
|
||||||
}) {
|
}) {
|
||||||
serializer ??= driftRuntimeOptions.defaultSerializer;
|
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||||
return KeyVerification(
|
return KeyVerification(
|
||||||
|
verificationId: serializer.fromJson<int>(json['verificationId']),
|
||||||
contactId: serializer.fromJson<int>(json['contactId']),
|
contactId: serializer.fromJson<int>(json['contactId']),
|
||||||
type: $KeyVerificationsTable.$convertertype.fromJson(
|
type: $KeyVerificationsTable.$convertertype.fromJson(
|
||||||
serializer.fromJson<String>(json['type']),
|
serializer.fromJson<String>(json['type']),
|
||||||
|
|
@ -9184,6 +9224,7 @@ class KeyVerification extends DataClass implements Insertable<KeyVerification> {
|
||||||
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
|
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
|
||||||
serializer ??= driftRuntimeOptions.defaultSerializer;
|
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||||
return <String, dynamic>{
|
return <String, dynamic>{
|
||||||
|
'verificationId': serializer.toJson<int>(verificationId),
|
||||||
'contactId': serializer.toJson<int>(contactId),
|
'contactId': serializer.toJson<int>(contactId),
|
||||||
'type': serializer.toJson<String>(
|
'type': serializer.toJson<String>(
|
||||||
$KeyVerificationsTable.$convertertype.toJson(type),
|
$KeyVerificationsTable.$convertertype.toJson(type),
|
||||||
|
|
@ -9193,16 +9234,21 @@ class KeyVerification extends DataClass implements Insertable<KeyVerification> {
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyVerification copyWith({
|
KeyVerification copyWith({
|
||||||
|
int? verificationId,
|
||||||
int? contactId,
|
int? contactId,
|
||||||
VerificationType? type,
|
VerificationType? type,
|
||||||
DateTime? createdAt,
|
DateTime? createdAt,
|
||||||
}) => KeyVerification(
|
}) => KeyVerification(
|
||||||
|
verificationId: verificationId ?? this.verificationId,
|
||||||
contactId: contactId ?? this.contactId,
|
contactId: contactId ?? this.contactId,
|
||||||
type: type ?? this.type,
|
type: type ?? this.type,
|
||||||
createdAt: createdAt ?? this.createdAt,
|
createdAt: createdAt ?? this.createdAt,
|
||||||
);
|
);
|
||||||
KeyVerification copyWithCompanion(KeyVerificationsCompanion data) {
|
KeyVerification copyWithCompanion(KeyVerificationsCompanion data) {
|
||||||
return KeyVerification(
|
return KeyVerification(
|
||||||
|
verificationId: data.verificationId.present
|
||||||
|
? data.verificationId.value
|
||||||
|
: this.verificationId,
|
||||||
contactId: data.contactId.present ? data.contactId.value : this.contactId,
|
contactId: data.contactId.present ? data.contactId.value : this.contactId,
|
||||||
type: data.type.present ? data.type.value : this.type,
|
type: data.type.present ? data.type.value : this.type,
|
||||||
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
|
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
|
||||||
|
|
@ -9212,6 +9258,7 @@ class KeyVerification extends DataClass implements Insertable<KeyVerification> {
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return (StringBuffer('KeyVerification(')
|
return (StringBuffer('KeyVerification(')
|
||||||
|
..write('verificationId: $verificationId, ')
|
||||||
..write('contactId: $contactId, ')
|
..write('contactId: $contactId, ')
|
||||||
..write('type: $type, ')
|
..write('type: $type, ')
|
||||||
..write('createdAt: $createdAt')
|
..write('createdAt: $createdAt')
|
||||||
|
|
@ -9220,36 +9267,43 @@ class KeyVerification extends DataClass implements Insertable<KeyVerification> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(contactId, type, createdAt);
|
int get hashCode => Object.hash(verificationId, contactId, type, createdAt);
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
identical(this, other) ||
|
identical(this, other) ||
|
||||||
(other is KeyVerification &&
|
(other is KeyVerification &&
|
||||||
|
other.verificationId == this.verificationId &&
|
||||||
other.contactId == this.contactId &&
|
other.contactId == this.contactId &&
|
||||||
other.type == this.type &&
|
other.type == this.type &&
|
||||||
other.createdAt == this.createdAt);
|
other.createdAt == this.createdAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
class KeyVerificationsCompanion extends UpdateCompanion<KeyVerification> {
|
class KeyVerificationsCompanion extends UpdateCompanion<KeyVerification> {
|
||||||
|
final Value<int> verificationId;
|
||||||
final Value<int> contactId;
|
final Value<int> contactId;
|
||||||
final Value<VerificationType> type;
|
final Value<VerificationType> type;
|
||||||
final Value<DateTime> createdAt;
|
final Value<DateTime> createdAt;
|
||||||
const KeyVerificationsCompanion({
|
const KeyVerificationsCompanion({
|
||||||
|
this.verificationId = const Value.absent(),
|
||||||
this.contactId = const Value.absent(),
|
this.contactId = const Value.absent(),
|
||||||
this.type = const Value.absent(),
|
this.type = const Value.absent(),
|
||||||
this.createdAt = const Value.absent(),
|
this.createdAt = const Value.absent(),
|
||||||
});
|
});
|
||||||
KeyVerificationsCompanion.insert({
|
KeyVerificationsCompanion.insert({
|
||||||
this.contactId = const Value.absent(),
|
this.verificationId = const Value.absent(),
|
||||||
|
required int contactId,
|
||||||
required VerificationType type,
|
required VerificationType type,
|
||||||
this.createdAt = const Value.absent(),
|
this.createdAt = const Value.absent(),
|
||||||
}) : type = Value(type);
|
}) : contactId = Value(contactId),
|
||||||
|
type = Value(type);
|
||||||
static Insertable<KeyVerification> custom({
|
static Insertable<KeyVerification> custom({
|
||||||
|
Expression<int>? verificationId,
|
||||||
Expression<int>? contactId,
|
Expression<int>? contactId,
|
||||||
Expression<String>? type,
|
Expression<String>? type,
|
||||||
Expression<DateTime>? createdAt,
|
Expression<DateTime>? createdAt,
|
||||||
}) {
|
}) {
|
||||||
return RawValuesInsertable({
|
return RawValuesInsertable({
|
||||||
|
if (verificationId != null) 'verification_id': verificationId,
|
||||||
if (contactId != null) 'contact_id': contactId,
|
if (contactId != null) 'contact_id': contactId,
|
||||||
if (type != null) 'type': type,
|
if (type != null) 'type': type,
|
||||||
if (createdAt != null) 'created_at': createdAt,
|
if (createdAt != null) 'created_at': createdAt,
|
||||||
|
|
@ -9257,11 +9311,13 @@ class KeyVerificationsCompanion extends UpdateCompanion<KeyVerification> {
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyVerificationsCompanion copyWith({
|
KeyVerificationsCompanion copyWith({
|
||||||
|
Value<int>? verificationId,
|
||||||
Value<int>? contactId,
|
Value<int>? contactId,
|
||||||
Value<VerificationType>? type,
|
Value<VerificationType>? type,
|
||||||
Value<DateTime>? createdAt,
|
Value<DateTime>? createdAt,
|
||||||
}) {
|
}) {
|
||||||
return KeyVerificationsCompanion(
|
return KeyVerificationsCompanion(
|
||||||
|
verificationId: verificationId ?? this.verificationId,
|
||||||
contactId: contactId ?? this.contactId,
|
contactId: contactId ?? this.contactId,
|
||||||
type: type ?? this.type,
|
type: type ?? this.type,
|
||||||
createdAt: createdAt ?? this.createdAt,
|
createdAt: createdAt ?? this.createdAt,
|
||||||
|
|
@ -9271,6 +9327,9 @@ class KeyVerificationsCompanion extends UpdateCompanion<KeyVerification> {
|
||||||
@override
|
@override
|
||||||
Map<String, Expression> toColumns(bool nullToAbsent) {
|
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||||
final map = <String, Expression>{};
|
final map = <String, Expression>{};
|
||||||
|
if (verificationId.present) {
|
||||||
|
map['verification_id'] = Variable<int>(verificationId.value);
|
||||||
|
}
|
||||||
if (contactId.present) {
|
if (contactId.present) {
|
||||||
map['contact_id'] = Variable<int>(contactId.value);
|
map['contact_id'] = Variable<int>(contactId.value);
|
||||||
}
|
}
|
||||||
|
|
@ -9288,6 +9347,7 @@ class KeyVerificationsCompanion extends UpdateCompanion<KeyVerification> {
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return (StringBuffer('KeyVerificationsCompanion(')
|
return (StringBuffer('KeyVerificationsCompanion(')
|
||||||
|
..write('verificationId: $verificationId, ')
|
||||||
..write('contactId: $contactId, ')
|
..write('contactId: $contactId, ')
|
||||||
..write('type: $type, ')
|
..write('type: $type, ')
|
||||||
..write('createdAt: $createdAt')
|
..write('createdAt: $createdAt')
|
||||||
|
|
@ -19473,12 +19533,14 @@ typedef $$GroupHistoriesTableProcessedTableManager =
|
||||||
>;
|
>;
|
||||||
typedef $$KeyVerificationsTableCreateCompanionBuilder =
|
typedef $$KeyVerificationsTableCreateCompanionBuilder =
|
||||||
KeyVerificationsCompanion Function({
|
KeyVerificationsCompanion Function({
|
||||||
Value<int> contactId,
|
Value<int> verificationId,
|
||||||
|
required int contactId,
|
||||||
required VerificationType type,
|
required VerificationType type,
|
||||||
Value<DateTime> createdAt,
|
Value<DateTime> createdAt,
|
||||||
});
|
});
|
||||||
typedef $$KeyVerificationsTableUpdateCompanionBuilder =
|
typedef $$KeyVerificationsTableUpdateCompanionBuilder =
|
||||||
KeyVerificationsCompanion Function({
|
KeyVerificationsCompanion Function({
|
||||||
|
Value<int> verificationId,
|
||||||
Value<int> contactId,
|
Value<int> contactId,
|
||||||
Value<VerificationType> type,
|
Value<VerificationType> type,
|
||||||
Value<DateTime> createdAt,
|
Value<DateTime> createdAt,
|
||||||
|
|
@ -19522,6 +19584,11 @@ class $$KeyVerificationsTableFilterComposer
|
||||||
super.$addJoinBuilderToRootComposer,
|
super.$addJoinBuilderToRootComposer,
|
||||||
super.$removeJoinBuilderFromRootComposer,
|
super.$removeJoinBuilderFromRootComposer,
|
||||||
});
|
});
|
||||||
|
ColumnFilters<int> get verificationId => $composableBuilder(
|
||||||
|
column: $table.verificationId,
|
||||||
|
builder: (column) => ColumnFilters(column),
|
||||||
|
);
|
||||||
|
|
||||||
ColumnWithTypeConverterFilters<VerificationType, VerificationType, String>
|
ColumnWithTypeConverterFilters<VerificationType, VerificationType, String>
|
||||||
get type => $composableBuilder(
|
get type => $composableBuilder(
|
||||||
column: $table.type,
|
column: $table.type,
|
||||||
|
|
@ -19566,6 +19633,11 @@ class $$KeyVerificationsTableOrderingComposer
|
||||||
super.$addJoinBuilderToRootComposer,
|
super.$addJoinBuilderToRootComposer,
|
||||||
super.$removeJoinBuilderFromRootComposer,
|
super.$removeJoinBuilderFromRootComposer,
|
||||||
});
|
});
|
||||||
|
ColumnOrderings<int> get verificationId => $composableBuilder(
|
||||||
|
column: $table.verificationId,
|
||||||
|
builder: (column) => ColumnOrderings(column),
|
||||||
|
);
|
||||||
|
|
||||||
ColumnOrderings<String> get type => $composableBuilder(
|
ColumnOrderings<String> get type => $composableBuilder(
|
||||||
column: $table.type,
|
column: $table.type,
|
||||||
builder: (column) => ColumnOrderings(column),
|
builder: (column) => ColumnOrderings(column),
|
||||||
|
|
@ -19609,6 +19681,11 @@ class $$KeyVerificationsTableAnnotationComposer
|
||||||
super.$addJoinBuilderToRootComposer,
|
super.$addJoinBuilderToRootComposer,
|
||||||
super.$removeJoinBuilderFromRootComposer,
|
super.$removeJoinBuilderFromRootComposer,
|
||||||
});
|
});
|
||||||
|
GeneratedColumn<int> get verificationId => $composableBuilder(
|
||||||
|
column: $table.verificationId,
|
||||||
|
builder: (column) => column,
|
||||||
|
);
|
||||||
|
|
||||||
GeneratedColumnWithTypeConverter<VerificationType, String> get type =>
|
GeneratedColumnWithTypeConverter<VerificationType, String> get type =>
|
||||||
$composableBuilder(column: $table.type, builder: (column) => column);
|
$composableBuilder(column: $table.type, builder: (column) => column);
|
||||||
|
|
||||||
|
|
@ -19669,20 +19746,24 @@ class $$KeyVerificationsTableTableManager
|
||||||
$$KeyVerificationsTableAnnotationComposer($db: db, $table: table),
|
$$KeyVerificationsTableAnnotationComposer($db: db, $table: table),
|
||||||
updateCompanionCallback:
|
updateCompanionCallback:
|
||||||
({
|
({
|
||||||
|
Value<int> verificationId = const Value.absent(),
|
||||||
Value<int> contactId = const Value.absent(),
|
Value<int> contactId = const Value.absent(),
|
||||||
Value<VerificationType> type = const Value.absent(),
|
Value<VerificationType> type = const Value.absent(),
|
||||||
Value<DateTime> createdAt = const Value.absent(),
|
Value<DateTime> createdAt = const Value.absent(),
|
||||||
}) => KeyVerificationsCompanion(
|
}) => KeyVerificationsCompanion(
|
||||||
|
verificationId: verificationId,
|
||||||
contactId: contactId,
|
contactId: contactId,
|
||||||
type: type,
|
type: type,
|
||||||
createdAt: createdAt,
|
createdAt: createdAt,
|
||||||
),
|
),
|
||||||
createCompanionCallback:
|
createCompanionCallback:
|
||||||
({
|
({
|
||||||
Value<int> contactId = const Value.absent(),
|
Value<int> verificationId = const Value.absent(),
|
||||||
|
required int contactId,
|
||||||
required VerificationType type,
|
required VerificationType type,
|
||||||
Value<DateTime> createdAt = const Value.absent(),
|
Value<DateTime> createdAt = const Value.absent(),
|
||||||
}) => KeyVerificationsCompanion.insert(
|
}) => KeyVerificationsCompanion.insert(
|
||||||
|
verificationId: verificationId,
|
||||||
contactId: contactId,
|
contactId: contactId,
|
||||||
type: type,
|
type: type,
|
||||||
createdAt: createdAt,
|
createdAt: createdAt,
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import 'package:twonly/src/services/api/mediafiles/upload.api.dart';
|
||||||
import 'package:twonly/src/services/signal/session.signal.dart';
|
import 'package:twonly/src/services/signal/session.signal.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
|
import 'package:twonly/src/utils/qr.utils.dart';
|
||||||
import 'package:twonly/src/visual/components/alert.dialog.dart';
|
import 'package:twonly/src/visual/components/alert.dialog.dart';
|
||||||
import 'package:twonly/src/visual/views/camera/share_image_editor.view.dart';
|
import 'package:twonly/src/visual/views/camera/share_image_editor.view.dart';
|
||||||
import 'package:twonly/src/visual/views/chats/add_new_user.view.dart';
|
import 'package:twonly/src/visual/views/chats/add_new_user.view.dart';
|
||||||
|
|
@ -25,6 +26,41 @@ Future<bool> handleIntentUrl(BuildContext context, Uri uri) async {
|
||||||
if (uri.host != 'me.twonly.eu') return false;
|
if (uri.host != 'me.twonly.eu') return false;
|
||||||
if (uri.hasEmptyPath) return false;
|
if (uri.hasEmptyPath) return false;
|
||||||
|
|
||||||
|
// Check if this is the QR code link which was
|
||||||
|
// therefore scanned with the system camera
|
||||||
|
|
||||||
|
if (uri.toString().startsWith(QrCodeUtils.linkPrefix)) {
|
||||||
|
final result = await QrCodeUtils.handleQrCodeLink(uri.toString());
|
||||||
|
|
||||||
|
if (!context.mounted) return false;
|
||||||
|
|
||||||
|
if (result != null) {
|
||||||
|
final (profile, contact, verificationOk) = result;
|
||||||
|
|
||||||
|
if (profile.username == userService.currentUser.username) {
|
||||||
|
await context.push(Routes.settingsPublicProfile);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contact != null) {
|
||||||
|
if (verificationOk) {
|
||||||
|
await context.push(Routes.profileContact(contact.userId));
|
||||||
|
} else {
|
||||||
|
await _pubKeysDoNotMatch(context, contact.username);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await context.navPush(
|
||||||
|
AddNewUserView(
|
||||||
|
username: profile.username,
|
||||||
|
publicKey: Uint8List.fromList(profile.publicIdentityKey),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
final publicKey = uri.hasFragment ? uri.fragment : null;
|
final publicKey = uri.hasFragment ? uri.fragment : null;
|
||||||
final userPaths = uri.path.split('/');
|
final userPaths = uri.path.split('/');
|
||||||
if (userPaths.length != 2) return false;
|
if (userPaths.length != 2) return false;
|
||||||
|
|
@ -40,25 +76,26 @@ Future<bool> handleIntentUrl(BuildContext context, Uri uri) async {
|
||||||
Log.info(
|
Log.info(
|
||||||
'Opened via deep link!: username = $username public_key = ${uri.fragment}',
|
'Opened via deep link!: username = $username public_key = ${uri.fragment}',
|
||||||
);
|
);
|
||||||
|
|
||||||
final contacts = await twonlyDB.contactsDao.getContactsByUsername(username);
|
final contacts = await twonlyDB.contactsDao.getContactsByUsername(username);
|
||||||
|
if (!context.mounted) return true;
|
||||||
|
|
||||||
if (contacts.isEmpty) {
|
if (contacts.isEmpty) {
|
||||||
if (!context.mounted) return true;
|
// User does not yet exists, making a request...
|
||||||
Uint8List? publicKeyBytes;
|
Uint8List? publicKeyBytes;
|
||||||
if (publicKey != null) {
|
if (publicKey != null) {
|
||||||
publicKeyBytes = base64Url.decode(publicKey);
|
publicKeyBytes = base64Url.decode(publicKey);
|
||||||
}
|
}
|
||||||
await Navigator.push(
|
await context.navPush(
|
||||||
context,
|
AddNewUserView(
|
||||||
MaterialPageRoute(
|
username: username,
|
||||||
builder: (context) {
|
publicKey: publicKeyBytes,
|
||||||
return AddNewUserView(
|
|
||||||
username: username,
|
|
||||||
publicKey: publicKeyBytes,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else if (publicKey != null) {
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (publicKey != null) {
|
||||||
try {
|
try {
|
||||||
final contact = contacts.first;
|
final contact = contacts.first;
|
||||||
final storedPublicKey = await getPublicKeyFromContact(contact.userId);
|
final storedPublicKey = await getPublicKeyFromContact(contact.userId);
|
||||||
|
|
@ -85,20 +122,25 @@ Future<bool> handleIntentUrl(BuildContext context, Uri uri) async {
|
||||||
await context.push(Routes.profileContact(contact.userId));
|
await context.push(Routes.profileContact(contact.userId));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await showAlertDialog(
|
await _pubKeysDoNotMatch(context, contact.username);
|
||||||
context,
|
|
||||||
context.lang.couldNotVerifyUsername(contact.username),
|
|
||||||
context.lang.linkPubkeyDoesNotMatch,
|
|
||||||
customCancel: '',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Log.warn(e);
|
Log.warn(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _pubKeysDoNotMatch(BuildContext context, String username) async {
|
||||||
|
await showAlertDialog(
|
||||||
|
context,
|
||||||
|
context.lang.couldNotVerifyUsername(username),
|
||||||
|
context.lang.linkPubkeyDoesNotMatch,
|
||||||
|
customCancel: '',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> handleIntentMediaFile(
|
Future<void> handleIntentMediaFile(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
String filePath,
|
String filePath,
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import 'package:twonly/src/services/api/mediafiles/upload.api.dart';
|
||||||
import 'package:twonly/src/services/user.service.dart';
|
import 'package:twonly/src/services/user.service.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/utils/qr.dart';
|
import 'package:twonly/src/utils/qr.utils.dart';
|
||||||
import 'package:twonly/src/visual/components/avatar_icon.comp.dart';
|
import 'package:twonly/src/visual/components/avatar_icon.comp.dart';
|
||||||
import 'package:twonly/src/visual/helpers/media_view_sizing.helper.dart';
|
import 'package:twonly/src/visual/helpers/media_view_sizing.helper.dart';
|
||||||
import 'package:twonly/src/visual/helpers/screenshot.helper.dart';
|
import 'package:twonly/src/visual/helpers/screenshot.helper.dart';
|
||||||
|
|
@ -377,7 +377,7 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
// shouldReturn is null when the user used the back button
|
// shouldReturn is null when the user used the back button
|
||||||
if (shouldReturn != null && shouldReturn) {
|
if (shouldReturn != null && shouldReturn) {
|
||||||
if (widget.sendToGroup == null) {
|
if (widget.sendToGroup == null) {
|
||||||
globalUpdateOfHomeViewPageIndex(0);
|
HomeViewState.streamHomeViewPageIndex.add(0);
|
||||||
} else if (mounted) {
|
} else if (mounted) {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/model/protobuf/client/generated/qr.pb.dart';
|
import 'package:twonly/src/model/protobuf/client/generated/qr.pb.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/utils/qr.dart';
|
import 'package:twonly/src/utils/qr.utils.dart';
|
||||||
import 'package:twonly/src/visual/helpers/screenshot.helper.dart';
|
import 'package:twonly/src/visual/helpers/screenshot.helper.dart';
|
||||||
import 'package:twonly/src/visual/views/camera/camera_preview_components/camera_preview_controller_view.dart';
|
import 'package:twonly/src/visual/views/camera/camera_preview_components/camera_preview_controller_view.dart';
|
||||||
import 'package:twonly/src/visual/views/camera/camera_preview_components/face_filters.dart';
|
import 'package:twonly/src/visual/views/camera/camera_preview_components/face_filters.dart';
|
||||||
|
|
@ -45,7 +45,7 @@ class MainCameraController {
|
||||||
bool initCameraStarted = true;
|
bool initCameraStarted = true;
|
||||||
Map<int, ScannedVerifiedContact> contactsVerified = {};
|
Map<int, ScannedVerifiedContact> contactsVerified = {};
|
||||||
Map<int, ScannedNewProfile> scannedNewProfiles = {};
|
Map<int, ScannedNewProfile> scannedNewProfiles = {};
|
||||||
Set<String> _handledProfileLinks = {};
|
final Set<String> _handledProfileLinks = {};
|
||||||
String? scannedUrl;
|
String? scannedUrl;
|
||||||
GlobalKey zoomButtonKey = GlobalKey();
|
GlobalKey zoomButtonKey = GlobalKey();
|
||||||
GlobalKey cameraPreviewKey = GlobalKey();
|
GlobalKey cameraPreviewKey = GlobalKey();
|
||||||
|
|
@ -381,6 +381,7 @@ class MainCameraController {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (link.startsWith('http://') || link.startsWith('https://')) {
|
if (link.startsWith('http://') || link.startsWith('https://')) {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
import 'package:twonly/locator.dart';
|
import 'package:twonly/locator.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/contacts.dao.dart';
|
||||||
|
|
@ -35,23 +35,21 @@ class _ContactViewState extends State<ContactView> {
|
||||||
List<GroupMember> _memberOfGroups = [];
|
List<GroupMember> _memberOfGroups = [];
|
||||||
List<KeyVerification> _keyVerifications = [];
|
List<KeyVerification> _keyVerifications = [];
|
||||||
|
|
||||||
late StreamSubscription<(Contact, bool)?> _contactSub;
|
late StreamSubscription<Contact?> _contactSub;
|
||||||
late StreamSubscription<List<GroupMember>> _groupMemberSub;
|
late StreamSubscription<List<GroupMember>> _groupMemberSub;
|
||||||
late StreamSubscription<List<KeyVerification>> _streamKeyVerifications;
|
late StreamSubscription<List<KeyVerification>> _streamKeyVerifications;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
_contactSub = twonlyDB.contactsDao
|
_contactSub = twonlyDB.contactsDao.watchContact(widget.userId).listen((
|
||||||
.watchContactAndVerificationState(widget.userId)
|
update,
|
||||||
.listen((
|
) {
|
||||||
update,
|
if (update != null) {
|
||||||
) {
|
setState(() {
|
||||||
if (update != null) {
|
_contact = update;
|
||||||
setState(() {
|
|
||||||
_contact = update.$1;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
_groupMemberSub = twonlyDB.groupsDao
|
_groupMemberSub = twonlyDB.groupsDao
|
||||||
.watchContactGroupMember(widget.userId)
|
.watchContactGroupMember(widget.userId)
|
||||||
.listen((groups) async {
|
.listen((groups) async {
|
||||||
|
|
@ -250,7 +248,7 @@ class _ContactViewState extends State<ContactView> {
|
||||||
backgroundColor: context.color.surfaceContainer,
|
backgroundColor: context.color.surfaceContainer,
|
||||||
collapsedShape: const RoundedRectangleBorder(),
|
collapsedShape: const RoundedRectangleBorder(),
|
||||||
leading: Padding(
|
leading: Padding(
|
||||||
padding: EdgeInsetsGeometry.only(left: 12, right: 12),
|
padding: const EdgeInsetsGeometry.only(left: 12, right: 12),
|
||||||
child: VerificationBadgeComp(
|
child: VerificationBadgeComp(
|
||||||
contact: contact,
|
contact: contact,
|
||||||
size: 20,
|
size: 20,
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,6 @@ import 'package:twonly/src/visual/views/camera/share_image_editor.view.dart';
|
||||||
import 'package:twonly/src/visual/views/chats/chat_list.view.dart';
|
import 'package:twonly/src/visual/views/chats/chat_list.view.dart';
|
||||||
import 'package:twonly/src/visual/views/memories/memories.view.dart';
|
import 'package:twonly/src/visual/views/memories/memories.view.dart';
|
||||||
|
|
||||||
void Function(int) globalUpdateOfHomeViewPageIndex = (a) {};
|
|
||||||
|
|
||||||
class HomeView extends StatefulWidget {
|
class HomeView extends StatefulWidget {
|
||||||
const HomeView({
|
const HomeView({
|
||||||
required this.initialPage,
|
required this.initialPage,
|
||||||
|
|
@ -32,68 +30,19 @@ class HomeView extends StatefulWidget {
|
||||||
State<HomeView> createState() => HomeViewState();
|
State<HomeView> createState() => HomeViewState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class Shade extends StatelessWidget {
|
|
||||||
const Shade({required this.opacity, super.key});
|
|
||||||
final double opacity;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Positioned.fill(
|
|
||||||
child: Opacity(
|
|
||||||
opacity: opacity,
|
|
||||||
child: Container(
|
|
||||||
color: context.color.surface,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class HomeViewState extends State<HomeView> {
|
class HomeViewState extends State<HomeView> {
|
||||||
int _activePageIdx = 1;
|
int _activePageIdx = 1;
|
||||||
|
double _offsetRatio = 0;
|
||||||
|
double _offsetFromOne = 0;
|
||||||
|
Timer? _disableCameraTimer;
|
||||||
|
|
||||||
final MainCameraController _mainCameraController = MainCameraController();
|
final MainCameraController _mainCameraController = MainCameraController();
|
||||||
|
|
||||||
final PageController _homeViewPageController = PageController(initialPage: 1);
|
final PageController _homeViewPageController = PageController(initialPage: 1);
|
||||||
|
|
||||||
late StreamSubscription<List<SharedFile>> _intentStreamSub;
|
late StreamSubscription<List<SharedFile>> _intentStreamSub;
|
||||||
late StreamSubscription<Uri> _deepLinkSub;
|
late StreamSubscription<Uri> _deepLinkSub;
|
||||||
|
|
||||||
double buttonDiameter = 100;
|
static final streamHomeViewPageIndex = StreamController<int>.broadcast();
|
||||||
double offsetRatio = 0;
|
|
||||||
double offsetFromOne = 0;
|
|
||||||
double lastChange = 0;
|
|
||||||
|
|
||||||
Timer? disableCameraTimer;
|
|
||||||
|
|
||||||
bool onPageView(ScrollNotification notification) {
|
|
||||||
disableCameraTimer?.cancel();
|
|
||||||
if (notification.depth == 0 && notification is ScrollUpdateNotification) {
|
|
||||||
final page = _homeViewPageController.page ?? 0;
|
|
||||||
lastChange = page;
|
|
||||||
setState(() {
|
|
||||||
offsetFromOne = 1.0 - (_homeViewPageController.page ?? 0);
|
|
||||||
offsetRatio = offsetFromOne.abs();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (_mainCameraController.cameraController == null &&
|
|
||||||
!_mainCameraController.initCameraStarted &&
|
|
||||||
offsetRatio < 1) {
|
|
||||||
unawaited(
|
|
||||||
_mainCameraController.selectCamera(
|
|
||||||
_mainCameraController.selectedCameraDetails.cameraId,
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (offsetRatio == 1) {
|
|
||||||
disableCameraTimer = Timer(const Duration(milliseconds: 500), () async {
|
|
||||||
await _mainCameraController.closeCamera();
|
|
||||||
_mainCameraController.sharedLinkForPreview = null;
|
|
||||||
disableCameraTimer = null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
|
@ -102,31 +51,32 @@ class HomeViewState extends State<HomeView> {
|
||||||
if (mounted) setState(() {});
|
if (mounted) setState(() {});
|
||||||
};
|
};
|
||||||
|
|
||||||
globalUpdateOfHomeViewPageIndex = (index) {
|
streamHomeViewPageIndex.stream.listen((index) {
|
||||||
_homeViewPageController.jumpToPage(index);
|
_homeViewPageController.jumpToPage(index);
|
||||||
setState(() {
|
setState(() {
|
||||||
_activePageIdx = index;
|
_activePageIdx = index;
|
||||||
});
|
});
|
||||||
};
|
});
|
||||||
|
|
||||||
selectNotificationStream.stream.listen((response) async {
|
selectNotificationStream.stream.listen((response) async {
|
||||||
if (response.payload != null &&
|
if (response.payload != null &&
|
||||||
response.payload!.startsWith(Routes.chats) &&
|
response.payload!.startsWith(Routes.chats) &&
|
||||||
response.payload! != Routes.chats) {
|
response.payload! != Routes.chats) {
|
||||||
await routerProvider.push(response.payload!);
|
await routerProvider.push(response.payload!);
|
||||||
}
|
}
|
||||||
globalUpdateOfHomeViewPageIndex(0);
|
streamHomeViewPageIndex.add(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
unawaited(_mainCameraController.selectCamera(0, true));
|
unawaited(_mainCameraController.selectCamera(0, true));
|
||||||
unawaited(initAsync());
|
unawaited(_initAsync());
|
||||||
|
|
||||||
// Subscribe to all events (initial link and further)
|
// Subscribe to all events (initial link and further)
|
||||||
_deepLinkSub = AppLinks().uriLinkStream.listen((uri) async {
|
_deepLinkSub = AppLinks().uriLinkStream.listen((uri) async {
|
||||||
if (mounted) {
|
if (!mounted) return;
|
||||||
Log.info('Got link via app links: ${uri.scheme}');
|
Log.info('Got link via app links: ${uri.scheme}');
|
||||||
if (!await handleIntentUrl(context, uri)) {
|
if (!await handleIntentUrl(context, uri)) {
|
||||||
if (uri.scheme.startsWith('http')) {
|
if (uri.scheme.startsWith('http')) {
|
||||||
_mainCameraController.setSharedLinkForPreview(uri);
|
_mainCameraController.setSharedLinkForPreview(uri);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -135,26 +85,17 @@ class HomeViewState extends State<HomeView> {
|
||||||
context,
|
context,
|
||||||
_mainCameraController.setSharedLinkForPreview,
|
_mainCameraController.setSharedLinkForPreview,
|
||||||
);
|
);
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
if (widget.initialPage == 1 &&
|
if (widget.initialPage == 1 &&
|
||||||
!userService.currentUser.startWithCameraOpen ||
|
!userService.currentUser.startWithCameraOpen ||
|
||||||
widget.initialPage == 0) {
|
widget.initialPage == 0) {
|
||||||
globalUpdateOfHomeViewPageIndex(0);
|
streamHomeViewPageIndex.add(0);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
Future<void> _initAsync() async {
|
||||||
void dispose() {
|
|
||||||
unawaited(selectNotificationStream.close());
|
|
||||||
disableCameraTimer?.cancel();
|
|
||||||
_mainCameraController.closeCamera();
|
|
||||||
_intentStreamSub.cancel();
|
|
||||||
_deepLinkSub.cancel();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> initAsync() async {
|
|
||||||
final notificationAppLaunchDetails = await flutterLocalNotificationsPlugin
|
final notificationAppLaunchDetails = await flutterLocalNotificationsPlugin
|
||||||
.getNotificationAppLaunchDetails();
|
.getNotificationAppLaunchDetails();
|
||||||
|
|
||||||
|
|
@ -168,10 +109,10 @@ class HomeViewState extends State<HomeView> {
|
||||||
payload.startsWith(Routes.chats) &&
|
payload.startsWith(Routes.chats) &&
|
||||||
payload != Routes.chats) {
|
payload != Routes.chats) {
|
||||||
await routerProvider.push(payload);
|
await routerProvider.push(payload);
|
||||||
globalUpdateOfHomeViewPageIndex(0);
|
streamHomeViewPageIndex.add(0);
|
||||||
}
|
}
|
||||||
if (payload == Routes.chats) {
|
if (payload == Routes.chats) {
|
||||||
globalUpdateOfHomeViewPageIndex(0);
|
streamHomeViewPageIndex.add(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -191,22 +132,69 @@ class HomeViewState extends State<HomeView> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
selectNotificationStream.close();
|
||||||
|
streamHomeViewPageIndex.close();
|
||||||
|
_disableCameraTimer?.cancel();
|
||||||
|
_mainCameraController.closeCamera();
|
||||||
|
_intentStreamSub.cancel();
|
||||||
|
_deepLinkSub.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _onPageView(ScrollNotification notification) {
|
||||||
|
_disableCameraTimer?.cancel();
|
||||||
|
|
||||||
|
if (notification.depth == 0 && notification is ScrollUpdateNotification) {
|
||||||
|
setState(() {
|
||||||
|
_offsetFromOne = 1.0 - (_homeViewPageController.page ?? 0);
|
||||||
|
_offsetRatio = _offsetFromOne.abs();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_mainCameraController.cameraController == null &&
|
||||||
|
!_mainCameraController.initCameraStarted &&
|
||||||
|
_offsetRatio < 1) {
|
||||||
|
unawaited(
|
||||||
|
_mainCameraController.selectCamera(
|
||||||
|
_mainCameraController.selectedCameraDetails.cameraId,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_offsetRatio == 1) {
|
||||||
|
_disableCameraTimer = Timer(const Duration(milliseconds: 500), () async {
|
||||||
|
await _mainCameraController.closeCamera();
|
||||||
|
_mainCameraController.sharedLinkForPreview = null;
|
||||||
|
_disableCameraTimer = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: GestureDetector(
|
body: GestureDetector(
|
||||||
onDoubleTap: offsetRatio == 0
|
onDoubleTap: _offsetRatio == 0
|
||||||
? _mainCameraController.onDoubleTap
|
? _mainCameraController.onDoubleTap
|
||||||
: null,
|
: null,
|
||||||
onTapDown: offsetRatio == 0 ? _mainCameraController.onTapDown : null,
|
onTapDown: _offsetRatio == 0 ? _mainCameraController.onTapDown : null,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
MainCameraPreview(mainCameraController: _mainCameraController),
|
MainCameraPreview(mainCameraController: _mainCameraController),
|
||||||
Shade(
|
Positioned.fill(
|
||||||
opacity: offsetRatio,
|
child: Opacity(
|
||||||
|
opacity: _offsetRatio,
|
||||||
|
child: Container(
|
||||||
|
color: context.color.surface,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
NotificationListener<ScrollNotification>(
|
NotificationListener<ScrollNotification>(
|
||||||
onNotification: onPageView,
|
onNotification: _onPageView,
|
||||||
child: Positioned.fill(
|
child: Positioned.fill(
|
||||||
child: PageView(
|
child: PageView(
|
||||||
controller: _homeViewPageController,
|
controller: _homeViewPageController,
|
||||||
|
|
@ -227,15 +215,16 @@ class HomeViewState extends State<HomeView> {
|
||||||
left: 0,
|
left: 0,
|
||||||
top: 0,
|
top: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
bottom: (offsetRatio > 0.25)
|
bottom: (_offsetRatio > 0.25)
|
||||||
? MediaQuery.sizeOf(context).height * 2
|
? MediaQuery.sizeOf(context).height * 2
|
||||||
: 0,
|
: 0,
|
||||||
child: Opacity(
|
child: Opacity(
|
||||||
opacity: 1 - (offsetRatio * 4) % 1,
|
opacity: 1 - (_offsetRatio * 4) % 1,
|
||||||
child: CameraPreviewControllerView(
|
child: CameraPreviewControllerView(
|
||||||
mainController: _mainCameraController,
|
mainController: _mainCameraController,
|
||||||
isVisible:
|
isVisible:
|
||||||
((1 - (offsetRatio * 4) % 1) == 1) && _activePageIdx == 1,
|
((1 - (_offsetRatio * 4) % 1) == 1) &&
|
||||||
|
_activePageIdx == 1,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/services/signal/identity.signal.dart';
|
import 'package:twonly/src/services/signal/identity.signal.dart';
|
||||||
import 'package:twonly/src/utils/avatars.dart';
|
import 'package:twonly/src/utils/avatars.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/utils/qr.dart';
|
import 'package:twonly/src/utils/qr.utils.dart';
|
||||||
import 'package:twonly/src/visual/elements/better_list_title.element.dart';
|
import 'package:twonly/src/visual/elements/better_list_title.element.dart';
|
||||||
|
|
||||||
class PublicProfileView extends StatefulWidget {
|
class PublicProfileView extends StatefulWidget {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue