mirror of
https://github.com/immich-app/immich.git
synced 2025-07-09 03:04:16 -04:00
app bar and log details page
This commit is contained in:
parent
a0afea04d8
commit
5385d43c8c
@ -38,9 +38,17 @@
|
||||
},
|
||||
"logs": {
|
||||
"title": "Logs",
|
||||
"no_logs": "No logs available"
|
||||
"no_logs": "No logs available",
|
||||
"detail": {
|
||||
"title": "Log Detail",
|
||||
"message_heading": "MESSAGE",
|
||||
"context_heading": "CONTEXT",
|
||||
"error_heading": "ERROR",
|
||||
"stack_heading": "STACK TRACE"
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"loading": "Loading"
|
||||
"loading": "Loading",
|
||||
"copied_long": "Copied to clipboard"
|
||||
}
|
||||
}
|
@ -1,5 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:immich_mobile/i18n/strings.g.dart';
|
||||
import 'package:immich_mobile/presentation/components/common/gap.widget.dart';
|
||||
import 'package:immich_mobile/presentation/components/common/user_avatar.widget.dart';
|
||||
import 'package:immich_mobile/presentation/components/image/immich_logo.widget.dart';
|
||||
import 'package:immich_mobile/presentation/states/current_user.state.dart';
|
||||
import 'package:immich_mobile/service_locator.dart';
|
||||
import 'package:immich_mobile/utils/constants/size_constants.dart';
|
||||
import 'package:immich_mobile/utils/extensions/build_context.extension.dart';
|
||||
|
||||
class ImAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||
@ -11,10 +16,26 @@ class ImAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppBar(
|
||||
backgroundColor: context.theme.appBarTheme.backgroundColor,
|
||||
automaticallyImplyLeading: false,
|
||||
title: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
ImLogo(dimension: SizeConstants.xm),
|
||||
SizedGap.sw(),
|
||||
ImLogoText(fontSize: 20),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: SizeConstants.m),
|
||||
child: ImUserAvatar(
|
||||
user: di<CurrentUserProvider>().value,
|
||||
radius: SizeConstants.m,
|
||||
),
|
||||
),
|
||||
],
|
||||
backgroundColor: context.theme.appBarTheme.backgroundColor,
|
||||
centerTitle: false,
|
||||
title: Text(context.t.immich),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -65,6 +65,8 @@ class _ImAssetGridState extends State<ImAssetGrid> {
|
||||
BlocBuilder<AssetGridCubit, AssetGridState>(
|
||||
builder: (_, state) {
|
||||
final elements = state.renderList.elements;
|
||||
|
||||
// Append padding if required
|
||||
if (widget.topPadding != null &&
|
||||
elements.firstOrNull is! RenderListPaddingElement) {
|
||||
elements.insert(
|
||||
@ -74,6 +76,9 @@ class _ImAssetGridState extends State<ImAssetGrid> {
|
||||
before: elements.firstOrNull,
|
||||
),
|
||||
);
|
||||
} else if (widget.topPadding == null &&
|
||||
elements.firstOrNull is RenderListPaddingElement) {
|
||||
elements.removeAt(0);
|
||||
}
|
||||
|
||||
final grid = FlutterListView(
|
||||
|
@ -0,0 +1,128 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:immich_mobile/domain/models/log.model.dart';
|
||||
import 'package:immich_mobile/i18n/strings.g.dart';
|
||||
import 'package:immich_mobile/presentation/components/scaffold/adaptive_route_appbar.widget.dart';
|
||||
import 'package:immich_mobile/presentation/theme/app_typography.dart';
|
||||
import 'package:immich_mobile/utils/extensions/build_context.extension.dart';
|
||||
import 'package:immich_mobile/utils/snackbar_manager.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
|
||||
@RoutePage()
|
||||
class LogDetailsPage extends StatelessWidget {
|
||||
final LogMessage log;
|
||||
|
||||
const LogDetailsPage({super.key, required this.log});
|
||||
|
||||
String _getClipboardText() {
|
||||
return """
|
||||
Message: ${log.content}
|
||||
Logged at: ${log.createdAt}
|
||||
Context: ${log.logger ?? "<NA>"}
|
||||
Error: ${log.error ?? "<NA>"}
|
||||
Stack:
|
||||
------
|
||||
${log.stack ?? "<NA>"}
|
||||
""";
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: ImAdaptiveRouteAppBar(
|
||||
title: context.t.logs.detail.title,
|
||||
isPrimary: false,
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () => unawaited(
|
||||
Clipboard.setData(ClipboardData(text: _getClipboardText()))
|
||||
.then((_) {
|
||||
if (context.mounted) {
|
||||
SnackbarManager.showText(
|
||||
content: context.t.common.copied_long,
|
||||
);
|
||||
}
|
||||
}),
|
||||
),
|
||||
icon: Icon(
|
||||
Symbols.copy_all_rounded,
|
||||
color: context.colorScheme.primary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: SafeArea(
|
||||
child: ListView(
|
||||
children: [
|
||||
_TextWithCopy(
|
||||
heading: context.t.logs.detail.message_heading,
|
||||
text: log.content,
|
||||
),
|
||||
Divider(),
|
||||
if (log.logger != null) ...<Widget>[
|
||||
_TextWithCopy(
|
||||
heading: context.t.logs.detail.context_heading,
|
||||
text: log.logger!.toString(),
|
||||
),
|
||||
Divider(),
|
||||
],
|
||||
if (log.error != null) ...<Widget>[
|
||||
_TextWithCopy(
|
||||
heading: context.t.logs.detail.error_heading,
|
||||
text: log.error!.toString(),
|
||||
),
|
||||
Divider(),
|
||||
],
|
||||
if (log.stack != null) ...<Widget>[
|
||||
_TextWithCopy(
|
||||
heading: context.t.logs.detail.stack_heading,
|
||||
text: log.stack!.toString(),
|
||||
),
|
||||
Divider(),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _TextWithCopy extends StatelessWidget {
|
||||
final String heading;
|
||||
final String text;
|
||||
|
||||
const _TextWithCopy({required this.heading, required this.text});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 16.0, top: 16.0, bottom: 10.0),
|
||||
child: Text(
|
||||
heading,
|
||||
style: AppTypography.bodyMedium.copyWith(
|
||||
color: context.colorScheme.primary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 16.0, bottom: 10.0),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: SelectableText(
|
||||
text,
|
||||
style: AppTypography.bodySmall,
|
||||
textAlign: TextAlign.justify,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -30,7 +30,6 @@ class LogsWrapperPage extends StatelessWidget {
|
||||
return ImAdaptiveRouteWrapper(
|
||||
primaryRoute: LogsRoute.name,
|
||||
primaryBody: (_) => const LogsPage(),
|
||||
bodyRatio: RatioConstants.oneThird,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -151,6 +150,7 @@ class _LogList extends StatelessWidget {
|
||||
trailing: const Icon(Symbols.arrow_forward_ios_rounded, size: 18),
|
||||
dense: true,
|
||||
visualDensity: VisualDensity.compact,
|
||||
onTap: () => unawaited(context.navigateTo(LogDetailsRoute(log: log))),
|
||||
tileColor: _getTileColor(context, log.level),
|
||||
minLeadingWidth: 10,
|
||||
);
|
||||
|
@ -2,7 +2,7 @@ import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_adaptive_scaffold/flutter_adaptive_scaffold.dart';
|
||||
import 'package:immich_mobile/i18n/strings.g.dart';
|
||||
import 'package:immich_mobile/presentation/components/common/immich_navigation_rail.dart';
|
||||
import 'package:immich_mobile/presentation/components/common/immich_navigation_rail.widget.dart';
|
||||
import 'package:immich_mobile/presentation/components/common/user_avatar.widget.dart';
|
||||
import 'package:immich_mobile/presentation/components/image/immich_logo.widget.dart';
|
||||
import 'package:immich_mobile/presentation/router/router.dart';
|
||||
|
@ -1,7 +1,10 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:immich_mobile/domain/models/log.model.dart';
|
||||
import 'package:immich_mobile/presentation/modules/home/pages/home.page.dart';
|
||||
import 'package:immich_mobile/presentation/modules/library/pages/library.page.dart';
|
||||
import 'package:immich_mobile/presentation/modules/login/pages/login.page.dart';
|
||||
import 'package:immich_mobile/presentation/modules/logs/pages/log_details.page.dart';
|
||||
import 'package:immich_mobile/presentation/modules/logs/pages/logs.page.dart';
|
||||
import 'package:immich_mobile/presentation/modules/search/pages/search.page.dart';
|
||||
import 'package:immich_mobile/presentation/modules/settings/pages/about_settings.page.dart';
|
||||
@ -37,8 +40,8 @@ class AppRouter extends RootStackRouter {
|
||||
),
|
||||
AutoRoute(page: LogsWrapperRoute.page, children: [
|
||||
AutoRoute(page: LogsRoute.page),
|
||||
AutoRoute(page: LogDetailsRoute.page),
|
||||
]),
|
||||
AutoRoute(page: LogsRoute.page),
|
||||
AutoRoute(page: TabControllerRoute.page, children: [
|
||||
AutoRoute(page: HomeRoute.page),
|
||||
AutoRoute(page: SearchRoute.page),
|
||||
|
@ -26,6 +26,7 @@ abstract final class AppColors {
|
||||
surface: Color(0xFFF0EFF4),
|
||||
onSurface: Color(0xff1a1b21),
|
||||
surfaceContainer: Color(0xfffefbff),
|
||||
surfaceContainerHigh: Color(0xFFE0E1EA),
|
||||
surfaceContainerHighest: Color(0xffe0e2ef),
|
||||
onSurfaceVariant: Color(0xff444651),
|
||||
outline: Color(0xff747782),
|
||||
@ -59,6 +60,7 @@ abstract final class AppColors {
|
||||
surface: Color(0xFF15181C),
|
||||
onSurface: Color(0xffe2e2e9),
|
||||
surfaceContainer: Color(0xff1a1e22),
|
||||
surfaceContainerHigh: Color(0xFF2C3138),
|
||||
surfaceContainerHighest: Color(0xff424852),
|
||||
onSurfaceVariant: Color(0xffc2c6d2),
|
||||
outline: Color(0xff8c919c),
|
||||
|
@ -64,6 +64,7 @@ enum AppTheme {
|
||||
closeButtonIconBuilder: (_) => Icon(Symbols.close_rounded),
|
||||
),
|
||||
appBarTheme: AppBarTheme(
|
||||
backgroundColor: color.surfaceContainerLowest,
|
||||
iconTheme: IconThemeData(size: 22, color: color.onSurface),
|
||||
titleTextStyle:
|
||||
AppTypography.titleLarge.copyWith(color: color.onSurface),
|
||||
|
@ -14,6 +14,8 @@ abstract final class SizeConstants {
|
||||
abstract final class RatioConstants {
|
||||
const RatioConstants._();
|
||||
|
||||
// 0.5
|
||||
static const oneHalf = 1 / 2;
|
||||
// 0.3
|
||||
static const oneThird = 1 / 3;
|
||||
// 0.25
|
||||
|
@ -10,4 +10,9 @@ abstract final class SnackbarManager {
|
||||
_s?.clearSnackBars();
|
||||
_s?.showSnackBar(SnackBar(content: Text(errorMsg)));
|
||||
}
|
||||
|
||||
static void showText({required String content, TextStyle? style}) {
|
||||
_s?.clearSnackBars();
|
||||
_s?.showSnackBar(SnackBar(content: Text(content, style: style)));
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user