From af154d82de777f7ca5ddbd9ee4ab8eede8db8769 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 28 Aug 2025 18:17:11 +0530 Subject: [PATCH] ducky analyzing riv animation --- .../photos/assets/ducky_analyze_files.riv | Bin 0 -> 14301 bytes .../lib/ui/tools/similar_images_page.dart | 266 ++---------------- mobile/apps/photos/pubspec.lock | 16 ++ mobile/apps/photos/pubspec.yaml | 1 + 4 files changed, 40 insertions(+), 243 deletions(-) create mode 100644 mobile/apps/photos/assets/ducky_analyze_files.riv diff --git a/mobile/apps/photos/assets/ducky_analyze_files.riv b/mobile/apps/photos/assets/ducky_analyze_files.riv new file mode 100644 index 0000000000000000000000000000000000000000..af3501deefcd2db280f0a5e3570c411fa2e1d15e GIT binary patch literal 14301 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�Cpq!`&jvUod?GB7rXeoE!j`}%|7r79LUBI-hXWEm5tiJz<+zosjQ7y+ z`A%%?cbe_t|H#0TK2XXDfbqljUjDKgbU@g0GR7qiGsZE}mxGdY7Hu9mE!uiv&NDTu z#KO~gV^qtQEh#D|*hF15!v8}uTsInS7!5a#hFeBMiP2DMG~6>9?i&r1)kMKx2>jK8 zzXtH;BPeieFcAWkU0~Zwb*RIWr}c7MrWs+`%S22$~FRGO@|R zCKsFC*qp-V5;ixnxra?gFf_}sS&Pk9Z1S-=h|O_qE@E>Hn@8BZz-EpSnoMk#VY3;V zUDzDNrU080Mye*g$ZMn7w})+aa&X;DQ#Dy;no{;muiD#|W~-$&_x&*IuUMMh*D=TU zL$kM|X!f0xvwS~{JPtpkgd8aQfj#}j9^doxEPG { } Widget _getLoadingView() { - return const SimilarImagesLoadingWidget(); + final textTheme = getEnteTextTheme(context); + + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox( + height: 160, + child: RiveAnimation.asset( + 'assets/ducky_analyze_files.riv', + fit: BoxFit.contain, + ), + ), + const SizedBox(height: 16), + Text( + AppLocalizations.of(context).analyzingPhotosLocally, + style: textTheme.bodyMuted, + textAlign: TextAlign.center, + ), + ], + ), + ); } Widget _getResultsView() { @@ -1161,245 +1183,3 @@ class _SimilarImagesPageState extends State { ); } } - -class SimilarImagesLoadingWidget extends StatefulWidget { - const SimilarImagesLoadingWidget({super.key}); - - @override - State createState() => - _SimilarImagesLoadingWidgetState(); -} - -class _SimilarImagesLoadingWidgetState extends State - with TickerProviderStateMixin { - late AnimationController _loadingAnimationController; - late AnimationController _pulseAnimationController; - late Animation _scaleAnimation; - late Animation _pulseAnimation; - int _loadingMessageIndex = 0; - - List get _loadingMessages => [ - AppLocalizations.of(context).analyzingPhotosLocally, - AppLocalizations.of(context).lookingForVisualSimilarities, - AppLocalizations.of(context).comparingImageDetails, - AppLocalizations.of(context).findingSimilarImages, - AppLocalizations.of(context).almostDone, - ]; - - @override - void initState() { - super.initState(); - - // Initialize loading animations - _loadingAnimationController = AnimationController( - duration: const Duration(seconds: 2), - vsync: this, - )..repeat(); - - _pulseAnimationController = AnimationController( - duration: const Duration(milliseconds: 1500), - vsync: this, - )..repeat(reverse: true); - - _scaleAnimation = Tween( - begin: 0.8, - end: 1.0, - ).animate( - CurvedAnimation( - parent: _pulseAnimationController, - curve: Curves.easeInOut, - ), - ); - - _pulseAnimation = Tween( - begin: 0.4, - end: 1.0, - ).animate( - CurvedAnimation( - parent: _pulseAnimationController, - curve: Curves.easeInOut, - ), - ); - - _startMessageCycling(); - } - - void _startMessageCycling() { - Future.doWhile(() async { - if (!mounted) return false; - await Future.delayed(const Duration(seconds: 7)); - if (mounted) { - setState(() { - _loadingMessageIndex++; - }); - // Stop cycling after reaching the last message - return _loadingMessageIndex < _loadingMessages.length - 1; - } - return false; - }); - } - - @override - void dispose() { - _loadingAnimationController.dispose(); - _pulseAnimationController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final textTheme = getEnteTextTheme(context); - final colorScheme = getEnteColorScheme(context); - - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - // Animated scanning effect - Stack( - alignment: Alignment.center, - children: [ - // Pulsing background circle - AnimatedBuilder( - animation: _pulseAnimation, - builder: (context, child) { - return Container( - width: 160, - height: 160, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: colorScheme.primary500.withValues( - alpha: _pulseAnimation.value * 0.1, - ), - ), - ); - }, - ), - // Rotating scanner ring - AnimatedBuilder( - animation: _loadingAnimationController, - builder: (context, child) { - return Transform.rotate( - angle: _loadingAnimationController.value * 2 * 3.14159, - child: Container( - width: 120, - height: 120, - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all( - color: colorScheme.primary500, - width: 2, - ), - gradient: SweepGradient( - colors: [ - colorScheme.primary500.withValues(alpha: 0), - colorScheme.primary500.withValues(alpha: 0.3), - colorScheme.primary500.withValues(alpha: 0.6), - colorScheme.primary500, - colorScheme.primary500.withValues(alpha: 0), - ], - stops: const [0.0, 0.25, 0.5, 0.75, 1.0], - ), - ), - ), - ); - }, - ), - // Center icon with scale animation - AnimatedBuilder( - animation: _scaleAnimation, - builder: (context, child) { - return Transform.scale( - scale: _scaleAnimation.value, - child: Container( - width: 80, - height: 80, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: colorScheme.backgroundElevated, - boxShadow: [ - BoxShadow( - color: colorScheme.strokeFaint, - blurRadius: 8, - offset: const Offset(0, 2), - ), - ], - ), - child: Icon( - Icons.photo_library_outlined, - size: 40, - color: colorScheme.primary500, - ), - ), - ); - }, - ), - ], - ), - const SizedBox(height: 48), - // Privacy badge - Container( - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), - decoration: BoxDecoration( - color: colorScheme.fillFaint, - borderRadius: BorderRadius.circular(20), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - Icons.lock_outline, - size: 14, - color: colorScheme.textMuted, - ), - const SizedBox(width: 6), - Text( - AppLocalizations.of(context).processingLocally, - style: textTheme.miniFaint, - ), - ], - ), - ), - const SizedBox(height: 16), - // Animated loading message - AnimatedSwitcher( - duration: const Duration(milliseconds: 500), - child: Text( - _loadingMessages[_loadingMessageIndex], - key: ValueKey(_loadingMessageIndex), - style: textTheme.body, - textAlign: TextAlign.center, - ), - ), - const SizedBox(height: 8), - // Progress dots - Row( - mainAxisSize: MainAxisSize.min, - children: List.generate( - 3, - (index) => AnimatedBuilder( - animation: _loadingAnimationController, - builder: (context, child) { - final delay = index * 0.2; - final value = - (_loadingAnimationController.value + delay) % 1.0; - return Container( - margin: const EdgeInsets.symmetric(horizontal: 4), - width: 8, - height: 8, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: colorScheme.primary500.withValues( - alpha: value < 0.5 ? value * 2 : 2 - value * 2, - ), - ), - ); - }, - ), - ), - ), - ], - ), - ); - } -} diff --git a/mobile/apps/photos/pubspec.lock b/mobile/apps/photos/pubspec.lock index ce3162ad2b..0c11e8576a 100644 --- a/mobile/apps/photos/pubspec.lock +++ b/mobile/apps/photos/pubspec.lock @@ -2165,6 +2165,22 @@ packages: url: "https://github.com/KasemJaffer/receive_sharing_intent.git" source: git version: "1.8.1" + rive: + dependency: "direct main" + description: + name: rive + sha256: "2551a44fa766a7ed3f52aa2b94feda6d18d00edc25dee5f66e72e9b365bb6d6c" + url: "https://pub.dev" + source: hosted + version: "0.13.20" + rive_common: + dependency: transitive + description: + name: rive_common + sha256: "2ba42f80d37a4efd0696fb715787c4785f8a13361e8aea9227c50f1e78cf763a" + url: "https://pub.dev" + source: hosted + version: "0.4.15" rust_lib_photos: dependency: "direct main" description: diff --git a/mobile/apps/photos/pubspec.yaml b/mobile/apps/photos/pubspec.yaml index 13fdd3a965..0d45cceb7d 100644 --- a/mobile/apps/photos/pubspec.yaml +++ b/mobile/apps/photos/pubspec.yaml @@ -179,6 +179,7 @@ dependencies: git: url: https://github.com/KasemJaffer/receive_sharing_intent.git ref: 2cea396 + rive: ^0.13.20 rust_lib_photos: path: rust_builder screenshot: ^3.0.0