diff --git a/lib/app/constants/routes.dart b/lib/app/constants/routes.dart index 0b030940..7d22f418 100644 --- a/lib/app/constants/routes.dart +++ b/lib/app/constants/routes.dart @@ -44,7 +44,7 @@ class Routes { static GoRoute mfaTokenScan = buildRoute('token-scan', t.mfa.tokenScan.title, TokenScanScreen()); static GoRoute mfaTokenInput = buildRoute('token-input', t.mfa.tokenInput.title, TokenInputScreen()); static GoRoute mfa = buildRoute('/mfa', t.mfa.mfa, MfaScreen(), routes: [mfaTokenScan, mfaTokenInput]); - static GoRoute tools = buildRoute('/tools', t.tools.tools, ToolsScreen()); + static GoRoute tools = buildRoute('/tools', t.tools.tools, ToolsScreen(), withMainLayout: false); static GoRoute login = buildRoute('/login', t.login.login, LoginScreen(), withMainLayout: false); static GoRoute support = buildRoute('support', t.settings.support.support, SupportScreen()); static GoRoute settings = buildRoute( diff --git a/lib/features/settings/screens/support_screen.dart b/lib/features/settings/screens/support_screen.dart index a9e4e0b8..0f0c1525 100644 --- a/lib/features/settings/screens/support_screen.dart +++ b/lib/features/settings/screens/support_screen.dart @@ -25,7 +25,13 @@ class SupportScreen extends StatelessWidget { SettingsCard( title: t.settings.support.generalFeedback, subtitle: supportEnabled ? grueneSupportMail : '', - icon: 'assets/icons/gruene.png', + icon: Image.asset( + 'assets/icons/gruene.png', + height: 48, + width: 48, + color: supportEnabled ? null : Colors.grey, + colorBlendMode: supportEnabled ? null : BlendMode.saturation, + ), onPress: () => openMail(grueneSupportMail, context), isExternal: true, isEnabled: supportEnabled, @@ -33,7 +39,13 @@ class SupportScreen extends StatelessWidget { SettingsCard( title: t.settings.support.campaignSupport, subtitle: supportEnabled ? pollionSupportMail : '', - icon: 'assets/icons/pollion.png', + icon: Image.asset( + 'assets/icons/pollion.png', + height: 48, + width: 48, + color: supportEnabled ? null : Colors.grey, + colorBlendMode: supportEnabled ? null : BlendMode.saturation, + ), onPress: () => openMail(pollionSupportMail, context), isExternal: true, isEnabled: supportEnabled, @@ -41,7 +53,13 @@ class SupportScreen extends StatelessWidget { SettingsCard( title: t.settings.support.otherSupport, subtitle: supportEnabled ? verdigadoSupportMail : '', - icon: 'assets/icons/verdigado.png', + icon: Image.asset( + 'assets/icons/verdigado.png', + height: 48, + width: 48, + color: supportEnabled ? null : Colors.grey, + colorBlendMode: supportEnabled ? null : BlendMode.saturation, + ), onPress: () => openMail(verdigadoSupportMail, context), isExternal: true, isEnabled: supportEnabled, diff --git a/lib/features/settings/widgets/settings_card.dart b/lib/features/settings/widgets/settings_card.dart index a3bb32ce..f2c098e8 100644 --- a/lib/features/settings/widgets/settings_card.dart +++ b/lib/features/settings/widgets/settings_card.dart @@ -2,19 +2,19 @@ import 'package:flutter/material.dart'; import 'package:gruene_app/app/theme/theme.dart'; class SettingsCard extends StatelessWidget { - final String icon; + final Widget icon; final String title; final String subtitle; final bool isExternal; final bool isEnabled; - final void Function() onPress; + final void Function()? onPress; const SettingsCard({ super.key, required this.icon, required this.title, required this.subtitle, - required this.onPress, + this.onPress, this.isExternal = false, this.isEnabled = true, }); @@ -40,7 +40,7 @@ class SettingsCard extends StatelessWidget { borderRadius: BorderRadius.circular(10), ), child: ListTile( - onTap: isEnabled ? onPress : null, + onTap: isEnabled && onPress != null ? onPress : null, contentPadding: EdgeInsets.symmetric(horizontal: 12), title: Text( title, @@ -49,21 +49,17 @@ class SettingsCard extends StatelessWidget { subtitle: Text(subtitle, style: TextStyle(color: isEnabled ? ThemeColors.text : ThemeColors.textDisabled)), leading: ClipRRect( borderRadius: BorderRadius.circular(8.0), - child: Image.asset( - icon, - height: 48, - width: 48, - color: isEnabled ? null : Colors.grey, - colorBlendMode: isEnabled ? null : BlendMode.saturation, - ), - ), - trailing: Padding( - padding: const EdgeInsets.only(right: 10), - child: Icon( - isExternal ? Icons.open_in_browser_outlined : Icons.chevron_right_outlined, - color: theme.disabledColor, - ), + child: icon, ), + trailing: onPress != null + ? Padding( + padding: const EdgeInsets.only(right: 10), + child: Icon( + isExternal ? Icons.open_in_browser_outlined : Icons.chevron_right_outlined, + color: theme.disabledColor, + ), + ) + : null, ), ), ); diff --git a/lib/features/tools/domain/tools_api_service.dart b/lib/features/tools/domain/tools_api_service.dart new file mode 100644 index 00000000..291dcae0 --- /dev/null +++ b/lib/features/tools/domain/tools_api_service.dart @@ -0,0 +1,13 @@ +import 'package:gruene_app/app/services/gruene_api_core.dart'; +import 'package:gruene_app/swagger_generated_code/gruene_api.swagger.dart'; + +Future> fetchTools() async => getFromApi( + request: (api) => api.v1GnetzApplicationsGet(), + map: (data) => data.data, + ); + +List getToolCategories(List tools) { + final categories = tools.map((tool) => tool.categories).expand((it) => it).toSet().toList(); + categories.sort((a, b) => a.order.compareTo(b.order)); + return categories; +} diff --git a/lib/features/tools/screens/tools_screen.dart b/lib/features/tools/screens/tools_screen.dart index 1caaacf1..0f5902de 100644 --- a/lib/features/tools/screens/tools_screen.dart +++ b/lib/features/tools/screens/tools_screen.dart @@ -1,22 +1,32 @@ import 'package:flutter/material.dart'; -import 'package:gruene_app/app/theme/theme.dart'; -import 'package:gruene_app/i18n/translations.g.dart'; +import 'package:gruene_app/app/screens/future_loading_screen.dart'; +import 'package:gruene_app/app/screens/tab_screen.dart'; +import 'package:gruene_app/app/widgets/main_layout.dart'; +import 'package:gruene_app/app/widgets/tab_bar.dart'; +import 'package:gruene_app/features/tools/domain/tools_api_service.dart'; +import 'package:gruene_app/features/tools/widgets/tools_list.dart'; +import 'package:gruene_app/swagger_generated_code/gruene_api.swagger.dart'; class ToolsScreen extends StatelessWidget { const ToolsScreen({super.key}); @override Widget build(BuildContext context) { - final theme = Theme.of(context); - return Padding( - padding: const EdgeInsets.fromLTRB(24, 33, 24, 26), - child: Center( - child: Text( - t.common.notImplementedYet, - style: theme.textTheme.bodyLarge?.apply(color: ThemeColors.text), - textAlign: TextAlign.center, - ), - ), + return FutureLoadingScreen( + load: fetchTools, + layoutBuilder: (Widget child) => MainLayout(child: child), + buildChild: (List data) { + final tabs = getToolCategories(data) + .map( + (category) => TabModel( + label: category.title, + view: ToolsList(tools: data.where((tool) => tool.categories.contains(category)).toList()), + ), + ) + .toList(); + + return TabScreen(tabs: tabs); + }, ); } } diff --git a/lib/features/tools/widgets/tools_list.dart b/lib/features/tools/widgets/tools_list.dart new file mode 100644 index 00000000..8137950f --- /dev/null +++ b/lib/features/tools/widgets/tools_list.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:gruene_app/app/constants/config.dart'; +import 'package:gruene_app/app/utils/open_url.dart'; +import 'package:gruene_app/features/settings/widgets/settings_card.dart'; +import 'package:gruene_app/swagger_generated_code/gruene_api.swagger.dart'; + +class ToolsList extends StatelessWidget { + final List tools; + + const ToolsList({super.key, required this.tools}); + + @override + Widget build(BuildContext context) { + return ListView( + padding: const EdgeInsets.all(16), + children: tools.map( + (tool) { + final icon = tool.icon; + final url = tool.url; + return SettingsCard( + title: tool.title, + subtitle: tool.shortDescription[Config.defaultLocale] as String? ?? '', + onPress: url != null ? () => openUrl(url, context) : null, + isExternal: true, + icon: icon != null ? SvgPicture.string(icon, width: 48, height: 48) : SizedBox(width: 48, height: 48), + ); + }, + ).toList(), + ); + } +}