diff --git a/lib/app.dart b/lib/app.dart index ba3a4a3c..c65fa7c2 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -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/settings.provider.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/log.dart'; import 'package:twonly/src/utils/pow.dart'; @@ -182,11 +181,6 @@ class _AppMainWidgetState extends State { context, handleShareLink, ); - - _emailTokenSub = PasswordlessRecoveryService.onEmailTokenReceived.stream - .listen((token) { - routerProvider.go(Routes.recoverPasswordless, extra: token); - }); } Future initAsync() async { diff --git a/lib/src/model/json/userdata.model.dart b/lib/src/model/json/userdata.model.dart index e16ca159..cf245035 100644 --- a/lib/src/model/json/userdata.model.dart +++ b/lib/src/model/json/userdata.model.dart @@ -195,8 +195,12 @@ class TwonlySafeBackup { factory TwonlySafeBackup.fromJson(Map json) => _$TwonlySafeBackupFromJson(json); + @JsonKey(defaultValue: 0) int lastBackupSize = 0; + + @JsonKey(defaultValue: LastBackupUploadState.none) LastBackupUploadState backupUploadState = LastBackupUploadState.none; + DateTime? lastBackupDone; List backupId; List encryptionKey; @@ -216,6 +220,7 @@ class PasswordLessRecovery { // <-- // Data shared with trusted friends + @JsonKey(defaultValue: 2) int threshold; // Trusted friends are able to brute-force the pin -> Server delets after X tries List? pinSeed; diff --git a/lib/src/model/json/userdata.model.g.dart b/lib/src/model/json/userdata.model.g.dart index e92748ef..13e3565a 100644 --- a/lib/src/model/json/userdata.model.g.dart +++ b/lib/src/model/json/userdata.model.g.dart @@ -202,11 +202,13 @@ TwonlySafeBackup _$TwonlySafeBackupFromJson(Map json) => .map((e) => (e as num).toInt()) .toList(), ) - ..lastBackupSize = (json['lastBackupSize'] as num).toInt() - ..backupUploadState = $enumDecode( - _$LastBackupUploadStateEnumMap, - json['backupUploadState'], - ) + ..lastBackupSize = (json['lastBackupSize'] as num?)?.toInt() ?? 0 + ..backupUploadState = + $enumDecodeNullable( + _$LastBackupUploadStateEnumMap, + json['backupUploadState'], + ) ?? + LastBackupUploadState.none ..lastBackupDone = json['lastBackupDone'] == null ? null : DateTime.parse(json['lastBackupDone'] as String); @@ -230,7 +232,7 @@ const _$LastBackupUploadStateEnumMap = { PasswordLessRecovery _$PasswordLessRecoveryFromJson( Map json, -) => PasswordLessRecovery((json['threshold'] as num).toInt()) +) => PasswordLessRecovery((json['threshold'] as num?)?.toInt() ?? 2) ..email = json['email'] as String? ..pinSeed = (json['pinSeed'] as List?) ?.map((e) => (e as num).toInt()) diff --git a/lib/src/services/passwordless_recovery.service.dart b/lib/src/services/passwordless_recovery.service.dart index 7770d119..ced9abd4 100644 --- a/lib/src/services/passwordless_recovery.service.dart +++ b/lib/src/services/passwordless_recovery.service.dart @@ -36,6 +36,8 @@ class PasswordlessRecoveryService { static final StreamController onEmailTokenReceived = StreamController.broadcast(); + static String? lastEmailToken; + static String linkPrefix = 'https://me.twonly.eu/r/#'; static final Set _handledNotificationIds = {}; @@ -48,6 +50,7 @@ class PasswordlessRecoveryService { final parts = fragment.split('/'); if (parts.length < 2) { if (fragment.isNotEmpty) { + lastEmailToken = fragment; onEmailTokenReceived.add(fragment); } return; diff --git a/lib/src/visual/components/animate_icon.comp.dart b/lib/src/visual/components/animate_icon.comp.dart index 5f721baf..afe42cb6 100644 --- a/lib/src/visual/components/animate_icon.comp.dart +++ b/lib/src/visual/components/animate_icon.comp.dart @@ -245,7 +245,7 @@ class EmojiAnimationComp extends StatelessWidget { return Lottie.asset( 'assets/animated_icons/${animatedIcons[emoji]}', repeat: repeat, - renderCache: RenderCache.raster, + renderCache: RenderCache.drawingCommands, ); } else if (isOneEmoji(emoji)) { return Text( diff --git a/lib/src/visual/views/onboarding/recover_passwordless.view.dart b/lib/src/visual/views/onboarding/recover_passwordless.view.dart index f8b88a1b..d1bb2d6c 100644 --- a/lib/src/visual/views/onboarding/recover_passwordless.view.dart +++ b/lib/src/visual/views/onboarding/recover_passwordless.view.dart @@ -60,26 +60,21 @@ class _RecoverPasswordlessState extends State { @override void initState() { super.initState(); - _secondFactorController.text = widget.initialEmailToken ?? ''; + final token = + widget.initialEmailToken ?? PasswordlessRecoveryService.lastEmailToken; + _secondFactorController.text = token ?? ''; + _emailTokenSubscription = PasswordlessRecoveryService .onEmailTokenReceived .stream .listen((token) async { if (mounted) { - final state = _onboardingState; - if (state != null && !state.emailRecoveryRequested) { - state.emailRecoveryRequested = true; - await KeyValueStore.update( - key: KeyValueKeys.onboardingState, - update: (s) => s.emailRecoveryRequested = true, - ); - } setState(() { _secondFactorController.text = token; }); } }); - _initAsync(); + _initAsync(token); } @override @@ -90,14 +85,14 @@ class _RecoverPasswordlessState extends State { super.dispose(); } - Future _initAsync() async { + Future _initAsync(String? initialToken) async { try { // 1. Load OnboardingState final state = await KeyValueStore.getModel( KeyValueKeys.onboardingState, ); - if (widget.initialEmailToken != null && !state.emailRecoveryRequested) { + if (initialToken != null && !state.emailRecoveryRequested) { state.emailRecoveryRequested = true; await KeyValueStore.update( key: KeyValueKeys.onboardingState, diff --git a/lib/src/visual/views/settings/chat/chat_reactions.view.dart b/lib/src/visual/views/settings/chat/chat_reactions.view.dart index d050baa4..c6dde23e 100644 --- a/lib/src/visual/views/settings/chat/chat_reactions.view.dart +++ b/lib/src/visual/views/settings/chat/chat_reactions.view.dart @@ -75,10 +75,16 @@ class _ChatReactionSelectionView extends State { ? context.color.primary.withAlpha(150) : context.color.surface, child: Center( - child: Text( - emoji, - style: const TextStyle(fontSize: 32), - ), + child: isSelected + ? SizedBox( + width: 40, + height: 40, + child: EmojiAnimationComp(emoji: emoji), + ) + : Text( + emoji, + style: const TextStyle(fontSize: 32), + ), ), ), ); diff --git a/pubspec.lock b/pubspec.lock index e506b9cd..7b57597c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1266,7 +1266,7 @@ packages: path: "dependencies/lottie" relative: true source: path - version: "3.3.2" + version: "3.5.1" markdown: dependency: transitive description: