mirror of
				https://github.com/KevinMidboe/immich.git
				synced 2025-10-29 17:40:28 +00:00 
			
		
		
		
	feat(mobile): improved logging page experience (#2158)
* feat(mobile): improve logging page * Use new API for share file * removed unused code * Better safe area on the home screen * Added preparing share dialog to home screen
This commit is contained in:
		| @@ -27,6 +27,7 @@ import 'package:immich_mobile/shared/providers/websocket.provider.dart'; | |||||||
| import 'package:immich_mobile/shared/services/share.service.dart'; | import 'package:immich_mobile/shared/services/share.service.dart'; | ||||||
| import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart'; | import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart'; | ||||||
| import 'package:immich_mobile/shared/ui/immich_toast.dart'; | import 'package:immich_mobile/shared/ui/immich_toast.dart'; | ||||||
|  | import 'package:immich_mobile/shared/ui/share_dialog.dart'; | ||||||
|  |  | ||||||
| class HomePage extends HookConsumerWidget { | class HomePage extends HookConsumerWidget { | ||||||
|   const HomePage({Key? key}) : super(key: key); |   const HomePage({Key? key}) : super(key: key); | ||||||
| @@ -81,7 +82,19 @@ class HomePage extends HookConsumerWidget { | |||||||
|       } |       } | ||||||
|  |  | ||||||
|       void onShareAssets() { |       void onShareAssets() { | ||||||
|         ref.watch(shareServiceProvider).shareAssets(selection.value.toList()); |         showDialog( | ||||||
|  |           context: context, | ||||||
|  |           builder: (BuildContext buildContext) { | ||||||
|  |             ref | ||||||
|  |                 .watch(shareServiceProvider) | ||||||
|  |                 .shareAssets(selection.value.toList()) | ||||||
|  |                 .then((_) => Navigator.of(buildContext).pop()); | ||||||
|  |             return const ShareDialog(); | ||||||
|  |           }, | ||||||
|  |           barrierDismissible: false, | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         // ref.watch(shareServiceProvider).shareAssets(selection.value.toList()); | ||||||
|         selectionEnabledHook.value = false; |         selectionEnabledHook.value = false; | ||||||
|       } |       } | ||||||
|  |  | ||||||
| @@ -244,6 +257,7 @@ class HomePage extends HookConsumerWidget { | |||||||
|  |  | ||||||
|       return SafeArea( |       return SafeArea( | ||||||
|         top: true, |         top: true, | ||||||
|  |         bottom: false, | ||||||
|         child: Stack( |         child: Stack( | ||||||
|           children: [ |           children: [ | ||||||
|             ref.watch(assetProvider).renderList == null || |             ref.watch(assetProvider).renderList == null || | ||||||
| @@ -261,17 +275,14 @@ class HomePage extends HookConsumerWidget { | |||||||
|                     onRefresh: refreshAssets, |                     onRefresh: refreshAssets, | ||||||
|                   ), |                   ), | ||||||
|             if (selectionEnabledHook.value) |             if (selectionEnabledHook.value) | ||||||
|               SafeArea( |               ControlBottomAppBar( | ||||||
|                 bottom: true, |                 onShare: onShareAssets, | ||||||
|                 child: ControlBottomAppBar( |                 onFavorite: onFavoriteAssets, | ||||||
|                   onShare: onShareAssets, |                 onDelete: onDelete, | ||||||
|                   onFavorite: onFavoriteAssets, |                 onAddToAlbum: onAddToAlbum, | ||||||
|                   onDelete: onDelete, |                 albums: albums, | ||||||
|                   onAddToAlbum: onAddToAlbum, |                 sharedAlbums: sharedAlbums, | ||||||
|                   albums: albums, |                 onCreateNewAlbum: onCreateNewAlbum, | ||||||
|                   sharedAlbums: sharedAlbums, |  | ||||||
|                   onCreateNewAlbum: onCreateNewAlbum, |  | ||||||
|                 ), |  | ||||||
|               ), |               ), | ||||||
|           ], |           ], | ||||||
|         ), |         ), | ||||||
| @@ -279,9 +290,11 @@ class HomePage extends HookConsumerWidget { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     return Scaffold( |     return Scaffold( | ||||||
|       appBar: HomePageAppBar( |       appBar: !selectionEnabledHook.value | ||||||
|         onPopBack: reloadAllAsset, |           ? HomePageAppBar( | ||||||
|       ), |               onPopBack: reloadAllAsset, | ||||||
|  |             ) | ||||||
|  |           : null, | ||||||
|       drawer: const ProfileDrawer(), |       drawer: const ProfileDrawer(), | ||||||
|       body: buildBody(), |       body: buildBody(), | ||||||
|     ); |     ); | ||||||
|   | |||||||
| @@ -34,8 +34,10 @@ import 'package:immich_mobile/routing/duplicate_guard.dart'; | |||||||
| import 'package:immich_mobile/routing/gallery_permission_guard.dart'; | import 'package:immich_mobile/routing/gallery_permission_guard.dart'; | ||||||
| import 'package:immich_mobile/shared/models/asset.dart'; | import 'package:immich_mobile/shared/models/asset.dart'; | ||||||
| import 'package:immich_mobile/shared/models/album.dart'; | import 'package:immich_mobile/shared/models/album.dart'; | ||||||
|  | import 'package:immich_mobile/shared/models/logger_message.model.dart'; | ||||||
| import 'package:immich_mobile/shared/providers/api.provider.dart'; | import 'package:immich_mobile/shared/providers/api.provider.dart'; | ||||||
| import 'package:immich_mobile/shared/services/api.service.dart'; | import 'package:immich_mobile/shared/services/api.service.dart'; | ||||||
|  | import 'package:immich_mobile/shared/views/app_log_detail_page.dart'; | ||||||
| import 'package:immich_mobile/shared/views/app_log_page.dart'; | import 'package:immich_mobile/shared/views/app_log_page.dart'; | ||||||
| import 'package:immich_mobile/shared/views/splash_screen.dart'; | import 'package:immich_mobile/shared/views/splash_screen.dart'; | ||||||
| import 'package:immich_mobile/shared/views/tab_controller_page.dart'; | import 'package:immich_mobile/shared/views/tab_controller_page.dart'; | ||||||
| @@ -47,8 +49,12 @@ part 'router.gr.dart'; | |||||||
|   replaceInRouteName: 'Page,Route', |   replaceInRouteName: 'Page,Route', | ||||||
|   routes: <AutoRoute>[ |   routes: <AutoRoute>[ | ||||||
|     AutoRoute(page: SplashScreenPage, initial: true), |     AutoRoute(page: SplashScreenPage, initial: true), | ||||||
|     AutoRoute(page: PermissionOnboardingPage, guards: [AuthGuard, DuplicateGuard]), |     AutoRoute( | ||||||
|     AutoRoute(page: LoginPage, |       page: PermissionOnboardingPage, | ||||||
|  |       guards: [AuthGuard, DuplicateGuard], | ||||||
|  |     ), | ||||||
|  |     AutoRoute( | ||||||
|  |       page: LoginPage, | ||||||
|       guards: [ |       guards: [ | ||||||
|         DuplicateGuard, |         DuplicateGuard, | ||||||
|       ], |       ], | ||||||
| @@ -65,7 +71,10 @@ part 'router.gr.dart'; | |||||||
|       ], |       ], | ||||||
|       transitionsBuilder: TransitionsBuilders.fadeIn, |       transitionsBuilder: TransitionsBuilders.fadeIn, | ||||||
|     ), |     ), | ||||||
|     AutoRoute(page: GalleryViewerPage, guards: [AuthGuard, DuplicateGuard, GalleryPermissionGuard]), |     AutoRoute( | ||||||
|  |       page: GalleryViewerPage, | ||||||
|  |       guards: [AuthGuard, DuplicateGuard, GalleryPermissionGuard], | ||||||
|  |     ), | ||||||
|     AutoRoute(page: VideoViewerPage, guards: [AuthGuard, DuplicateGuard]), |     AutoRoute(page: VideoViewerPage, guards: [AuthGuard, DuplicateGuard]), | ||||||
|     AutoRoute(page: BackupControllerPage, guards: [AuthGuard, DuplicateGuard]), |     AutoRoute(page: BackupControllerPage, guards: [AuthGuard, DuplicateGuard]), | ||||||
|     AutoRoute(page: SearchResultPage, guards: [AuthGuard, DuplicateGuard]), |     AutoRoute(page: SearchResultPage, guards: [AuthGuard, DuplicateGuard]), | ||||||
| @@ -75,7 +84,10 @@ part 'router.gr.dart'; | |||||||
|     AutoRoute(page: FavoritesPage, guards: [AuthGuard, DuplicateGuard]), |     AutoRoute(page: FavoritesPage, guards: [AuthGuard, DuplicateGuard]), | ||||||
|     AutoRoute(page: AllVideosPage, guards: [AuthGuard, DuplicateGuard]), |     AutoRoute(page: AllVideosPage, guards: [AuthGuard, DuplicateGuard]), | ||||||
|     AutoRoute(page: AllMotionPhotosPage, guards: [AuthGuard, DuplicateGuard]), |     AutoRoute(page: AllMotionPhotosPage, guards: [AuthGuard, DuplicateGuard]), | ||||||
|     AutoRoute(page: RecentlyAddedPage, guards: [AuthGuard, DuplicateGuard],), |     AutoRoute( | ||||||
|  |       page: RecentlyAddedPage, | ||||||
|  |       guards: [AuthGuard, DuplicateGuard], | ||||||
|  |     ), | ||||||
|     CustomRoute<AssetSelectionPageResult?>( |     CustomRoute<AssetSelectionPageResult?>( | ||||||
|       page: AssetSelectionPage, |       page: AssetSelectionPage, | ||||||
|       guards: [AuthGuard, DuplicateGuard], |       guards: [AuthGuard, DuplicateGuard], | ||||||
| @@ -92,14 +104,18 @@ part 'router.gr.dart'; | |||||||
|       guards: [AuthGuard, DuplicateGuard], |       guards: [AuthGuard, DuplicateGuard], | ||||||
|       transitionsBuilder: TransitionsBuilders.slideBottom, |       transitionsBuilder: TransitionsBuilders.slideBottom, | ||||||
|     ), |     ), | ||||||
|     AutoRoute(page: BackupAlbumSelectionPage, guards: [AuthGuard, DuplicateGuard]), |     AutoRoute( | ||||||
|  |       page: BackupAlbumSelectionPage, | ||||||
|  |       guards: [AuthGuard, DuplicateGuard], | ||||||
|  |     ), | ||||||
|     AutoRoute(page: AlbumPreviewPage, guards: [AuthGuard, DuplicateGuard]), |     AutoRoute(page: AlbumPreviewPage, guards: [AuthGuard, DuplicateGuard]), | ||||||
|     CustomRoute( |     CustomRoute( | ||||||
|       page: FailedBackupStatusPage, |       page: FailedBackupStatusPage, | ||||||
|       guards: [AuthGuard, DuplicateGuard], |       guards: [AuthGuard, DuplicateGuard], | ||||||
|       transitionsBuilder: TransitionsBuilders.slideBottom, |       transitionsBuilder: TransitionsBuilders.slideBottom, | ||||||
|     ), |     ), | ||||||
|     AutoRoute(page: SettingsPage,  |     AutoRoute( | ||||||
|  |       page: SettingsPage, | ||||||
|       guards: [ |       guards: [ | ||||||
|         AuthGuard, |         AuthGuard, | ||||||
|         DuplicateGuard, |         DuplicateGuard, | ||||||
| @@ -109,6 +125,9 @@ part 'router.gr.dart'; | |||||||
|       page: AppLogPage, |       page: AppLogPage, | ||||||
|       transitionsBuilder: TransitionsBuilders.slideBottom, |       transitionsBuilder: TransitionsBuilders.slideBottom, | ||||||
|     ), |     ), | ||||||
|  |     AutoRoute( | ||||||
|  |       page: AppLogDetailPage, | ||||||
|  |     ), | ||||||
|   ], |   ], | ||||||
| ) | ) | ||||||
| class AppRouter extends _$AppRouter { | class AppRouter extends _$AppRouter { | ||||||
| @@ -116,14 +135,19 @@ class AppRouter extends _$AppRouter { | |||||||
|   final ApiService _apiService; |   final ApiService _apiService; | ||||||
|  |  | ||||||
|   AppRouter( |   AppRouter( | ||||||
|     this._apiService,  |     this._apiService, | ||||||
|     GalleryPermissionNotifier galleryPermissionNotifier, |     GalleryPermissionNotifier galleryPermissionNotifier, | ||||||
|     ) : super( |   ) : super( | ||||||
|           authGuard: AuthGuard(_apiService),  |           authGuard: AuthGuard(_apiService), | ||||||
|           duplicateGuard: DuplicateGuard(), |           duplicateGuard: DuplicateGuard(), | ||||||
|           galleryPermissionGuard: GalleryPermissionGuard(galleryPermissionNotifier), |           galleryPermissionGuard: | ||||||
|  |               GalleryPermissionGuard(galleryPermissionNotifier), | ||||||
|         ); |         ); | ||||||
| } | } | ||||||
|  |  | ||||||
| final appRouterProvider = | final appRouterProvider = Provider( | ||||||
|     Provider((ref) => AppRouter(ref.watch(apiServiceProvider), ref.watch(galleryPermissionNotifier.notifier))); |   (ref) => AppRouter( | ||||||
|  |     ref.watch(apiServiceProvider), | ||||||
|  |     ref.watch(galleryPermissionNotifier.notifier), | ||||||
|  |   ), | ||||||
|  | ); | ||||||
|   | |||||||
| @@ -230,6 +230,16 @@ class _$AppRouter extends RootStackRouter { | |||||||
|         barrierDismissible: false, |         barrierDismissible: false, | ||||||
|       ); |       ); | ||||||
|     }, |     }, | ||||||
|  |     AppLogDetailRoute.name: (routeData) { | ||||||
|  |       final args = routeData.argsAs<AppLogDetailRouteArgs>(); | ||||||
|  |       return MaterialPageX<dynamic>( | ||||||
|  |         routeData: routeData, | ||||||
|  |         child: AppLogDetailPage( | ||||||
|  |           key: args.key, | ||||||
|  |           logMessage: args.logMessage, | ||||||
|  |         ), | ||||||
|  |       ); | ||||||
|  |     }, | ||||||
|     HomeRoute.name: (routeData) { |     HomeRoute.name: (routeData) { | ||||||
|       return MaterialPageX<dynamic>( |       return MaterialPageX<dynamic>( | ||||||
|         routeData: routeData, |         routeData: routeData, | ||||||
| @@ -485,6 +495,10 @@ class _$AppRouter extends RootStackRouter { | |||||||
|           AppLogRoute.name, |           AppLogRoute.name, | ||||||
|           path: '/app-log-page', |           path: '/app-log-page', | ||||||
|         ), |         ), | ||||||
|  |         RouteConfig( | ||||||
|  |           AppLogDetailRoute.name, | ||||||
|  |           path: '/app-log-detail-page', | ||||||
|  |         ), | ||||||
|       ]; |       ]; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -974,6 +988,40 @@ class AppLogRoute extends PageRouteInfo<void> { | |||||||
|   static const String name = 'AppLogRoute'; |   static const String name = 'AppLogRoute'; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /// generated route for | ||||||
|  | /// [AppLogDetailPage] | ||||||
|  | class AppLogDetailRoute extends PageRouteInfo<AppLogDetailRouteArgs> { | ||||||
|  |   AppLogDetailRoute({ | ||||||
|  |     Key? key, | ||||||
|  |     required LoggerMessage logMessage, | ||||||
|  |   }) : super( | ||||||
|  |           AppLogDetailRoute.name, | ||||||
|  |           path: '/app-log-detail-page', | ||||||
|  |           args: AppLogDetailRouteArgs( | ||||||
|  |             key: key, | ||||||
|  |             logMessage: logMessage, | ||||||
|  |           ), | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |   static const String name = 'AppLogDetailRoute'; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class AppLogDetailRouteArgs { | ||||||
|  |   const AppLogDetailRouteArgs({ | ||||||
|  |     this.key, | ||||||
|  |     required this.logMessage, | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   final Key? key; | ||||||
|  |  | ||||||
|  |   final LoggerMessage logMessage; | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   String toString() { | ||||||
|  |     return 'AppLogDetailRouteArgs{key: $key, logMessage: $logMessage}'; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
| /// generated route for | /// generated route for | ||||||
| /// [HomePage] | /// [HomePage] | ||||||
| class HomeRoute extends PageRouteInfo<void> { | class HomeRoute extends PageRouteInfo<void> { | ||||||
|   | |||||||
| @@ -102,15 +102,13 @@ class ImmichLogger { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Share file |     // Share file | ||||||
|     // ignore: deprecated_member_use |     await Share.shareXFiles( | ||||||
|     await Share.shareFiles( |       [XFile(filePath)], | ||||||
|       [filePath], |  | ||||||
|       subject: "Immich logs $dateTime", |       subject: "Immich logs $dateTime", | ||||||
|       sharePositionOrigin: Rect.zero, |       sharePositionOrigin: Rect.zero, | ||||||
|  |     ).then( | ||||||
|  |       (value) => logFile.delete(), | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|     // Clean up temp file |  | ||||||
|     await logFile.delete(); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   /// Flush pending log messages to persistent storage |   /// Flush pending log messages to persistent storage | ||||||
|   | |||||||
| @@ -36,7 +36,6 @@ class ShareService { | |||||||
|       } |       } | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     // ignore: deprecated_member_use |  | ||||||
|     Share.shareXFiles( |     Share.shareXFiles( | ||||||
|       await Future.wait(downloadedXFiles), |       await Future.wait(downloadedXFiles), | ||||||
|       sharePositionOrigin: Rect.zero, |       sharePositionOrigin: Rect.zero, | ||||||
|   | |||||||
| @@ -369,7 +369,6 @@ class SyncService { | |||||||
|     List<AssetPathEntity> onDevice, [ |     List<AssetPathEntity> onDevice, [ | ||||||
|     Set<String>? excludedAssets, |     Set<String>? excludedAssets, | ||||||
|   ]) async { |   ]) async { | ||||||
|     _log.info("Syncing ${onDevice.length} albums from device: $onDevice"); |  | ||||||
|     onDevice.sort((a, b) => a.id.compareTo(b.id)); |     onDevice.sort((a, b) => a.id.compareTo(b.id)); | ||||||
|     final List<Album> inDb = |     final List<Album> inDb = | ||||||
|         await _db.albums.where().localIdIsNotNull().sortByLocalId().findAll(); |         await _db.albums.where().localIdIsNotNull().sortByLocalId().findAll(); | ||||||
|   | |||||||
							
								
								
									
										190
									
								
								mobile/lib/shared/views/app_log_detail_page.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								mobile/lib/shared/views/app_log_detail_page.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,190 @@ | |||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||||
|  | import 'package:immich_mobile/shared/models/logger_message.model.dart'; | ||||||
|  | import 'package:flutter/services.dart'; | ||||||
|  |  | ||||||
|  | class AppLogDetailPage extends HookConsumerWidget { | ||||||
|  |   const AppLogDetailPage({super.key, required this.logMessage}); | ||||||
|  |  | ||||||
|  |   final LoggerMessage logMessage; | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context, WidgetRef ref) { | ||||||
|  |     var isDarkMode = Theme.of(context).brightness == Brightness.dark; | ||||||
|  |  | ||||||
|  |     buildStackMessage(String stackTrace) { | ||||||
|  |       return Padding( | ||||||
|  |         padding: const EdgeInsets.all(8.0), | ||||||
|  |         child: Column( | ||||||
|  |           crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |           children: [ | ||||||
|  |             Row( | ||||||
|  |               mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||||
|  |               crossAxisAlignment: CrossAxisAlignment.center, | ||||||
|  |               children: [ | ||||||
|  |                 Padding( | ||||||
|  |                   padding: const EdgeInsets.only(bottom: 8.0), | ||||||
|  |                   child: Text( | ||||||
|  |                     "STACK TRACES", | ||||||
|  |                     style: TextStyle( | ||||||
|  |                       fontSize: 12.0, | ||||||
|  |                       color: Theme.of(context).primaryColor, | ||||||
|  |                       fontWeight: FontWeight.bold, | ||||||
|  |                     ), | ||||||
|  |                   ), | ||||||
|  |                 ), | ||||||
|  |                 IconButton( | ||||||
|  |                   onPressed: () { | ||||||
|  |                     Clipboard.setData(ClipboardData(text: stackTrace)) | ||||||
|  |                         .then((_) { | ||||||
|  |                       ScaffoldMessenger.of(context).showSnackBar( | ||||||
|  |                         const SnackBar(content: Text("Copied to clipboard")), | ||||||
|  |                       ); | ||||||
|  |                     }); | ||||||
|  |                   }, | ||||||
|  |                   icon: Icon( | ||||||
|  |                     Icons.copy, | ||||||
|  |                     size: 16.0, | ||||||
|  |                     color: Theme.of(context).primaryColor, | ||||||
|  |                   ), | ||||||
|  |                 ) | ||||||
|  |               ], | ||||||
|  |             ), | ||||||
|  |             Container( | ||||||
|  |               decoration: BoxDecoration( | ||||||
|  |                 color: isDarkMode ? Colors.grey[900] : Colors.grey[200], | ||||||
|  |                 borderRadius: BorderRadius.circular(15.0), | ||||||
|  |               ), | ||||||
|  |               child: Padding( | ||||||
|  |                 padding: const EdgeInsets.all(8.0), | ||||||
|  |                 child: SelectableText( | ||||||
|  |                   stackTrace, | ||||||
|  |                   style: const TextStyle( | ||||||
|  |                     fontSize: 12.0, | ||||||
|  |                     fontWeight: FontWeight.bold, | ||||||
|  |                     fontFamily: "Inconsolata", | ||||||
|  |                   ), | ||||||
|  |                 ), | ||||||
|  |               ), | ||||||
|  |             ), | ||||||
|  |           ], | ||||||
|  |         ), | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     buildLogMessage(String message) { | ||||||
|  |       return Padding( | ||||||
|  |         padding: const EdgeInsets.all(8.0), | ||||||
|  |         child: Column( | ||||||
|  |           crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |           children: [ | ||||||
|  |             Row( | ||||||
|  |               mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||||
|  |               crossAxisAlignment: CrossAxisAlignment.center, | ||||||
|  |               children: [ | ||||||
|  |                 Padding( | ||||||
|  |                   padding: const EdgeInsets.only(bottom: 8.0), | ||||||
|  |                   child: Text( | ||||||
|  |                     "MESSAGE", | ||||||
|  |                     style: TextStyle( | ||||||
|  |                       fontSize: 12.0, | ||||||
|  |                       color: Theme.of(context).primaryColor, | ||||||
|  |                       fontWeight: FontWeight.bold, | ||||||
|  |                     ), | ||||||
|  |                   ), | ||||||
|  |                 ), | ||||||
|  |                 IconButton( | ||||||
|  |                   onPressed: () { | ||||||
|  |                     Clipboard.setData(ClipboardData(text: message)).then((_) { | ||||||
|  |                       ScaffoldMessenger.of(context).showSnackBar( | ||||||
|  |                         const SnackBar(content: Text("Copied to clipboard")), | ||||||
|  |                       ); | ||||||
|  |                     }); | ||||||
|  |                   }, | ||||||
|  |                   icon: Icon( | ||||||
|  |                     Icons.copy, | ||||||
|  |                     size: 16.0, | ||||||
|  |                     color: Theme.of(context).primaryColor, | ||||||
|  |                   ), | ||||||
|  |                 ) | ||||||
|  |               ], | ||||||
|  |             ), | ||||||
|  |             Container( | ||||||
|  |               decoration: BoxDecoration( | ||||||
|  |                 color: isDarkMode ? Colors.grey[900] : Colors.grey[200], | ||||||
|  |                 borderRadius: BorderRadius.circular(15.0), | ||||||
|  |               ), | ||||||
|  |               child: Padding( | ||||||
|  |                 padding: const EdgeInsets.all(8.0), | ||||||
|  |                 child: SelectableText( | ||||||
|  |                   message, | ||||||
|  |                   style: const TextStyle( | ||||||
|  |                     fontSize: 12.0, | ||||||
|  |                     fontWeight: FontWeight.bold, | ||||||
|  |                     fontFamily: "Inconsolata", | ||||||
|  |                   ), | ||||||
|  |                 ), | ||||||
|  |               ), | ||||||
|  |             ), | ||||||
|  |           ], | ||||||
|  |         ), | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     buildLogContext1(String context1) { | ||||||
|  |       return Padding( | ||||||
|  |         padding: const EdgeInsets.all(8.0), | ||||||
|  |         child: Column( | ||||||
|  |           crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |           children: [ | ||||||
|  |             Padding( | ||||||
|  |               padding: const EdgeInsets.only(bottom: 8.0), | ||||||
|  |               child: Text( | ||||||
|  |                 "FROM", | ||||||
|  |                 style: TextStyle( | ||||||
|  |                   fontSize: 12.0, | ||||||
|  |                   color: Theme.of(context).primaryColor, | ||||||
|  |                   fontWeight: FontWeight.bold, | ||||||
|  |                 ), | ||||||
|  |               ), | ||||||
|  |             ), | ||||||
|  |             Container( | ||||||
|  |               decoration: BoxDecoration( | ||||||
|  |                 color: isDarkMode ? Colors.grey[900] : Colors.grey[200], | ||||||
|  |                 borderRadius: BorderRadius.circular(15.0), | ||||||
|  |               ), | ||||||
|  |               child: Padding( | ||||||
|  |                 padding: const EdgeInsets.all(8.0), | ||||||
|  |                 child: SelectableText( | ||||||
|  |                   context1.toString(), | ||||||
|  |                   style: const TextStyle( | ||||||
|  |                     fontSize: 12.0, | ||||||
|  |                     fontWeight: FontWeight.bold, | ||||||
|  |                     fontFamily: "Inconsolata", | ||||||
|  |                   ), | ||||||
|  |                 ), | ||||||
|  |               ), | ||||||
|  |             ), | ||||||
|  |           ], | ||||||
|  |         ), | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return Scaffold( | ||||||
|  |       appBar: AppBar( | ||||||
|  |         title: const Text("Log Detail"), | ||||||
|  |       ), | ||||||
|  |       body: SafeArea( | ||||||
|  |         child: ListView( | ||||||
|  |           children: [ | ||||||
|  |             buildLogMessage(logMessage.message), | ||||||
|  |             if (logMessage.context1 != null) | ||||||
|  |               buildLogContext1(logMessage.context1.toString()), | ||||||
|  |             if (logMessage.context2 != null) | ||||||
|  |               buildStackMessage(logMessage.context2.toString()) | ||||||
|  |           ], | ||||||
|  |         ), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -2,6 +2,7 @@ import 'package:auto_route/auto_route.dart'; | |||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| import 'package:flutter_hooks/flutter_hooks.dart'; | import 'package:flutter_hooks/flutter_hooks.dart'; | ||||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||||
|  | import 'package:immich_mobile/routing/router.dart'; | ||||||
| import 'package:immich_mobile/shared/models/logger_message.model.dart'; | import 'package:immich_mobile/shared/models/logger_message.model.dart'; | ||||||
| import 'package:immich_mobile/shared/services/immich_logger.service.dart'; | import 'package:immich_mobile/shared/services/immich_logger.service.dart'; | ||||||
| import 'package:intl/intl.dart'; | import 'package:intl/intl.dart'; | ||||||
| @@ -123,6 +124,12 @@ class AppLogPage extends HookConsumerWidget { | |||||||
|         itemBuilder: (context, index) { |         itemBuilder: (context, index) { | ||||||
|           var logMessage = logMessages.value[index]; |           var logMessage = logMessages.value[index]; | ||||||
|           return ListTile( |           return ListTile( | ||||||
|  |             onTap: () => AutoRouter.of(context).push( | ||||||
|  |               AppLogDetailRoute( | ||||||
|  |                 logMessage: logMessage, | ||||||
|  |               ), | ||||||
|  |             ), | ||||||
|  |             trailing: const Icon(Icons.arrow_forward_ios_rounded), | ||||||
|             visualDensity: VisualDensity.compact, |             visualDensity: VisualDensity.compact, | ||||||
|             dense: true, |             dense: true, | ||||||
|             tileColor: getTileColor(logMessage.level), |             tileColor: getTileColor(logMessage.level), | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user