fix some smaller issues

This commit is contained in:
otsmr 2026-07-15 20:20:02 +02:00
parent 2a4574ef36
commit 4588c9995d
8 changed files with 35 additions and 30 deletions

View file

@ -15,7 +15,6 @@ import 'package:twonly/src/model/json/onboarding_state.model.dart';
import 'package:twonly/src/providers/routing.provider.dart'; import 'package:twonly/src/providers/routing.provider.dart';
import 'package:twonly/src/providers/settings.provider.dart'; import 'package:twonly/src/providers/settings.provider.dart';
import 'package:twonly/src/services/intent/links.intent.dart'; import 'package:twonly/src/services/intent/links.intent.dart';
import 'package:twonly/src/services/passwordless_recovery.service.dart';
import 'package:twonly/src/utils/keyvalue.dart'; import 'package:twonly/src/utils/keyvalue.dart';
import 'package:twonly/src/utils/log.dart'; import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/pow.dart'; import 'package:twonly/src/utils/pow.dart';
@ -182,11 +181,6 @@ class _AppMainWidgetState extends State<AppMainWidget> {
context, context,
handleShareLink, handleShareLink,
); );
_emailTokenSub = PasswordlessRecoveryService.onEmailTokenReceived.stream
.listen((token) {
routerProvider.go(Routes.recoverPasswordless, extra: token);
});
} }
Future<void> initAsync() async { Future<void> initAsync() async {

View file

@ -195,8 +195,12 @@ class TwonlySafeBackup {
factory TwonlySafeBackup.fromJson(Map<String, dynamic> json) => factory TwonlySafeBackup.fromJson(Map<String, dynamic> json) =>
_$TwonlySafeBackupFromJson(json); _$TwonlySafeBackupFromJson(json);
@JsonKey(defaultValue: 0)
int lastBackupSize = 0; int lastBackupSize = 0;
@JsonKey(defaultValue: LastBackupUploadState.none)
LastBackupUploadState backupUploadState = LastBackupUploadState.none; LastBackupUploadState backupUploadState = LastBackupUploadState.none;
DateTime? lastBackupDone; DateTime? lastBackupDone;
List<int> backupId; List<int> backupId;
List<int> encryptionKey; List<int> encryptionKey;
@ -216,6 +220,7 @@ class PasswordLessRecovery {
// <-- // <--
// Data shared with trusted friends // Data shared with trusted friends
@JsonKey(defaultValue: 2)
int threshold; int threshold;
// Trusted friends are able to brute-force the pin -> Server delets after X tries // Trusted friends are able to brute-force the pin -> Server delets after X tries
List<int>? pinSeed; List<int>? pinSeed;

View file

@ -202,11 +202,13 @@ TwonlySafeBackup _$TwonlySafeBackupFromJson(Map<String, dynamic> json) =>
.map((e) => (e as num).toInt()) .map((e) => (e as num).toInt())
.toList(), .toList(),
) )
..lastBackupSize = (json['lastBackupSize'] as num).toInt() ..lastBackupSize = (json['lastBackupSize'] as num?)?.toInt() ?? 0
..backupUploadState = $enumDecode( ..backupUploadState =
_$LastBackupUploadStateEnumMap, $enumDecodeNullable(
json['backupUploadState'], _$LastBackupUploadStateEnumMap,
) json['backupUploadState'],
) ??
LastBackupUploadState.none
..lastBackupDone = json['lastBackupDone'] == null ..lastBackupDone = json['lastBackupDone'] == null
? null ? null
: DateTime.parse(json['lastBackupDone'] as String); : DateTime.parse(json['lastBackupDone'] as String);
@ -230,7 +232,7 @@ const _$LastBackupUploadStateEnumMap = {
PasswordLessRecovery _$PasswordLessRecoveryFromJson( PasswordLessRecovery _$PasswordLessRecoveryFromJson(
Map<String, dynamic> json, Map<String, dynamic> json,
) => PasswordLessRecovery((json['threshold'] as num).toInt()) ) => PasswordLessRecovery((json['threshold'] as num?)?.toInt() ?? 2)
..email = json['email'] as String? ..email = json['email'] as String?
..pinSeed = (json['pinSeed'] as List<dynamic>?) ..pinSeed = (json['pinSeed'] as List<dynamic>?)
?.map((e) => (e as num).toInt()) ?.map((e) => (e as num).toInt())

View file

@ -36,6 +36,8 @@ class PasswordlessRecoveryService {
static final StreamController<String> onEmailTokenReceived = static final StreamController<String> onEmailTokenReceived =
StreamController<String>.broadcast(); StreamController<String>.broadcast();
static String? lastEmailToken;
static String linkPrefix = 'https://me.twonly.eu/r/#'; static String linkPrefix = 'https://me.twonly.eu/r/#';
static final Set<String> _handledNotificationIds = {}; static final Set<String> _handledNotificationIds = {};
@ -48,6 +50,7 @@ class PasswordlessRecoveryService {
final parts = fragment.split('/'); final parts = fragment.split('/');
if (parts.length < 2) { if (parts.length < 2) {
if (fragment.isNotEmpty) { if (fragment.isNotEmpty) {
lastEmailToken = fragment;
onEmailTokenReceived.add(fragment); onEmailTokenReceived.add(fragment);
} }
return; return;

View file

@ -245,7 +245,7 @@ class EmojiAnimationComp extends StatelessWidget {
return Lottie.asset( return Lottie.asset(
'assets/animated_icons/${animatedIcons[emoji]}', 'assets/animated_icons/${animatedIcons[emoji]}',
repeat: repeat, repeat: repeat,
renderCache: RenderCache.raster, renderCache: RenderCache.drawingCommands,
); );
} else if (isOneEmoji(emoji)) { } else if (isOneEmoji(emoji)) {
return Text( return Text(

View file

@ -60,26 +60,21 @@ class _RecoverPasswordlessState extends State<RecoverPasswordless> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_secondFactorController.text = widget.initialEmailToken ?? ''; final token =
widget.initialEmailToken ?? PasswordlessRecoveryService.lastEmailToken;
_secondFactorController.text = token ?? '';
_emailTokenSubscription = PasswordlessRecoveryService _emailTokenSubscription = PasswordlessRecoveryService
.onEmailTokenReceived .onEmailTokenReceived
.stream .stream
.listen((token) async { .listen((token) async {
if (mounted) { if (mounted) {
final state = _onboardingState;
if (state != null && !state.emailRecoveryRequested) {
state.emailRecoveryRequested = true;
await KeyValueStore.update<OnboardingState>(
key: KeyValueKeys.onboardingState,
update: (s) => s.emailRecoveryRequested = true,
);
}
setState(() { setState(() {
_secondFactorController.text = token; _secondFactorController.text = token;
}); });
} }
}); });
_initAsync(); _initAsync(token);
} }
@override @override
@ -90,14 +85,14 @@ class _RecoverPasswordlessState extends State<RecoverPasswordless> {
super.dispose(); super.dispose();
} }
Future<void> _initAsync() async { Future<void> _initAsync(String? initialToken) async {
try { try {
// 1. Load OnboardingState // 1. Load OnboardingState
final state = await KeyValueStore.getModel<OnboardingState>( final state = await KeyValueStore.getModel<OnboardingState>(
KeyValueKeys.onboardingState, KeyValueKeys.onboardingState,
); );
if (widget.initialEmailToken != null && !state.emailRecoveryRequested) { if (initialToken != null && !state.emailRecoveryRequested) {
state.emailRecoveryRequested = true; state.emailRecoveryRequested = true;
await KeyValueStore.update<OnboardingState>( await KeyValueStore.update<OnboardingState>(
key: KeyValueKeys.onboardingState, key: KeyValueKeys.onboardingState,

View file

@ -75,10 +75,16 @@ class _ChatReactionSelectionView extends State<ChatReactionSelectionView> {
? context.color.primary.withAlpha(150) ? context.color.primary.withAlpha(150)
: context.color.surface, : context.color.surface,
child: Center( child: Center(
child: Text( child: isSelected
emoji, ? SizedBox(
style: const TextStyle(fontSize: 32), width: 40,
), height: 40,
child: EmojiAnimationComp(emoji: emoji),
)
: Text(
emoji,
style: const TextStyle(fontSize: 32),
),
), ),
), ),
); );

View file

@ -1266,7 +1266,7 @@ packages:
path: "dependencies/lottie" path: "dependencies/lottie"
relative: true relative: true
source: path source: path
version: "3.3.2" version: "3.5.1"
markdown: markdown:
dependency: transitive dependency: transitive
description: description: