chore(mobile): small visual fix and update (#17547)

* chore(mobile): small visual fix and update

* update

* update

* remove design placeholder
This commit is contained in:
Alex 2025-04-13 08:01:32 -05:00 committed by GitHub
parent 1f18fe31f0
commit ab2a7006f9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 168 additions and 39 deletions

View File

@ -1,4 +1,5 @@
{ {
"open": "Open",
"action_common_back": "Back", "action_common_back": "Back",
"action_common_cancel": "Cancel", "action_common_cancel": "Cancel",
"action_common_clear": "Clear", "action_common_clear": "Clear",
@ -312,7 +313,7 @@
"home_page_delete_remote_err_local": "Local assets in delete remote selection, skipping", "home_page_delete_remote_err_local": "Local assets in delete remote selection, skipping",
"home_page_favorite_err_local": "Can not favorite local assets yet, skipping", "home_page_favorite_err_local": "Can not favorite local assets yet, skipping",
"home_page_favorite_err_partner": "Can not favorite partner assets yet, skipping", "home_page_favorite_err_partner": "Can not favorite partner assets yet, skipping",
"home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).", "home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album so that the timeline can populate photos and videos in it",
"home_page_share_err_local": "Can not share local assets via link, skipping", "home_page_share_err_local": "Can not share local assets via link, skipping",
"home_page_upload_err_limit": "Can only upload a maximum of 30 assets at a time, skipping", "home_page_upload_err_limit": "Can only upload a maximum of 30 assets at a time, skipping",
"ignore_icloud_photos": "Ignore iCloud photos", "ignore_icloud_photos": "Ignore iCloud photos",
@ -693,4 +694,4 @@
"viewer_unstack": "Un-Stack", "viewer_unstack": "Un-Stack",
"wifi_name": "WiFi Name", "wifi_name": "WiFi Name",
"your_wifi_name": "Your WiFi name" "your_wifi_name": "Your WiFi name"
} }

View File

@ -97,6 +97,25 @@ PODS:
- sqflite_darwin (0.0.4): - sqflite_darwin (0.0.4):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
- sqlite3 (3.49.1):
- sqlite3/common (= 3.49.1)
- sqlite3/common (3.49.1)
- sqlite3/dbstatvtab (3.49.1):
- sqlite3/common
- sqlite3/fts5 (3.49.1):
- sqlite3/common
- sqlite3/perf-threadsafe (3.49.1):
- sqlite3/common
- sqlite3/rtree (3.49.1):
- sqlite3/common
- sqlite3_flutter_libs (0.0.1):
- Flutter
- FlutterMacOS
- sqlite3 (~> 3.49.1)
- sqlite3/dbstatvtab
- sqlite3/fts5
- sqlite3/perf-threadsafe
- sqlite3/rtree
- SwiftyGif (5.4.5) - SwiftyGif (5.4.5)
- url_launcher_ios (0.0.1): - url_launcher_ios (0.0.1):
- Flutter - Flutter
@ -130,6 +149,7 @@ DEPENDENCIES:
- share_plus (from `.symlinks/plugins/share_plus/ios`) - share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`) - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
- sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/darwin`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`) - wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
@ -140,6 +160,7 @@ SPEC REPOS:
- MapLibre - MapLibre
- SAMKeychain - SAMKeychain
- SDWebImage - SDWebImage
- sqlite3
- SwiftyGif - SwiftyGif
EXTERNAL SOURCES: EXTERNAL SOURCES:
@ -195,6 +216,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin" :path: ".symlinks/plugins/shared_preferences_foundation/darwin"
sqflite_darwin: sqflite_darwin:
:path: ".symlinks/plugins/sqflite_darwin/darwin" :path: ".symlinks/plugins/sqflite_darwin/darwin"
sqlite3_flutter_libs:
:path: ".symlinks/plugins/sqlite3_flutter_libs/darwin"
url_launcher_ios: url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios" :path: ".symlinks/plugins/url_launcher_ios/ios"
wakelock_plus: wakelock_plus:
@ -232,6 +255,8 @@ SPEC CHECKSUMS:
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
sqlite3: fc1400008a9b3525f5914ed715a5d1af0b8f4983
sqlite3_flutter_libs: f8fc13346870e73fe35ebf6dbb997fbcd156b241
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
wakelock_plus: 04623e3f525556020ebd4034310f20fe7fda8b49 wakelock_plus: 04623e3f525556020ebd4034310f20fe7fda8b49

View File

@ -10,7 +10,6 @@ import 'package:immich_mobile/services/app_settings.service.dart';
import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart';
import 'package:immich_mobile/widgets/backup/album_info_card.dart'; import 'package:immich_mobile/widgets/backup/album_info_card.dart';
import 'package:immich_mobile/widgets/backup/album_info_list_tile.dart'; import 'package:immich_mobile/widgets/backup/album_info_list_tile.dart';
import 'package:immich_mobile/widgets/common/immich_loading_indicator.dart';
import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart';
@RoutePage() @RoutePage()
@ -37,7 +36,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
if (albums.isEmpty) { if (albums.isEmpty) {
return const SliverToBoxAdapter( return const SliverToBoxAdapter(
child: Center( child: Center(
child: ImmichLoadingIndicator(), child: CircularProgressIndicator(),
), ),
); );
} }
@ -61,7 +60,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
if (albums.isEmpty) { if (albums.isEmpty) {
return const SliverToBoxAdapter( return const SliverToBoxAdapter(
child: Center( child: Center(
child: ImmichLoadingIndicator(), child: CircularProgressIndicator(),
), ),
); );
} }

View File

@ -53,28 +53,29 @@ class PhotosPage extends HookConsumerWidget {
padding: const EdgeInsets.only(top: 16.0), padding: const EdgeInsets.only(top: 16.0),
child: Text( child: Text(
'home_page_building_timeline', 'home_page_building_timeline',
style: TextStyle( style: context.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
fontSize: 16,
color: context.primaryColor, color: context.primaryColor,
), ),
).tr(), ).tr(),
), ),
const SizedBox(height: 8),
AnimatedOpacity( AnimatedOpacity(
duration: const Duration(milliseconds: 500), duration: const Duration(milliseconds: 1000),
opacity: tipOneOpacity.value, opacity: tipOneOpacity.value,
child: SizedBox( child: Column(
width: 250, children: [
child: Padding( SizedBox(
padding: const EdgeInsets.only(top: 8.0), width: 320,
child: const Text( child: Padding(
'home_page_first_time_notice', padding: const EdgeInsets.only(top: 8.0),
textAlign: TextAlign.justify, child: Text(
style: TextStyle( 'home_page_first_time_notice',
fontSize: 12, textAlign: TextAlign.center,
style: context.textTheme.bodyMedium,
).tr(),
), ),
).tr(), ),
), ],
), ),
), ),
], ],

View File

@ -715,7 +715,7 @@ class SearchPage extends HookConsumerWidget {
), ),
if (isSearching.value) if (isSearching.value)
const Expanded( const Expanded(
child: Center(child: CircularProgressIndicator.adaptive()), child: Center(child: CircularProgressIndicator()),
) )
else else
SearchResultGrid( SearchResultGrid(

View File

@ -163,6 +163,13 @@ ThemeData getThemeData({
), ),
), ),
dialogTheme: DialogThemeData(backgroundColor: colorScheme.surfaceContainer), dialogTheme: DialogThemeData(backgroundColor: colorScheme.surfaceContainer),
progressIndicatorTheme: const ProgressIndicatorThemeData(
// ignore: deprecated_member_use
year2023: false,
// TODO: Uncommented after upgrade to version later than 3.29.2
// circularTrackColor: Colors.black12,
trackGap: 3,
),
); );
} }

View File

@ -22,7 +22,6 @@ import 'package:immich_mobile/entities/album.entity.dart';
import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/providers/asset.provider.dart'; import 'package:immich_mobile/providers/asset.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/widgets/common/immich_loading_indicator.dart';
import 'package:immich_mobile/widgets/common/immich_toast.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart';
import 'package:immich_mobile/utils/immich_loading_overlay.dart'; import 'package:immich_mobile/utils/immich_loading_overlay.dart';
import 'package:immich_mobile/utils/selection_handlers.dart'; import 'package:immich_mobile/utils/selection_handlers.dart';
@ -59,7 +58,7 @@ class MultiselectGrid extends HookConsumerWidget {
final bool editEnabled; final bool editEnabled;
final Widget? emptyIndicator; final Widget? emptyIndicator;
Widget buildDefaultLoadingIndicator() => Widget buildDefaultLoadingIndicator() =>
const Center(child: ImmichLoadingIndicator()); const Center(child: CircularProgressIndicator());
Widget buildEmptyIndicator() => Widget buildEmptyIndicator() =>
emptyIndicator ?? Center(child: const Text("no_assets_to_show").tr()); emptyIndicator ?? Center(child: const Text("no_assets_to_show").tr());

View File

@ -1,7 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:immich_mobile/widgets/common/immich_logo.dart';
class ImmichLoadingIndicator extends StatelessWidget { class ImmichLoadingIndicator extends HookWidget {
final double? borderRadius; final double? borderRadius;
const ImmichLoadingIndicator({ const ImmichLoadingIndicator({
@ -11,18 +12,109 @@ class ImmichLoadingIndicator extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final logoAnimationController = useAnimationController(
duration: const Duration(seconds: 6),
)
..reverse()
..repeat();
final borderAnimationController = useAnimationController(
duration: const Duration(seconds: 6),
)..repeat();
return Container( return Container(
height: 60, height: 80,
width: 60, width: 80,
decoration: BoxDecoration( decoration: BoxDecoration(
color: context.primaryColor.withAlpha(200), color: Colors.transparent,
borderRadius: BorderRadius.circular(borderRadius ?? 10), borderRadius: BorderRadius.circular(borderRadius ?? 50),
backgroundBlendMode: BlendMode.luminosity,
), ),
padding: const EdgeInsets.all(15), child: AnimatedBuilder(
child: const CircularProgressIndicator( animation: borderAnimationController,
color: Colors.white, builder: (context, child) {
strokeWidth: 3, return CustomPaint(
painter: GradientBorderPainter(
animation: borderAnimationController.value,
strokeWidth: 3,
),
child: child,
);
},
child: Padding(
padding: const EdgeInsets.all(15),
child: RotationTransition(
turns: logoAnimationController,
child: const ImmichLogo(
heroTag: 'logo',
),
),
),
), ),
); );
} }
} }
class GradientBorderPainter extends CustomPainter {
final double animation;
final double strokeWidth;
final double opacity = 0.7;
final colors = [
const Color(0xFFFA2921),
const Color(0xFFED79B5),
const Color(0xFFFFB400),
const Color(0xFF1E83F7),
const Color(0xFF18C249),
];
GradientBorderPainter({
required this.animation,
required this.strokeWidth,
});
@override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
final radius = min(size.width, size.height) / 2 - strokeWidth / 2;
// Create a sweep gradient that covers the entire circle
final Rect rect = Rect.fromCircle(center: center, radius: radius);
// Create a paint with the gradient
final paint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = strokeWidth;
// Create a gradient that smoothly transitions between colors
final shader = SweepGradient(
// Use a fixed starting point and let matrix transformation handle rotation
startAngle: 0,
endAngle: 2 * 3.14159,
colors: [
// Repeat colors to ensure smooth transitions
...colors.map((c) => c.withValues(alpha: opacity)),
colors.first.withValues(alpha: opacity),
],
// Add evenly distributed stops
stops: List.generate(
colors.length + 1,
(index) => index / colors.length,
),
tileMode: TileMode.clamp,
// Use transformations to rotate the gradient
transform: GradientRotation(-animation * 2 * 3.14159),
).createShader(rect);
paint.shader = shader;
// Draw the circular border
canvas.drawCircle(center, radius, paint);
}
@override
bool shouldRepaint(GradientBorderPainter oldDelegate) {
return animation != oldDelegate.animation;
}
double min(double a, double b) => a < b ? a : b;
}

View File

@ -303,7 +303,7 @@ class LoginForm extends HookConsumerWidget {
), ),
onPressed: () => context.pushRoute(const SettingsRoute()), onPressed: () => context.pushRoute(const SettingsRoute()),
icon: const Icon(Icons.settings_rounded), icon: const Icon(Icons.settings_rounded),
label: const SizedBox.shrink(), label: const Text(""),
), ),
), ),
const SizedBox(width: 1), const SizedBox(width: 1),

View File

@ -13,7 +13,6 @@ import 'package:immich_mobile/widgets/settings/settings_button_list_tile.dart';
import 'package:immich_mobile/widgets/settings/settings_sub_page_scaffold.dart'; import 'package:immich_mobile/widgets/settings/settings_sub_page_scaffold.dart';
import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart';
import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart';
import 'package:immich_mobile/widgets/common/immich_loading_indicator.dart';
class BackupSettings extends HookConsumerWidget { class BackupSettings extends HookConsumerWidget {
const BackupSettings({ const BackupSettings({
@ -59,7 +58,7 @@ class BackupSettings extends HookConsumerWidget {
? const Column( ? const Column(
children: [ children: [
SizedBox(height: 20), SizedBox(height: 20),
Center(child: ImmichLoadingIndicator()), Center(child: CircularProgressIndicator()),
SizedBox(height: 20), SizedBox(height: 20),
], ],
) )
@ -83,9 +82,7 @@ class BackupSettings extends HookConsumerWidget {
), ),
buttonText: 'sync_albums'.tr(), buttonText: 'sync_albums'.tr(),
child: isAlbumSyncInProgress.value child: isAlbumSyncInProgress.value
? const CircularProgressIndicator.adaptive( ? const CircularProgressIndicator()
strokeWidth: 2,
)
: ElevatedButton( : ElevatedButton(
onPressed: syncAlbums, onPressed: syncAlbums,
child: Text('sync'.tr()), child: Text('sync'.tr()),

View File

@ -1320,6 +1320,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.5.0" version: "1.5.0"
punycode:
dependency: "direct main"
description:
name: punycode
sha256: "39b874cc1f78b94e57db17e74b3f2ba2a96e25c0bebdcc8a571614dccda0ff0c"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
recase: recase:
dependency: transitive dependency: transitive
description: description: