mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-03 19:17:11 -05:00 
			
		
		
		
	refactor(mobile): move error details to separate DB column (#6898)
* Add "details" column to LoggerMessage * Include error details in log details page * Move error details out of log message * Add error message to mixin * Create extension for HTTP Response logging * Fix analyze errors * format * fix analyze errors, format again
This commit is contained in:
		
							parent
							
								
									878932f87e
								
							
						
					
					
						commit
						bc3979029d
					
				@ -30,7 +30,7 @@ extension LogOnError<T> on AsyncValue<T> {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (hasError && !hasValue) {
 | 
					    if (hasError && !hasValue) {
 | 
				
			||||||
      _asyncErrorLogger.severe("$error", error, stackTrace);
 | 
					      _asyncErrorLogger.severe('Could not load value', error, stackTrace);
 | 
				
			||||||
      return onError?.call(error, stackTrace) ??
 | 
					      return onError?.call(error, stackTrace) ??
 | 
				
			||||||
          ScaffoldErrorBody(errorMsg: error?.toString());
 | 
					          ScaffoldErrorBody(errorMsg: error?.toString());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										5
									
								
								mobile/lib/extensions/response_extensions.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								mobile/lib/extensions/response_extensions.dart
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					import 'package:http/http.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extension LoggerExtension on Response {
 | 
				
			||||||
 | 
					  String toLoggerString() => "Status: $statusCode $reasonPhrase\n\n$body";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -73,15 +73,14 @@ Future<void> initApp() async {
 | 
				
			|||||||
  FlutterError.onError = (details) {
 | 
					  FlutterError.onError = (details) {
 | 
				
			||||||
    FlutterError.presentError(details);
 | 
					    FlutterError.presentError(details);
 | 
				
			||||||
    log.severe(
 | 
					    log.severe(
 | 
				
			||||||
      'FlutterError - Catch all error: ${details.toString()} - ${details.exception} - ${details.library} - ${details.context} - ${details.stack}',
 | 
					      'FlutterError - Catch all',
 | 
				
			||||||
      details,
 | 
					      "${details.toString()}\nException: ${details.exception}\nLibrary: ${details.library}\nContext: ${details.context}",
 | 
				
			||||||
      details.stack,
 | 
					      details.stack,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  PlatformDispatcher.instance.onError = (error, stack) {
 | 
					  PlatformDispatcher.instance.onError = (error, stack) {
 | 
				
			||||||
    log.severe('PlatformDispatcher - Catch all error: $error', error, stack);
 | 
					    log.severe('PlatformDispatcher - Catch all', error, stack);
 | 
				
			||||||
    debugPrint("PlatformDispatcher - Catch all error: $error $stack");
 | 
					 | 
				
			||||||
    return true;
 | 
					    return true;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -10,13 +10,14 @@ mixin ErrorLoggerMixin {
 | 
				
			|||||||
  /// Else, logs the error to the overrided logger and returns an AsyncError<>
 | 
					  /// Else, logs the error to the overrided logger and returns an AsyncError<>
 | 
				
			||||||
  AsyncFuture<T> guardError<T>(
 | 
					  AsyncFuture<T> guardError<T>(
 | 
				
			||||||
    Future<T> Function() fn, {
 | 
					    Future<T> Function() fn, {
 | 
				
			||||||
 | 
					    required String errorMessage,
 | 
				
			||||||
    Level logLevel = Level.SEVERE,
 | 
					    Level logLevel = Level.SEVERE,
 | 
				
			||||||
  }) async {
 | 
					  }) async {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      final result = await fn();
 | 
					      final result = await fn();
 | 
				
			||||||
      return AsyncData(result);
 | 
					      return AsyncData(result);
 | 
				
			||||||
    } catch (error, stackTrace) {
 | 
					    } catch (error, stackTrace) {
 | 
				
			||||||
      logger.log(logLevel, "$error", error, stackTrace);
 | 
					      logger.log(logLevel, errorMessage, error, stackTrace);
 | 
				
			||||||
      return AsyncError(error, stackTrace);
 | 
					      return AsyncError(error, stackTrace);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -26,12 +27,13 @@ mixin ErrorLoggerMixin {
 | 
				
			|||||||
  Future<T> logError<T>(
 | 
					  Future<T> logError<T>(
 | 
				
			||||||
    Future<T> Function() fn, {
 | 
					    Future<T> Function() fn, {
 | 
				
			||||||
    required T defaultValue,
 | 
					    required T defaultValue,
 | 
				
			||||||
 | 
					    required String errorMessage,
 | 
				
			||||||
    Level logLevel = Level.SEVERE,
 | 
					    Level logLevel = Level.SEVERE,
 | 
				
			||||||
  }) async {
 | 
					  }) async {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      return await fn();
 | 
					      return await fn();
 | 
				
			||||||
    } catch (error, stackTrace) {
 | 
					    } catch (error, stackTrace) {
 | 
				
			||||||
      logger.log(logLevel, "$error", error, stackTrace);
 | 
					      logger.log(logLevel, errorMessage, error, stackTrace);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return defaultValue;
 | 
					    return defaultValue;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -24,6 +24,7 @@ class ActivityService with ErrorLoggerMixin {
 | 
				
			|||||||
        return list != null ? list.map(Activity.fromDto).toList() : [];
 | 
					        return list != null ? list.map(Activity.fromDto).toList() : [];
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      defaultValue: [],
 | 
					      defaultValue: [],
 | 
				
			||||||
 | 
					      errorMessage: "Failed to get all activities for album $albumId",
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -35,6 +36,7 @@ class ActivityService with ErrorLoggerMixin {
 | 
				
			|||||||
        return dto?.comments ?? 0;
 | 
					        return dto?.comments ?? 0;
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      defaultValue: 0,
 | 
					      defaultValue: 0,
 | 
				
			||||||
 | 
					      errorMessage: "Failed to statistics for album $albumId",
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -45,6 +47,7 @@ class ActivityService with ErrorLoggerMixin {
 | 
				
			|||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      defaultValue: false,
 | 
					      defaultValue: false,
 | 
				
			||||||
 | 
					      errorMessage: "Failed to delete activity",
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -54,21 +57,24 @@ class ActivityService with ErrorLoggerMixin {
 | 
				
			|||||||
    String? assetId,
 | 
					    String? assetId,
 | 
				
			||||||
    String? comment,
 | 
					    String? comment,
 | 
				
			||||||
  }) async {
 | 
					  }) async {
 | 
				
			||||||
    return guardError(() async {
 | 
					    return guardError(
 | 
				
			||||||
      final dto = await _apiService.activityApi.createActivity(
 | 
					      () async {
 | 
				
			||||||
        ActivityCreateDto(
 | 
					        final dto = await _apiService.activityApi.createActivity(
 | 
				
			||||||
          albumId: albumId,
 | 
					          ActivityCreateDto(
 | 
				
			||||||
          type: type == ActivityType.comment
 | 
					            albumId: albumId,
 | 
				
			||||||
              ? ReactionType.comment
 | 
					            type: type == ActivityType.comment
 | 
				
			||||||
              : ReactionType.like,
 | 
					                ? ReactionType.comment
 | 
				
			||||||
          assetId: assetId,
 | 
					                : ReactionType.like,
 | 
				
			||||||
          comment: comment,
 | 
					            assetId: assetId,
 | 
				
			||||||
        ),
 | 
					            comment: comment,
 | 
				
			||||||
      );
 | 
					          ),
 | 
				
			||||||
      if (dto != null) {
 | 
					        );
 | 
				
			||||||
        return Activity.fromDto(dto);
 | 
					        if (dto != null) {
 | 
				
			||||||
      }
 | 
					          return Activity.fromDto(dto);
 | 
				
			||||||
      throw NoResponseDtoError();
 | 
					        }
 | 
				
			||||||
    });
 | 
					        throw NoResponseDtoError();
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      errorMessage: "Failed to create $type for album $albumId",
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,7 @@
 | 
				
			|||||||
import 'dart:io';
 | 
					import 'dart:io';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
					import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
				
			||||||
 | 
					import 'package:immich_mobile/extensions/response_extensions.dart';
 | 
				
			||||||
import 'package:immich_mobile/shared/models/asset.dart';
 | 
					import 'package:immich_mobile/shared/models/asset.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';
 | 
				
			||||||
@ -39,7 +40,8 @@ class ImageViewerService {
 | 
				
			|||||||
          final failedResponse =
 | 
					          final failedResponse =
 | 
				
			||||||
              imageResponse.statusCode != 200 ? imageResponse : motionReponse;
 | 
					              imageResponse.statusCode != 200 ? imageResponse : motionReponse;
 | 
				
			||||||
          _log.severe(
 | 
					          _log.severe(
 | 
				
			||||||
            "Motion asset download failed with status - ${failedResponse.statusCode} and response - ${failedResponse.body}",
 | 
					            "Motion asset download failed",
 | 
				
			||||||
 | 
					            failedResponse.toLoggerString(),
 | 
				
			||||||
          );
 | 
					          );
 | 
				
			||||||
          return false;
 | 
					          return false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -75,9 +77,7 @@ class ImageViewerService {
 | 
				
			|||||||
            .downloadFileWithHttpInfo(asset.remoteId!);
 | 
					            .downloadFileWithHttpInfo(asset.remoteId!);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (res.statusCode != 200) {
 | 
					        if (res.statusCode != 200) {
 | 
				
			||||||
          _log.severe(
 | 
					          _log.severe("Asset download failed", res.toLoggerString());
 | 
				
			||||||
            "Asset download failed with status - ${res.statusCode} and response - ${res.body}",
 | 
					 | 
				
			||||||
          );
 | 
					 | 
				
			||||||
          return false;
 | 
					          return false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -98,7 +98,7 @@ class ImageViewerService {
 | 
				
			|||||||
        return entity != null;
 | 
					        return entity != null;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } catch (error, stack) {
 | 
					    } catch (error, stack) {
 | 
				
			||||||
      _log.severe("Error saving file ${error.toString()}", error, stack);
 | 
					      _log.severe("Error saving downloaded asset", error, stack);
 | 
				
			||||||
      return false;
 | 
					      return false;
 | 
				
			||||||
    } finally {
 | 
					    } finally {
 | 
				
			||||||
      // Clear temp files
 | 
					      // Clear temp files
 | 
				
			||||||
 | 
				
			|||||||
@ -48,7 +48,7 @@ class DescriptionInput extends HookConsumerWidget {
 | 
				
			|||||||
        );
 | 
					        );
 | 
				
			||||||
      } catch (error, stack) {
 | 
					      } catch (error, stack) {
 | 
				
			||||||
        hasError.value = true;
 | 
					        hasError.value = true;
 | 
				
			||||||
        _log.severe("Error updating description $error", error, stack);
 | 
					        _log.severe("Error updating description", error, stack);
 | 
				
			||||||
        ImmichToast.show(
 | 
					        ImmichToast.show(
 | 
				
			||||||
          context: context,
 | 
					          context: context,
 | 
				
			||||||
          msg: "description_input_submit_error".tr(),
 | 
					          msg: "description_input_submit_error".tr(),
 | 
				
			||||||
 | 
				
			|||||||
@ -245,7 +245,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
 | 
				
			|||||||
        } catch (e, stack) {
 | 
					        } catch (e, stack) {
 | 
				
			||||||
          log.severe(
 | 
					          log.severe(
 | 
				
			||||||
            "Failed to get thumbnail for album ${album.name}",
 | 
					            "Failed to get thumbnail for album ${album.name}",
 | 
				
			||||||
            e.toString(),
 | 
					            e,
 | 
				
			||||||
            stack,
 | 
					            stack,
 | 
				
			||||||
          );
 | 
					          );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -108,7 +108,7 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
 | 
				
			|||||||
          .then((_) => log.info("Logout was successful for $userEmail"))
 | 
					          .then((_) => log.info("Logout was successful for $userEmail"))
 | 
				
			||||||
          .onError(
 | 
					          .onError(
 | 
				
			||||||
            (error, stackTrace) =>
 | 
					            (error, stackTrace) =>
 | 
				
			||||||
                log.severe("Error logging out $userEmail", error, stackTrace),
 | 
					                log.severe("Logout failed for $userEmail", error, stackTrace),
 | 
				
			||||||
          );
 | 
					          );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      await Future.wait([
 | 
					      await Future.wait([
 | 
				
			||||||
@ -129,8 +129,8 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
 | 
				
			|||||||
        shouldChangePassword: false,
 | 
					        shouldChangePassword: false,
 | 
				
			||||||
        isAuthenticated: false,
 | 
					        isAuthenticated: false,
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e, stack) {
 | 
				
			||||||
      log.severe("Error logging out $e");
 | 
					      log.severe('Logout failed', e, stack);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -36,7 +36,7 @@ class OAuthService {
 | 
				
			|||||||
        ),
 | 
					        ),
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    } catch (e, stack) {
 | 
					    } catch (e, stack) {
 | 
				
			||||||
      log.severe("Error performing oAuthLogin: ${e.toString()}", e, stack);
 | 
					      log.severe("OAuth login failed", e, stack);
 | 
				
			||||||
      return null;
 | 
					      return null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,7 @@
 | 
				
			|||||||
import 'dart:io';
 | 
					import 'dart:io';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					import 'package:immich_mobile/extensions/response_extensions.dart';
 | 
				
			||||||
import 'package:immich_mobile/modules/map/models/map_state.model.dart';
 | 
					import 'package:immich_mobile/modules/map/models/map_state.model.dart';
 | 
				
			||||||
import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart';
 | 
					import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart';
 | 
				
			||||||
import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
 | 
					import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
 | 
				
			||||||
@ -51,7 +52,8 @@ class MapStateNotifier extends _$MapStateNotifier {
 | 
				
			|||||||
        lightStyleFetched: AsyncError(lightResponse.body, StackTrace.current),
 | 
					        lightStyleFetched: AsyncError(lightResponse.body, StackTrace.current),
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
      _log.severe(
 | 
					      _log.severe(
 | 
				
			||||||
        "Cannot fetch map light style with status - ${lightResponse.statusCode} and response - ${lightResponse.body}",
 | 
					        "Cannot fetch map light style",
 | 
				
			||||||
 | 
					        lightResponse.toLoggerString(),
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -77,9 +79,7 @@ class MapStateNotifier extends _$MapStateNotifier {
 | 
				
			|||||||
      state = state.copyWith(
 | 
					      state = state.copyWith(
 | 
				
			||||||
        darkStyleFetched: AsyncError(darkResponse.body, StackTrace.current),
 | 
					        darkStyleFetched: AsyncError(darkResponse.body, StackTrace.current),
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
      _log.severe(
 | 
					      _log.severe("Cannot fetch map dark style", darkResponse.toLoggerString());
 | 
				
			||||||
        "Cannot fetch map dark style with status - ${darkResponse.statusCode} and response - ${darkResponse.body}",
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -28,6 +28,7 @@ class MapSerivce with ErrorLoggerMixin {
 | 
				
			|||||||
        return markers?.map(MapMarker.fromDto) ?? [];
 | 
					        return markers?.map(MapMarker.fromDto) ?? [];
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      defaultValue: [],
 | 
					      defaultValue: [],
 | 
				
			||||||
 | 
					      errorMessage: "Failed to get map markers",
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -105,10 +105,8 @@ class MapUtils {
 | 
				
			|||||||
        timeLimit: const Duration(seconds: 5),
 | 
					        timeLimit: const Duration(seconds: 5),
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
      return (currentUserLocation, null);
 | 
					      return (currentUserLocation, null);
 | 
				
			||||||
    } catch (error) {
 | 
					    } catch (error, stack) {
 | 
				
			||||||
      _log.severe(
 | 
					      _log.severe("Cannot get user's current location", error, stack);
 | 
				
			||||||
        "Cannot get user's current location due to ${error.toString()}",
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
      return (null, LocationPermission.unableToDetermine);
 | 
					      return (null, LocationPermission.unableToDetermine);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -147,7 +147,7 @@ class MapAssetGrid extends HookConsumerWidget {
 | 
				
			|||||||
                        },
 | 
					                        },
 | 
				
			||||||
                        error: (error, stackTrace) {
 | 
					                        error: (error, stackTrace) {
 | 
				
			||||||
                          log.warning(
 | 
					                          log.warning(
 | 
				
			||||||
                            "Cannot get assets in the current map bounds $error",
 | 
					                            "Cannot get assets in the current map bounds",
 | 
				
			||||||
                            error,
 | 
					                            error,
 | 
				
			||||||
                            stackTrace,
 | 
					                            stackTrace,
 | 
				
			||||||
                          );
 | 
					                          );
 | 
				
			||||||
 | 
				
			|||||||
@ -47,7 +47,7 @@ class MemoryService {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      return memories.isNotEmpty ? memories : null;
 | 
					      return memories.isNotEmpty ? memories : null;
 | 
				
			||||||
    } catch (error, stack) {
 | 
					    } catch (error, stack) {
 | 
				
			||||||
      log.severe("Cannot get memories ${error.toString()}", error, stack);
 | 
					      log.severe("Cannot get memories", error, stack);
 | 
				
			||||||
      return null;
 | 
					      return null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -40,7 +40,7 @@ class PartnerService {
 | 
				
			|||||||
        return userDtos.map((u) => User.fromPartnerDto(u)).toList();
 | 
					        return userDtos.map((u) => User.fromPartnerDto(u)).toList();
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      _log.warning("failed to get partners for direction $direction:\n$e");
 | 
					      _log.warning("Failed to get partners for direction $direction", e);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null;
 | 
					    return null;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -51,7 +51,7 @@ class PartnerService {
 | 
				
			|||||||
      partner.isPartnerSharedBy = false;
 | 
					      partner.isPartnerSharedBy = false;
 | 
				
			||||||
      await _db.writeTxn(() => _db.users.put(partner));
 | 
					      await _db.writeTxn(() => _db.users.put(partner));
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      _log.warning("failed to remove partner ${partner.id}:\n$e");
 | 
					      _log.warning("Failed to remove partner ${partner.id}", e);
 | 
				
			||||||
      return false;
 | 
					      return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return true;
 | 
					    return true;
 | 
				
			||||||
@ -66,7 +66,7 @@ class PartnerService {
 | 
				
			|||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      _log.warning("failed to add partner ${partner.id}:\n$e");
 | 
					      _log.warning("Failed to add partner ${partner.id}", e);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -81,7 +81,7 @@ class PartnerService {
 | 
				
			|||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      _log.warning("failed to update partner ${partner.id}:\n$e");
 | 
					      _log.warning("Failed to update partner ${partner.id}", e);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -22,7 +22,7 @@ class SharedLinkService {
 | 
				
			|||||||
          ? AsyncData(list.map(SharedLink.fromDto).toList())
 | 
					          ? AsyncData(list.map(SharedLink.fromDto).toList())
 | 
				
			||||||
          : const AsyncData([]);
 | 
					          : const AsyncData([]);
 | 
				
			||||||
    } catch (e, stack) {
 | 
					    } catch (e, stack) {
 | 
				
			||||||
      _log.severe("failed to fetch shared links - $e");
 | 
					      _log.severe("Failed to fetch shared links", e, stack);
 | 
				
			||||||
      return AsyncError(e, stack);
 | 
					      return AsyncError(e, stack);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -31,7 +31,7 @@ class SharedLinkService {
 | 
				
			|||||||
    try {
 | 
					    try {
 | 
				
			||||||
      return await _apiService.sharedLinkApi.removeSharedLink(id);
 | 
					      return await _apiService.sharedLinkApi.removeSharedLink(id);
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      _log.severe("failed to delete shared link id - $id with error - $e");
 | 
					      _log.severe("Failed to delete shared link id - $id", e);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -81,7 +81,7 @@ class SharedLinkService {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      _log.severe("failed to create shared link with error - $e");
 | 
					      _log.severe("Failed to create shared link", e);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null;
 | 
					    return null;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -113,7 +113,7 @@ class SharedLinkService {
 | 
				
			|||||||
        return SharedLink.fromDto(responseDto);
 | 
					        return SharedLink.fromDto(responseDto);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      _log.severe("failed to update shared link id - $id with error - $e");
 | 
					      _log.severe("Failed to update shared link id - $id", e);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null;
 | 
					    return null;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -44,7 +44,7 @@ class TrashNotifier extends StateNotifier<bool> {
 | 
				
			|||||||
          .read(syncServiceProvider)
 | 
					          .read(syncServiceProvider)
 | 
				
			||||||
          .handleRemoteAssetRemoval(idsToRemove.cast<String>().toList());
 | 
					          .handleRemoteAssetRemoval(idsToRemove.cast<String>().toList());
 | 
				
			||||||
    } catch (error, stack) {
 | 
					    } catch (error, stack) {
 | 
				
			||||||
      _log.severe("Cannot empty trash ${error.toString()}", error, stack);
 | 
					      _log.severe("Cannot empty trash", error, stack);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -70,7 +70,7 @@ class TrashNotifier extends StateNotifier<bool> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      return isRemoved;
 | 
					      return isRemoved;
 | 
				
			||||||
    } catch (error, stack) {
 | 
					    } catch (error, stack) {
 | 
				
			||||||
      _log.severe("Cannot empty trash ${error.toString()}", error, stack);
 | 
					      _log.severe("Cannot remove assets", error, stack);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -93,7 +93,7 @@ class TrashNotifier extends StateNotifier<bool> {
 | 
				
			|||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } catch (error, stack) {
 | 
					    } catch (error, stack) {
 | 
				
			||||||
      _log.severe("Cannot restore trash ${error.toString()}", error, stack);
 | 
					      _log.severe("Cannot restore assets", error, stack);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -123,7 +123,7 @@ class TrashNotifier extends StateNotifier<bool> {
 | 
				
			|||||||
        await _db.assets.putAll(updatedAssets);
 | 
					        await _db.assets.putAll(updatedAssets);
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    } catch (error, stack) {
 | 
					    } catch (error, stack) {
 | 
				
			||||||
      _log.severe("Cannot restore trash ${error.toString()}", error, stack);
 | 
					      _log.severe("Cannot restore trash", error, stack);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -25,7 +25,7 @@ class TrashService {
 | 
				
			|||||||
      await _apiService.trashApi.restoreAssets(BulkIdsDto(ids: remoteIds));
 | 
					      await _apiService.trashApi.restoreAssets(BulkIdsDto(ids: remoteIds));
 | 
				
			||||||
      return true;
 | 
					      return true;
 | 
				
			||||||
    } catch (error, stack) {
 | 
					    } catch (error, stack) {
 | 
				
			||||||
      _log.severe("Cannot restore assets ${error.toString()}", error, stack);
 | 
					      _log.severe("Cannot restore assets", error, stack);
 | 
				
			||||||
      return false;
 | 
					      return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -34,7 +34,7 @@ class TrashService {
 | 
				
			|||||||
    try {
 | 
					    try {
 | 
				
			||||||
      await _apiService.trashApi.emptyTrash();
 | 
					      await _apiService.trashApi.emptyTrash();
 | 
				
			||||||
    } catch (error, stack) {
 | 
					    } catch (error, stack) {
 | 
				
			||||||
      _log.severe("Cannot empty trash ${error.toString()}", error, stack);
 | 
					      _log.severe("Cannot empty trash", error, stack);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -42,7 +42,7 @@ class TrashService {
 | 
				
			|||||||
    try {
 | 
					    try {
 | 
				
			||||||
      await _apiService.trashApi.restoreTrash();
 | 
					      await _apiService.trashApi.restoreTrash();
 | 
				
			||||||
    } catch (error, stack) {
 | 
					    } catch (error, stack) {
 | 
				
			||||||
      _log.severe("Cannot restore trash ${error.toString()}", error, stack);
 | 
					      _log.severe("Cannot restore trash", error, stack);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -9,6 +9,7 @@ part 'logger_message.model.g.dart';
 | 
				
			|||||||
class LoggerMessage {
 | 
					class LoggerMessage {
 | 
				
			||||||
  Id id = Isar.autoIncrement;
 | 
					  Id id = Isar.autoIncrement;
 | 
				
			||||||
  String message;
 | 
					  String message;
 | 
				
			||||||
 | 
					  String? details;
 | 
				
			||||||
  @Enumerated(EnumType.ordinal)
 | 
					  @Enumerated(EnumType.ordinal)
 | 
				
			||||||
  LogLevel level = LogLevel.INFO;
 | 
					  LogLevel level = LogLevel.INFO;
 | 
				
			||||||
  DateTime createdAt;
 | 
					  DateTime createdAt;
 | 
				
			||||||
@ -17,6 +18,7 @@ class LoggerMessage {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  LoggerMessage({
 | 
					  LoggerMessage({
 | 
				
			||||||
    required this.message,
 | 
					    required this.message,
 | 
				
			||||||
 | 
					    required this.details,
 | 
				
			||||||
    required this.level,
 | 
					    required this.level,
 | 
				
			||||||
    required this.createdAt,
 | 
					    required this.createdAt,
 | 
				
			||||||
    required this.context1,
 | 
					    required this.context1,
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										220
									
								
								mobile/lib/shared/models/logger_message.model.g.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										220
									
								
								mobile/lib/shared/models/logger_message.model.g.dart
									
									
									
										generated
									
									
									
								
							@ -32,14 +32,19 @@ const LoggerMessageSchema = CollectionSchema(
 | 
				
			|||||||
      name: r'createdAt',
 | 
					      name: r'createdAt',
 | 
				
			||||||
      type: IsarType.dateTime,
 | 
					      type: IsarType.dateTime,
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    r'level': PropertySchema(
 | 
					    r'details': PropertySchema(
 | 
				
			||||||
      id: 3,
 | 
					      id: 3,
 | 
				
			||||||
 | 
					      name: r'details',
 | 
				
			||||||
 | 
					      type: IsarType.string,
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    r'level': PropertySchema(
 | 
				
			||||||
 | 
					      id: 4,
 | 
				
			||||||
      name: r'level',
 | 
					      name: r'level',
 | 
				
			||||||
      type: IsarType.byte,
 | 
					      type: IsarType.byte,
 | 
				
			||||||
      enumMap: _LoggerMessagelevelEnumValueMap,
 | 
					      enumMap: _LoggerMessagelevelEnumValueMap,
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    r'message': PropertySchema(
 | 
					    r'message': PropertySchema(
 | 
				
			||||||
      id: 4,
 | 
					      id: 5,
 | 
				
			||||||
      name: r'message',
 | 
					      name: r'message',
 | 
				
			||||||
      type: IsarType.string,
 | 
					      type: IsarType.string,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
@ -76,6 +81,12 @@ int _loggerMessageEstimateSize(
 | 
				
			|||||||
      bytesCount += 3 + value.length * 3;
 | 
					      bytesCount += 3 + value.length * 3;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    final value = object.details;
 | 
				
			||||||
 | 
					    if (value != null) {
 | 
				
			||||||
 | 
					      bytesCount += 3 + value.length * 3;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  bytesCount += 3 + object.message.length * 3;
 | 
					  bytesCount += 3 + object.message.length * 3;
 | 
				
			||||||
  return bytesCount;
 | 
					  return bytesCount;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -89,8 +100,9 @@ void _loggerMessageSerialize(
 | 
				
			|||||||
  writer.writeString(offsets[0], object.context1);
 | 
					  writer.writeString(offsets[0], object.context1);
 | 
				
			||||||
  writer.writeString(offsets[1], object.context2);
 | 
					  writer.writeString(offsets[1], object.context2);
 | 
				
			||||||
  writer.writeDateTime(offsets[2], object.createdAt);
 | 
					  writer.writeDateTime(offsets[2], object.createdAt);
 | 
				
			||||||
  writer.writeByte(offsets[3], object.level.index);
 | 
					  writer.writeString(offsets[3], object.details);
 | 
				
			||||||
  writer.writeString(offsets[4], object.message);
 | 
					  writer.writeByte(offsets[4], object.level.index);
 | 
				
			||||||
 | 
					  writer.writeString(offsets[5], object.message);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LoggerMessage _loggerMessageDeserialize(
 | 
					LoggerMessage _loggerMessageDeserialize(
 | 
				
			||||||
@ -103,9 +115,10 @@ LoggerMessage _loggerMessageDeserialize(
 | 
				
			|||||||
    context1: reader.readStringOrNull(offsets[0]),
 | 
					    context1: reader.readStringOrNull(offsets[0]),
 | 
				
			||||||
    context2: reader.readStringOrNull(offsets[1]),
 | 
					    context2: reader.readStringOrNull(offsets[1]),
 | 
				
			||||||
    createdAt: reader.readDateTime(offsets[2]),
 | 
					    createdAt: reader.readDateTime(offsets[2]),
 | 
				
			||||||
    level: _LoggerMessagelevelValueEnumMap[reader.readByteOrNull(offsets[3])] ??
 | 
					    details: reader.readStringOrNull(offsets[3]),
 | 
				
			||||||
 | 
					    level: _LoggerMessagelevelValueEnumMap[reader.readByteOrNull(offsets[4])] ??
 | 
				
			||||||
        LogLevel.ALL,
 | 
					        LogLevel.ALL,
 | 
				
			||||||
    message: reader.readString(offsets[4]),
 | 
					    message: reader.readString(offsets[5]),
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
  object.id = id;
 | 
					  object.id = id;
 | 
				
			||||||
  return object;
 | 
					  return object;
 | 
				
			||||||
@ -125,9 +138,11 @@ P _loggerMessageDeserializeProp<P>(
 | 
				
			|||||||
    case 2:
 | 
					    case 2:
 | 
				
			||||||
      return (reader.readDateTime(offset)) as P;
 | 
					      return (reader.readDateTime(offset)) as P;
 | 
				
			||||||
    case 3:
 | 
					    case 3:
 | 
				
			||||||
 | 
					      return (reader.readStringOrNull(offset)) as P;
 | 
				
			||||||
 | 
					    case 4:
 | 
				
			||||||
      return (_LoggerMessagelevelValueEnumMap[reader.readByteOrNull(offset)] ??
 | 
					      return (_LoggerMessagelevelValueEnumMap[reader.readByteOrNull(offset)] ??
 | 
				
			||||||
          LogLevel.ALL) as P;
 | 
					          LogLevel.ALL) as P;
 | 
				
			||||||
    case 4:
 | 
					    case 5:
 | 
				
			||||||
      return (reader.readString(offset)) as P;
 | 
					      return (reader.readString(offset)) as P;
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      throw IsarError('Unknown property with id $propertyId');
 | 
					      throw IsarError('Unknown property with id $propertyId');
 | 
				
			||||||
@ -619,6 +634,160 @@ extension LoggerMessageQueryFilter
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  QueryBuilder<LoggerMessage, LoggerMessage, QAfterFilterCondition>
 | 
				
			||||||
 | 
					      detailsIsNull() {
 | 
				
			||||||
 | 
					    return QueryBuilder.apply(this, (query) {
 | 
				
			||||||
 | 
					      return query.addFilterCondition(const FilterCondition.isNull(
 | 
				
			||||||
 | 
					        property: r'details',
 | 
				
			||||||
 | 
					      ));
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  QueryBuilder<LoggerMessage, LoggerMessage, QAfterFilterCondition>
 | 
				
			||||||
 | 
					      detailsIsNotNull() {
 | 
				
			||||||
 | 
					    return QueryBuilder.apply(this, (query) {
 | 
				
			||||||
 | 
					      return query.addFilterCondition(const FilterCondition.isNotNull(
 | 
				
			||||||
 | 
					        property: r'details',
 | 
				
			||||||
 | 
					      ));
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  QueryBuilder<LoggerMessage, LoggerMessage, QAfterFilterCondition>
 | 
				
			||||||
 | 
					      detailsEqualTo(
 | 
				
			||||||
 | 
					    String? value, {
 | 
				
			||||||
 | 
					    bool caseSensitive = true,
 | 
				
			||||||
 | 
					  }) {
 | 
				
			||||||
 | 
					    return QueryBuilder.apply(this, (query) {
 | 
				
			||||||
 | 
					      return query.addFilterCondition(FilterCondition.equalTo(
 | 
				
			||||||
 | 
					        property: r'details',
 | 
				
			||||||
 | 
					        value: value,
 | 
				
			||||||
 | 
					        caseSensitive: caseSensitive,
 | 
				
			||||||
 | 
					      ));
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  QueryBuilder<LoggerMessage, LoggerMessage, QAfterFilterCondition>
 | 
				
			||||||
 | 
					      detailsGreaterThan(
 | 
				
			||||||
 | 
					    String? value, {
 | 
				
			||||||
 | 
					    bool include = false,
 | 
				
			||||||
 | 
					    bool caseSensitive = true,
 | 
				
			||||||
 | 
					  }) {
 | 
				
			||||||
 | 
					    return QueryBuilder.apply(this, (query) {
 | 
				
			||||||
 | 
					      return query.addFilterCondition(FilterCondition.greaterThan(
 | 
				
			||||||
 | 
					        include: include,
 | 
				
			||||||
 | 
					        property: r'details',
 | 
				
			||||||
 | 
					        value: value,
 | 
				
			||||||
 | 
					        caseSensitive: caseSensitive,
 | 
				
			||||||
 | 
					      ));
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  QueryBuilder<LoggerMessage, LoggerMessage, QAfterFilterCondition>
 | 
				
			||||||
 | 
					      detailsLessThan(
 | 
				
			||||||
 | 
					    String? value, {
 | 
				
			||||||
 | 
					    bool include = false,
 | 
				
			||||||
 | 
					    bool caseSensitive = true,
 | 
				
			||||||
 | 
					  }) {
 | 
				
			||||||
 | 
					    return QueryBuilder.apply(this, (query) {
 | 
				
			||||||
 | 
					      return query.addFilterCondition(FilterCondition.lessThan(
 | 
				
			||||||
 | 
					        include: include,
 | 
				
			||||||
 | 
					        property: r'details',
 | 
				
			||||||
 | 
					        value: value,
 | 
				
			||||||
 | 
					        caseSensitive: caseSensitive,
 | 
				
			||||||
 | 
					      ));
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  QueryBuilder<LoggerMessage, LoggerMessage, QAfterFilterCondition>
 | 
				
			||||||
 | 
					      detailsBetween(
 | 
				
			||||||
 | 
					    String? lower,
 | 
				
			||||||
 | 
					    String? upper, {
 | 
				
			||||||
 | 
					    bool includeLower = true,
 | 
				
			||||||
 | 
					    bool includeUpper = true,
 | 
				
			||||||
 | 
					    bool caseSensitive = true,
 | 
				
			||||||
 | 
					  }) {
 | 
				
			||||||
 | 
					    return QueryBuilder.apply(this, (query) {
 | 
				
			||||||
 | 
					      return query.addFilterCondition(FilterCondition.between(
 | 
				
			||||||
 | 
					        property: r'details',
 | 
				
			||||||
 | 
					        lower: lower,
 | 
				
			||||||
 | 
					        includeLower: includeLower,
 | 
				
			||||||
 | 
					        upper: upper,
 | 
				
			||||||
 | 
					        includeUpper: includeUpper,
 | 
				
			||||||
 | 
					        caseSensitive: caseSensitive,
 | 
				
			||||||
 | 
					      ));
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  QueryBuilder<LoggerMessage, LoggerMessage, QAfterFilterCondition>
 | 
				
			||||||
 | 
					      detailsStartsWith(
 | 
				
			||||||
 | 
					    String value, {
 | 
				
			||||||
 | 
					    bool caseSensitive = true,
 | 
				
			||||||
 | 
					  }) {
 | 
				
			||||||
 | 
					    return QueryBuilder.apply(this, (query) {
 | 
				
			||||||
 | 
					      return query.addFilterCondition(FilterCondition.startsWith(
 | 
				
			||||||
 | 
					        property: r'details',
 | 
				
			||||||
 | 
					        value: value,
 | 
				
			||||||
 | 
					        caseSensitive: caseSensitive,
 | 
				
			||||||
 | 
					      ));
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  QueryBuilder<LoggerMessage, LoggerMessage, QAfterFilterCondition>
 | 
				
			||||||
 | 
					      detailsEndsWith(
 | 
				
			||||||
 | 
					    String value, {
 | 
				
			||||||
 | 
					    bool caseSensitive = true,
 | 
				
			||||||
 | 
					  }) {
 | 
				
			||||||
 | 
					    return QueryBuilder.apply(this, (query) {
 | 
				
			||||||
 | 
					      return query.addFilterCondition(FilterCondition.endsWith(
 | 
				
			||||||
 | 
					        property: r'details',
 | 
				
			||||||
 | 
					        value: value,
 | 
				
			||||||
 | 
					        caseSensitive: caseSensitive,
 | 
				
			||||||
 | 
					      ));
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  QueryBuilder<LoggerMessage, LoggerMessage, QAfterFilterCondition>
 | 
				
			||||||
 | 
					      detailsContains(String value, {bool caseSensitive = true}) {
 | 
				
			||||||
 | 
					    return QueryBuilder.apply(this, (query) {
 | 
				
			||||||
 | 
					      return query.addFilterCondition(FilterCondition.contains(
 | 
				
			||||||
 | 
					        property: r'details',
 | 
				
			||||||
 | 
					        value: value,
 | 
				
			||||||
 | 
					        caseSensitive: caseSensitive,
 | 
				
			||||||
 | 
					      ));
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  QueryBuilder<LoggerMessage, LoggerMessage, QAfterFilterCondition>
 | 
				
			||||||
 | 
					      detailsMatches(String pattern, {bool caseSensitive = true}) {
 | 
				
			||||||
 | 
					    return QueryBuilder.apply(this, (query) {
 | 
				
			||||||
 | 
					      return query.addFilterCondition(FilterCondition.matches(
 | 
				
			||||||
 | 
					        property: r'details',
 | 
				
			||||||
 | 
					        wildcard: pattern,
 | 
				
			||||||
 | 
					        caseSensitive: caseSensitive,
 | 
				
			||||||
 | 
					      ));
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  QueryBuilder<LoggerMessage, LoggerMessage, QAfterFilterCondition>
 | 
				
			||||||
 | 
					      detailsIsEmpty() {
 | 
				
			||||||
 | 
					    return QueryBuilder.apply(this, (query) {
 | 
				
			||||||
 | 
					      return query.addFilterCondition(FilterCondition.equalTo(
 | 
				
			||||||
 | 
					        property: r'details',
 | 
				
			||||||
 | 
					        value: '',
 | 
				
			||||||
 | 
					      ));
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  QueryBuilder<LoggerMessage, LoggerMessage, QAfterFilterCondition>
 | 
				
			||||||
 | 
					      detailsIsNotEmpty() {
 | 
				
			||||||
 | 
					    return QueryBuilder.apply(this, (query) {
 | 
				
			||||||
 | 
					      return query.addFilterCondition(FilterCondition.greaterThan(
 | 
				
			||||||
 | 
					        property: r'details',
 | 
				
			||||||
 | 
					        value: '',
 | 
				
			||||||
 | 
					      ));
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  QueryBuilder<LoggerMessage, LoggerMessage, QAfterFilterCondition> idEqualTo(
 | 
					  QueryBuilder<LoggerMessage, LoggerMessage, QAfterFilterCondition> idEqualTo(
 | 
				
			||||||
      Id value) {
 | 
					      Id value) {
 | 
				
			||||||
    return QueryBuilder.apply(this, (query) {
 | 
					    return QueryBuilder.apply(this, (query) {
 | 
				
			||||||
@ -913,6 +1082,18 @@ extension LoggerMessageQuerySortBy
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  QueryBuilder<LoggerMessage, LoggerMessage, QAfterSortBy> sortByDetails() {
 | 
				
			||||||
 | 
					    return QueryBuilder.apply(this, (query) {
 | 
				
			||||||
 | 
					      return query.addSortBy(r'details', Sort.asc);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  QueryBuilder<LoggerMessage, LoggerMessage, QAfterSortBy> sortByDetailsDesc() {
 | 
				
			||||||
 | 
					    return QueryBuilder.apply(this, (query) {
 | 
				
			||||||
 | 
					      return query.addSortBy(r'details', Sort.desc);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  QueryBuilder<LoggerMessage, LoggerMessage, QAfterSortBy> sortByLevel() {
 | 
					  QueryBuilder<LoggerMessage, LoggerMessage, QAfterSortBy> sortByLevel() {
 | 
				
			||||||
    return QueryBuilder.apply(this, (query) {
 | 
					    return QueryBuilder.apply(this, (query) {
 | 
				
			||||||
      return query.addSortBy(r'level', Sort.asc);
 | 
					      return query.addSortBy(r'level', Sort.asc);
 | 
				
			||||||
@ -979,6 +1160,18 @@ extension LoggerMessageQuerySortThenBy
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  QueryBuilder<LoggerMessage, LoggerMessage, QAfterSortBy> thenByDetails() {
 | 
				
			||||||
 | 
					    return QueryBuilder.apply(this, (query) {
 | 
				
			||||||
 | 
					      return query.addSortBy(r'details', Sort.asc);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  QueryBuilder<LoggerMessage, LoggerMessage, QAfterSortBy> thenByDetailsDesc() {
 | 
				
			||||||
 | 
					    return QueryBuilder.apply(this, (query) {
 | 
				
			||||||
 | 
					      return query.addSortBy(r'details', Sort.desc);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  QueryBuilder<LoggerMessage, LoggerMessage, QAfterSortBy> thenById() {
 | 
					  QueryBuilder<LoggerMessage, LoggerMessage, QAfterSortBy> thenById() {
 | 
				
			||||||
    return QueryBuilder.apply(this, (query) {
 | 
					    return QueryBuilder.apply(this, (query) {
 | 
				
			||||||
      return query.addSortBy(r'id', Sort.asc);
 | 
					      return query.addSortBy(r'id', Sort.asc);
 | 
				
			||||||
@ -1038,6 +1231,13 @@ extension LoggerMessageQueryWhereDistinct
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  QueryBuilder<LoggerMessage, LoggerMessage, QDistinct> distinctByDetails(
 | 
				
			||||||
 | 
					      {bool caseSensitive = true}) {
 | 
				
			||||||
 | 
					    return QueryBuilder.apply(this, (query) {
 | 
				
			||||||
 | 
					      return query.addDistinctBy(r'details', caseSensitive: caseSensitive);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  QueryBuilder<LoggerMessage, LoggerMessage, QDistinct> distinctByLevel() {
 | 
					  QueryBuilder<LoggerMessage, LoggerMessage, QDistinct> distinctByLevel() {
 | 
				
			||||||
    return QueryBuilder.apply(this, (query) {
 | 
					    return QueryBuilder.apply(this, (query) {
 | 
				
			||||||
      return query.addDistinctBy(r'level');
 | 
					      return query.addDistinctBy(r'level');
 | 
				
			||||||
@ -1078,6 +1278,12 @@ extension LoggerMessageQueryProperty
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  QueryBuilder<LoggerMessage, String?, QQueryOperations> detailsProperty() {
 | 
				
			||||||
 | 
					    return QueryBuilder.apply(this, (query) {
 | 
				
			||||||
 | 
					      return query.addPropertyName(r'details');
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  QueryBuilder<LoggerMessage, LogLevel, QQueryOperations> levelProperty() {
 | 
					  QueryBuilder<LoggerMessage, LogLevel, QQueryOperations> levelProperty() {
 | 
				
			||||||
    return QueryBuilder.apply(this, (query) {
 | 
					    return QueryBuilder.apply(this, (query) {
 | 
				
			||||||
      return query.addPropertyName(r'level');
 | 
					      return query.addPropertyName(r'level');
 | 
				
			||||||
 | 
				
			|||||||
@ -90,7 +90,7 @@ class AssetService {
 | 
				
			|||||||
      return allAssets;
 | 
					      return allAssets;
 | 
				
			||||||
    } catch (error, stack) {
 | 
					    } catch (error, stack) {
 | 
				
			||||||
      log.severe(
 | 
					      log.severe(
 | 
				
			||||||
        'Error while getting remote assets: ${error.toString()}',
 | 
					        'Error while getting remote assets',
 | 
				
			||||||
        error,
 | 
					        error,
 | 
				
			||||||
        stack,
 | 
					        stack,
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
@ -117,7 +117,7 @@ class AssetService {
 | 
				
			|||||||
      );
 | 
					      );
 | 
				
			||||||
      return true;
 | 
					      return true;
 | 
				
			||||||
    } catch (error, stack) {
 | 
					    } catch (error, stack) {
 | 
				
			||||||
      log.severe("Error deleteAssets  ${error.toString()}", error, stack);
 | 
					      log.severe("Error while deleting assets", error, stack);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -12,7 +12,7 @@ import 'package:share_plus/share_plus.dart';
 | 
				
			|||||||
/// [ImmichLogger] is a custom logger that is built on top of the [logging] package.
 | 
					/// [ImmichLogger] is a custom logger that is built on top of the [logging] package.
 | 
				
			||||||
/// The logs are written to the database and onto console, using `debugPrint` method.
 | 
					/// The logs are written to the database and onto console, using `debugPrint` method.
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// The logs are deleted when exceeding the `maxLogEntries` (default 200) property
 | 
					/// The logs are deleted when exceeding the `maxLogEntries` (default 500) property
 | 
				
			||||||
/// in the class.
 | 
					/// in the class.
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// Logs can be shared by calling the `shareLogs` method, which will open a share dialog
 | 
					/// Logs can be shared by calling the `shareLogs` method, which will open a share dialog
 | 
				
			||||||
@ -58,6 +58,7 @@ class ImmichLogger {
 | 
				
			|||||||
    debugPrint('[${record.level.name}] [${record.time}] ${record.message}');
 | 
					    debugPrint('[${record.level.name}] [${record.time}] ${record.message}');
 | 
				
			||||||
    final lm = LoggerMessage(
 | 
					    final lm = LoggerMessage(
 | 
				
			||||||
      message: record.message,
 | 
					      message: record.message,
 | 
				
			||||||
 | 
					      details: record.error?.toString(),
 | 
				
			||||||
      level: record.level.toLogLevel(),
 | 
					      level: record.level.toLogLevel(),
 | 
				
			||||||
      createdAt: record.time,
 | 
					      createdAt: record.time,
 | 
				
			||||||
      context1: record.loggerName,
 | 
					      context1: record.loggerName,
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,7 @@ import 'dart:io';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
					import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
				
			||||||
 | 
					import 'package:immich_mobile/extensions/response_extensions.dart';
 | 
				
			||||||
import 'package:immich_mobile/shared/models/asset.dart';
 | 
					import 'package:immich_mobile/shared/models/asset.dart';
 | 
				
			||||||
import 'package:immich_mobile/shared/providers/api.provider.dart';
 | 
					import 'package:immich_mobile/shared/providers/api.provider.dart';
 | 
				
			||||||
import 'package:logging/logging.dart';
 | 
					import 'package:logging/logging.dart';
 | 
				
			||||||
@ -41,7 +42,8 @@ class ShareService {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
          if (res.statusCode != 200) {
 | 
					          if (res.statusCode != 200) {
 | 
				
			||||||
            _log.severe(
 | 
					            _log.severe(
 | 
				
			||||||
              "Asset download failed with status - ${res.statusCode} and response - ${res.body}",
 | 
					              "Asset download for ${asset.fileName} failed",
 | 
				
			||||||
 | 
					              res.toLoggerString(),
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
            continue;
 | 
					            continue;
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
@ -68,7 +70,7 @@ class ShareService {
 | 
				
			|||||||
      );
 | 
					      );
 | 
				
			||||||
      return true;
 | 
					      return true;
 | 
				
			||||||
    } catch (error) {
 | 
					    } catch (error) {
 | 
				
			||||||
      _log.severe("Share failed with error $error");
 | 
					      _log.severe("Share failed", error);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -140,7 +140,7 @@ class SyncService {
 | 
				
			|||||||
    try {
 | 
					    try {
 | 
				
			||||||
      await _db.writeTxn(() => a.put(_db));
 | 
					      await _db.writeTxn(() => a.put(_db));
 | 
				
			||||||
    } on IsarError catch (e) {
 | 
					    } on IsarError catch (e) {
 | 
				
			||||||
      _log.severe("Failed to put new asset into db: $e");
 | 
					      _log.severe("Failed to put new asset into db", e);
 | 
				
			||||||
      return false;
 | 
					      return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return true;
 | 
					    return true;
 | 
				
			||||||
@ -173,7 +173,7 @@ class SyncService {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      return false;
 | 
					      return false;
 | 
				
			||||||
    } on IsarError catch (e) {
 | 
					    } on IsarError catch (e) {
 | 
				
			||||||
      _log.severe("Failed to sync remote assets to db: $e");
 | 
					      _log.severe("Failed to sync remote assets to db", e);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null;
 | 
					    return null;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -232,7 +232,7 @@ class SyncService {
 | 
				
			|||||||
      await _db.writeTxn(() => _db.assets.deleteAll(idsToDelete));
 | 
					      await _db.writeTxn(() => _db.assets.deleteAll(idsToDelete));
 | 
				
			||||||
      await upsertAssetsWithExif(toAdd + toUpdate);
 | 
					      await upsertAssetsWithExif(toAdd + toUpdate);
 | 
				
			||||||
    } on IsarError catch (e) {
 | 
					    } on IsarError catch (e) {
 | 
				
			||||||
      _log.severe("Failed to sync remote assets to db: $e");
 | 
					      _log.severe("Failed to sync remote assets to db", e);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    await _updateUserAssetsETag(user, now);
 | 
					    await _updateUserAssetsETag(user, now);
 | 
				
			||||||
    return true;
 | 
					    return true;
 | 
				
			||||||
@ -364,7 +364,7 @@ class SyncService {
 | 
				
			|||||||
      });
 | 
					      });
 | 
				
			||||||
      _log.info("Synced changes of remote album ${album.name} to DB");
 | 
					      _log.info("Synced changes of remote album ${album.name} to DB");
 | 
				
			||||||
    } on IsarError catch (e) {
 | 
					    } on IsarError catch (e) {
 | 
				
			||||||
      _log.severe("Failed to sync remote album to database $e");
 | 
					      _log.severe("Failed to sync remote album to database", e);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (album.shared || dto.shared) {
 | 
					    if (album.shared || dto.shared) {
 | 
				
			||||||
@ -441,7 +441,7 @@ class SyncService {
 | 
				
			|||||||
      assert(ok);
 | 
					      assert(ok);
 | 
				
			||||||
      _log.info("Removed local album $album from DB");
 | 
					      _log.info("Removed local album $album from DB");
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      _log.severe("Failed to remove local album $album from DB");
 | 
					      _log.severe("Failed to remove local album $album from DB", e);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -577,7 +577,7 @@ class SyncService {
 | 
				
			|||||||
      });
 | 
					      });
 | 
				
			||||||
      _log.info("Synced changes of local album ${ape.name} to DB");
 | 
					      _log.info("Synced changes of local album ${ape.name} to DB");
 | 
				
			||||||
    } on IsarError catch (e) {
 | 
					    } on IsarError catch (e) {
 | 
				
			||||||
      _log.severe("Failed to update synced album ${ape.name} in DB: $e");
 | 
					      _log.severe("Failed to update synced album ${ape.name} in DB", e);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return true;
 | 
					    return true;
 | 
				
			||||||
@ -623,7 +623,7 @@ class SyncService {
 | 
				
			|||||||
      });
 | 
					      });
 | 
				
			||||||
      _log.info("Fast synced local album ${ape.name} to DB");
 | 
					      _log.info("Fast synced local album ${ape.name} to DB");
 | 
				
			||||||
    } on IsarError catch (e) {
 | 
					    } on IsarError catch (e) {
 | 
				
			||||||
      _log.severe("Failed to fast sync local album ${ape.name} to DB: $e");
 | 
					      _log.severe("Failed to fast sync local album ${ape.name} to DB", e);
 | 
				
			||||||
      return false;
 | 
					      return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -656,7 +656,7 @@ class SyncService {
 | 
				
			|||||||
      await _db.writeTxn(() => _db.albums.store(a));
 | 
					      await _db.writeTxn(() => _db.albums.store(a));
 | 
				
			||||||
      _log.info("Added a new local album to DB: ${ape.name}");
 | 
					      _log.info("Added a new local album to DB: ${ape.name}");
 | 
				
			||||||
    } on IsarError catch (e) {
 | 
					    } on IsarError catch (e) {
 | 
				
			||||||
      _log.severe("Failed to add new local album ${ape.name} to DB: $e");
 | 
					      _log.severe("Failed to add new local album ${ape.name} to DB", e);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -706,9 +706,7 @@ class SyncService {
 | 
				
			|||||||
      });
 | 
					      });
 | 
				
			||||||
      _log.info("Upserted ${assets.length} assets into the DB");
 | 
					      _log.info("Upserted ${assets.length} assets into the DB");
 | 
				
			||||||
    } on IsarError catch (e) {
 | 
					    } on IsarError catch (e) {
 | 
				
			||||||
      _log.severe(
 | 
					      _log.severe("Failed to upsert ${assets.length} assets into the DB", e);
 | 
				
			||||||
        "Failed to upsert ${assets.length} assets into the DB: ${e.toString()}",
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
      // give details on the errors
 | 
					      // give details on the errors
 | 
				
			||||||
      assets.sort(Asset.compareByOwnerChecksum);
 | 
					      assets.sort(Asset.compareByOwnerChecksum);
 | 
				
			||||||
      final inDb = await _db.assets.getAllByOwnerIdChecksum(
 | 
					      final inDb = await _db.assets.getAllByOwnerIdChecksum(
 | 
				
			||||||
@ -776,7 +774,7 @@ class SyncService {
 | 
				
			|||||||
      });
 | 
					      });
 | 
				
			||||||
      return true;
 | 
					      return true;
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      _log.severe("Failed to remove all local albums and assets: $e");
 | 
					      _log.severe("Failed to remove all local albums and assets", e);
 | 
				
			||||||
      return false;
 | 
					      return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -42,7 +42,7 @@ class UserService {
 | 
				
			|||||||
      final dto = await _apiService.userApi.getAllUsers(isAll);
 | 
					      final dto = await _apiService.userApi.getAllUsers(isAll);
 | 
				
			||||||
      return dto?.map(User.fromUserDto).toList();
 | 
					      return dto?.map(User.fromUserDto).toList();
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      _log.warning("Failed get all users:\n$e");
 | 
					      _log.warning("Failed get all users", e);
 | 
				
			||||||
      return null;
 | 
					      return null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -65,7 +65,7 @@ class UserService {
 | 
				
			|||||||
        ),
 | 
					        ),
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      _log.warning("Failed to upload profile image:\n$e");
 | 
					      _log.warning("Failed to upload profile image", e);
 | 
				
			||||||
      return null;
 | 
					      return null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -15,7 +15,7 @@ class AppLogDetailPage extends HookConsumerWidget {
 | 
				
			|||||||
  Widget build(BuildContext context, WidgetRef ref) {
 | 
					  Widget build(BuildContext context, WidgetRef ref) {
 | 
				
			||||||
    var isDarkTheme = context.isDarkTheme;
 | 
					    var isDarkTheme = context.isDarkTheme;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    buildStackMessage(String stackTrace) {
 | 
					    buildTextWithCopyButton(String header, String text) {
 | 
				
			||||||
      return Padding(
 | 
					      return Padding(
 | 
				
			||||||
        padding: const EdgeInsets.all(8.0),
 | 
					        padding: const EdgeInsets.all(8.0),
 | 
				
			||||||
        child: Column(
 | 
					        child: Column(
 | 
				
			||||||
@ -28,7 +28,7 @@ class AppLogDetailPage extends HookConsumerWidget {
 | 
				
			|||||||
                Padding(
 | 
					                Padding(
 | 
				
			||||||
                  padding: const EdgeInsets.only(bottom: 8.0),
 | 
					                  padding: const EdgeInsets.only(bottom: 8.0),
 | 
				
			||||||
                  child: Text(
 | 
					                  child: Text(
 | 
				
			||||||
                    "STACK TRACES",
 | 
					                    header,
 | 
				
			||||||
                    style: TextStyle(
 | 
					                    style: TextStyle(
 | 
				
			||||||
                      fontSize: 12.0,
 | 
					                      fontSize: 12.0,
 | 
				
			||||||
                      color: context.primaryColor,
 | 
					                      color: context.primaryColor,
 | 
				
			||||||
@ -38,8 +38,7 @@ class AppLogDetailPage extends HookConsumerWidget {
 | 
				
			|||||||
                ),
 | 
					                ),
 | 
				
			||||||
                IconButton(
 | 
					                IconButton(
 | 
				
			||||||
                  onPressed: () {
 | 
					                  onPressed: () {
 | 
				
			||||||
                    Clipboard.setData(ClipboardData(text: stackTrace))
 | 
					                    Clipboard.setData(ClipboardData(text: text)).then((_) {
 | 
				
			||||||
                        .then((_) {
 | 
					 | 
				
			||||||
                      ScaffoldMessenger.of(context).showSnackBar(
 | 
					                      ScaffoldMessenger.of(context).showSnackBar(
 | 
				
			||||||
                        SnackBar(
 | 
					                        SnackBar(
 | 
				
			||||||
                          content: Text(
 | 
					                          content: Text(
 | 
				
			||||||
@ -68,73 +67,7 @@ class AppLogDetailPage extends HookConsumerWidget {
 | 
				
			|||||||
              child: Padding(
 | 
					              child: Padding(
 | 
				
			||||||
                padding: const EdgeInsets.all(8.0),
 | 
					                padding: const EdgeInsets.all(8.0),
 | 
				
			||||||
                child: SelectableText(
 | 
					                child: SelectableText(
 | 
				
			||||||
                  stackTrace,
 | 
					                  text,
 | 
				
			||||||
                  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: context.primaryColor,
 | 
					 | 
				
			||||||
                      fontWeight: FontWeight.bold,
 | 
					 | 
				
			||||||
                    ),
 | 
					 | 
				
			||||||
                  ),
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
                IconButton(
 | 
					 | 
				
			||||||
                  onPressed: () {
 | 
					 | 
				
			||||||
                    Clipboard.setData(ClipboardData(text: message)).then((_) {
 | 
					 | 
				
			||||||
                      ScaffoldMessenger.of(context).showSnackBar(
 | 
					 | 
				
			||||||
                        SnackBar(
 | 
					 | 
				
			||||||
                          content: Text(
 | 
					 | 
				
			||||||
                            "Copied to clipboard",
 | 
					 | 
				
			||||||
                            style: context.textTheme.bodyLarge?.copyWith(
 | 
					 | 
				
			||||||
                              color: context.primaryColor,
 | 
					 | 
				
			||||||
                            ),
 | 
					 | 
				
			||||||
                          ),
 | 
					 | 
				
			||||||
                        ),
 | 
					 | 
				
			||||||
                      );
 | 
					 | 
				
			||||||
                    });
 | 
					 | 
				
			||||||
                  },
 | 
					 | 
				
			||||||
                  icon: Icon(
 | 
					 | 
				
			||||||
                    Icons.copy,
 | 
					 | 
				
			||||||
                    size: 16.0,
 | 
					 | 
				
			||||||
                    color: context.primaryColor,
 | 
					 | 
				
			||||||
                  ),
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
              ],
 | 
					 | 
				
			||||||
            ),
 | 
					 | 
				
			||||||
            Container(
 | 
					 | 
				
			||||||
              decoration: BoxDecoration(
 | 
					 | 
				
			||||||
                color: isDarkTheme ? 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(
 | 
					                  style: const TextStyle(
 | 
				
			||||||
                    fontSize: 12.0,
 | 
					                    fontSize: 12.0,
 | 
				
			||||||
                    fontWeight: FontWeight.bold,
 | 
					                    fontWeight: FontWeight.bold,
 | 
				
			||||||
@ -194,11 +127,16 @@ class AppLogDetailPage extends HookConsumerWidget {
 | 
				
			|||||||
      body: SafeArea(
 | 
					      body: SafeArea(
 | 
				
			||||||
        child: ListView(
 | 
					        child: ListView(
 | 
				
			||||||
          children: [
 | 
					          children: [
 | 
				
			||||||
            buildLogMessage(logMessage.message),
 | 
					            buildTextWithCopyButton("MESSAGE", logMessage.message),
 | 
				
			||||||
 | 
					            if (logMessage.details != null)
 | 
				
			||||||
 | 
					              buildTextWithCopyButton("DETAILS", logMessage.details.toString()),
 | 
				
			||||||
            if (logMessage.context1 != null)
 | 
					            if (logMessage.context1 != null)
 | 
				
			||||||
              buildLogContext1(logMessage.context1.toString()),
 | 
					              buildLogContext1(logMessage.context1.toString()),
 | 
				
			||||||
            if (logMessage.context2 != null)
 | 
					            if (logMessage.context2 != null)
 | 
				
			||||||
              buildStackMessage(logMessage.context2.toString()),
 | 
					              buildTextWithCopyButton(
 | 
				
			||||||
 | 
					                "STACK TRACE",
 | 
				
			||||||
 | 
					                logMessage.context2.toString(),
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
          ],
 | 
					          ],
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
      ),
 | 
					      ),
 | 
				
			||||||
 | 
				
			|||||||
@ -35,10 +35,10 @@ class SplashScreenPage extends HookConsumerWidget {
 | 
				
			|||||||
            deviceIsOffline = true;
 | 
					            deviceIsOffline = true;
 | 
				
			||||||
            log.fine("Device seems to be offline upon launch");
 | 
					            log.fine("Device seems to be offline upon launch");
 | 
				
			||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
            log.severe(e);
 | 
					            log.severe("Failed to resolve endpoint", e);
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        } catch (e) {
 | 
					        } catch (e) {
 | 
				
			||||||
          log.severe(e);
 | 
					          log.severe("Failed to resolve endpoint", e);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
@ -53,7 +53,7 @@ class SplashScreenPage extends HookConsumerWidget {
 | 
				
			|||||||
          ref.read(authenticationProvider.notifier).logout();
 | 
					          ref.read(authenticationProvider.notifier).logout();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          log.severe(
 | 
					          log.severe(
 | 
				
			||||||
            'Cannot set success login info: $error',
 | 
					            'Cannot set success login info',
 | 
				
			||||||
            error,
 | 
					            error,
 | 
				
			||||||
            stackTrace,
 | 
					            stackTrace,
 | 
				
			||||||
          );
 | 
					          );
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user