app bar and log details page

This commit is contained in:
shenlong-tanwen 2024-10-24 01:46:10 +05:30
parent a0afea04d8
commit 5385d43c8c
12 changed files with 183 additions and 8 deletions

View File

@ -38,9 +38,17 @@
}, },
"logs": { "logs": {
"title": "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": { "common": {
"loading": "Loading" "loading": "Loading",
"copied_long": "Copied to clipboard"
} }
} }

View File

@ -1,5 +1,10 @@
import 'package:flutter/material.dart'; 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'; import 'package:immich_mobile/utils/extensions/build_context.extension.dart';
class ImAppBar extends StatelessWidget implements PreferredSizeWidget { class ImAppBar extends StatelessWidget implements PreferredSizeWidget {
@ -11,10 +16,26 @@ class ImAppBar extends StatelessWidget implements PreferredSizeWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AppBar( return AppBar(
backgroundColor: context.theme.appBarTheme.backgroundColor,
automaticallyImplyLeading: false, 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, centerTitle: false,
title: Text(context.t.immich),
); );
} }
} }

View File

@ -65,6 +65,8 @@ class _ImAssetGridState extends State<ImAssetGrid> {
BlocBuilder<AssetGridCubit, AssetGridState>( BlocBuilder<AssetGridCubit, AssetGridState>(
builder: (_, state) { builder: (_, state) {
final elements = state.renderList.elements; final elements = state.renderList.elements;
// Append padding if required
if (widget.topPadding != null && if (widget.topPadding != null &&
elements.firstOrNull is! RenderListPaddingElement) { elements.firstOrNull is! RenderListPaddingElement) {
elements.insert( elements.insert(
@ -74,6 +76,9 @@ class _ImAssetGridState extends State<ImAssetGrid> {
before: elements.firstOrNull, before: elements.firstOrNull,
), ),
); );
} else if (widget.topPadding == null &&
elements.firstOrNull is RenderListPaddingElement) {
elements.removeAt(0);
} }
final grid = FlutterListView( final grid = FlutterListView(

View File

@ -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,
),
),
),
],
);
}
}

View File

@ -30,7 +30,6 @@ class LogsWrapperPage extends StatelessWidget {
return ImAdaptiveRouteWrapper( return ImAdaptiveRouteWrapper(
primaryRoute: LogsRoute.name, primaryRoute: LogsRoute.name,
primaryBody: (_) => const LogsPage(), primaryBody: (_) => const LogsPage(),
bodyRatio: RatioConstants.oneThird,
); );
} }
} }
@ -151,6 +150,7 @@ class _LogList extends StatelessWidget {
trailing: const Icon(Symbols.arrow_forward_ios_rounded, size: 18), trailing: const Icon(Symbols.arrow_forward_ios_rounded, size: 18),
dense: true, dense: true,
visualDensity: VisualDensity.compact, visualDensity: VisualDensity.compact,
onTap: () => unawaited(context.navigateTo(LogDetailsRoute(log: log))),
tileColor: _getTileColor(context, log.level), tileColor: _getTileColor(context, log.level),
minLeadingWidth: 10, minLeadingWidth: 10,
); );

View File

@ -2,7 +2,7 @@ import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_adaptive_scaffold/flutter_adaptive_scaffold.dart'; import 'package:flutter_adaptive_scaffold/flutter_adaptive_scaffold.dart';
import 'package:immich_mobile/i18n/strings.g.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/common/user_avatar.widget.dart';
import 'package:immich_mobile/presentation/components/image/immich_logo.widget.dart'; import 'package:immich_mobile/presentation/components/image/immich_logo.widget.dart';
import 'package:immich_mobile/presentation/router/router.dart'; import 'package:immich_mobile/presentation/router/router.dart';

View File

@ -1,7 +1,10 @@
import 'package:auto_route/auto_route.dart'; 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/home/pages/home.page.dart';
import 'package:immich_mobile/presentation/modules/library/pages/library.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/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/logs/pages/logs.page.dart';
import 'package:immich_mobile/presentation/modules/search/pages/search.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'; 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: LogsWrapperRoute.page, children: [
AutoRoute(page: LogsRoute.page), AutoRoute(page: LogsRoute.page),
AutoRoute(page: LogDetailsRoute.page),
]), ]),
AutoRoute(page: LogsRoute.page),
AutoRoute(page: TabControllerRoute.page, children: [ AutoRoute(page: TabControllerRoute.page, children: [
AutoRoute(page: HomeRoute.page), AutoRoute(page: HomeRoute.page),
AutoRoute(page: SearchRoute.page), AutoRoute(page: SearchRoute.page),

View File

@ -26,6 +26,7 @@ abstract final class AppColors {
surface: Color(0xFFF0EFF4), surface: Color(0xFFF0EFF4),
onSurface: Color(0xff1a1b21), onSurface: Color(0xff1a1b21),
surfaceContainer: Color(0xfffefbff), surfaceContainer: Color(0xfffefbff),
surfaceContainerHigh: Color(0xFFE0E1EA),
surfaceContainerHighest: Color(0xffe0e2ef), surfaceContainerHighest: Color(0xffe0e2ef),
onSurfaceVariant: Color(0xff444651), onSurfaceVariant: Color(0xff444651),
outline: Color(0xff747782), outline: Color(0xff747782),
@ -59,6 +60,7 @@ abstract final class AppColors {
surface: Color(0xFF15181C), surface: Color(0xFF15181C),
onSurface: Color(0xffe2e2e9), onSurface: Color(0xffe2e2e9),
surfaceContainer: Color(0xff1a1e22), surfaceContainer: Color(0xff1a1e22),
surfaceContainerHigh: Color(0xFF2C3138),
surfaceContainerHighest: Color(0xff424852), surfaceContainerHighest: Color(0xff424852),
onSurfaceVariant: Color(0xffc2c6d2), onSurfaceVariant: Color(0xffc2c6d2),
outline: Color(0xff8c919c), outline: Color(0xff8c919c),

View File

@ -64,6 +64,7 @@ enum AppTheme {
closeButtonIconBuilder: (_) => Icon(Symbols.close_rounded), closeButtonIconBuilder: (_) => Icon(Symbols.close_rounded),
), ),
appBarTheme: AppBarTheme( appBarTheme: AppBarTheme(
backgroundColor: color.surfaceContainerLowest,
iconTheme: IconThemeData(size: 22, color: color.onSurface), iconTheme: IconThemeData(size: 22, color: color.onSurface),
titleTextStyle: titleTextStyle:
AppTypography.titleLarge.copyWith(color: color.onSurface), AppTypography.titleLarge.copyWith(color: color.onSurface),

View File

@ -14,6 +14,8 @@ abstract final class SizeConstants {
abstract final class RatioConstants { abstract final class RatioConstants {
const RatioConstants._(); const RatioConstants._();
// 0.5
static const oneHalf = 1 / 2;
// 0.3 // 0.3
static const oneThird = 1 / 3; static const oneThird = 1 / 3;
// 0.25 // 0.25

View File

@ -10,4 +10,9 @@ abstract final class SnackbarManager {
_s?.clearSnackBars(); _s?.clearSnackBars();
_s?.showSnackBar(SnackBar(content: Text(errorMsg))); _s?.showSnackBar(SnackBar(content: Text(errorMsg)));
} }
static void showText({required String content, TextStyle? style}) {
_s?.clearSnackBars();
_s?.showSnackBar(SnackBar(content: Text(content, style: style)));
}
} }