From dfece96a5e4238cde7e88b5f219dfa5db5bc6424 Mon Sep 17 00:00:00 2001 From: Jganenokk Date: Sat, 11 Jul 2026 16:27:37 +0700 Subject: [PATCH] =?UTF-8?q?refactor:=20=D1=83=D0=BC=D0=B5=D0=BD=D1=8C?= =?UTF-8?q?=D1=88=D0=B8=D0=BB=20=D0=BE=D1=82=D1=81=D1=82=D1=83=D0=BF=D1=8B?= =?UTF-8?q?=20=D0=BE=D1=82=20=D0=BA=D1=80=D0=B0=D0=B5=D0=B2=20+=20=D1=81?= =?UTF-8?q?=D0=B4=D0=B5=D0=BB=D0=B0=D0=BB=20=D0=BA=D0=B0=D1=81=D1=82=D0=BE?= =?UTF-8?q?=D0=BC=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D1=8E=20=D1=80=D0=B0=D0=B7?= =?UTF-8?q?=D0=B2=D0=BE=D1=80=D0=B0=D1=87=D0=B8=D0=B2=D0=B0=D0=B5=D0=BC?= =?UTF-8?q?=D0=BE=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/frontend/screens/chats/chat_screen.dart | 2 +- .../screens/profile/customization_screen.dart | 187 ------------------ .../profile/customization_section.dart | 170 ++++++++++++++++ .../screens/profile/settings_tab.dart | 24 +-- lib/frontend/widgets/message_bubble.dart | 8 +- 5 files changed, 178 insertions(+), 213 deletions(-) delete mode 100644 lib/frontend/screens/profile/customization_screen.dart create mode 100644 lib/frontend/screens/profile/customization_section.dart diff --git a/lib/frontend/screens/chats/chat_screen.dart b/lib/frontend/screens/chats/chat_screen.dart index 89e5b53..f234e50 100644 --- a/lib/frontend/screens/chats/chat_screen.dart +++ b/lib/frontend/screens/chats/chat_screen.dart @@ -3897,7 +3897,7 @@ class _ChatScreenState extends State final cs = Theme.of(context).colorScheme; final accent = cs.primary; return Padding( - padding: const EdgeInsets.fromLTRB(12, 8, 12, 6), + padding: const EdgeInsets.fromLTRB(8, 8, 8, 6), child: Row( children: [ Expanded( diff --git a/lib/frontend/screens/profile/customization_screen.dart b/lib/frontend/screens/profile/customization_screen.dart deleted file mode 100644 index 5a794d5..0000000 --- a/lib/frontend/screens/profile/customization_screen.dart +++ /dev/null @@ -1,187 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:material_symbols_icons/symbols.dart'; - -import '../../widgets/connection_status.dart'; - -import '../../../core/utils/haptics.dart'; -import '../../widgets/glossy_pill.dart'; -import 'app_icon_screen.dart'; -import 'appearance_screen.dart'; -import 'chat_background_screen.dart'; -import 'font_settings_screen.dart'; -import 'message_actions_screen.dart'; -import 'theme_settings_screen.dart'; - -class _CustomizationCategory { - final IconData icon; - final String title; - final String subtitle; - final WidgetBuilder builder; - - const _CustomizationCategory({ - required this.icon, - required this.title, - required this.subtitle, - required this.builder, - }); -} - -class CustomizationScreen extends StatelessWidget { - const CustomizationScreen({super.key}); - - static const List<_CustomizationCategory> _categories = [ - _CustomizationCategory( - icon: Symbols.dark_mode, - title: 'Тема', - subtitle: 'Светлая, тёмная, AMOLED, расписание', - builder: _buildThemeSettings, - ), - _CustomizationCategory( - icon: Symbols.palette, - title: 'Внешний вид', - subtitle: 'Акцентный цвет интерфейса', - builder: _buildAppearance, - ), - _CustomizationCategory( - icon: Symbols.wallpaper, - title: 'Фон чатов', - subtitle: 'Общие обои и темы для всех чатов', - builder: _buildChatBackground, - ), - _CustomizationCategory( - icon: Symbols.text_fields, - title: 'Шрифты', - subtitle: 'Шрифт приложения, свои шрифты, размер текста', - builder: _buildFontSettings, - ), - _CustomizationCategory( - icon: Symbols.touch_app, - title: 'Меню действий', - subtitle: 'Радиальное или список — для долгого нажатия на сообщение', - builder: _buildMessageActions, - ), - _CustomizationCategory( - icon: Symbols.apps, - title: 'Иконка приложения', - subtitle: 'Default или Minimal — иконка на главном экране', - builder: _buildAppIcon, - ), - ]; - - static Widget _buildAppearance(BuildContext context) => - const AppearanceScreen(); - - static Widget _buildChatBackground(BuildContext context) => - const ChatBackgroundScreen(); - - static Widget _buildFontSettings(BuildContext context) => - const FontSettingsScreen(); - - static Widget _buildThemeSettings(BuildContext context) => - const ThemeSettingsScreen(); - - static Widget _buildMessageActions(BuildContext context) => - const MessageActionsScreen(); - - static Widget _buildAppIcon(BuildContext context) => const AppIconScreen(); - - @override - Widget build(BuildContext context) { - final cs = Theme.of(context).colorScheme; - - return Scaffold( - backgroundColor: cs.surface, - appBar: ConnectionTitleBar( - titleText: 'Кастомизация', - backgroundColor: cs.surface, - ), - body: SafeArea( - top: false, - child: ListView( - physics: const BouncingScrollPhysics(), - padding: const EdgeInsets.fromLTRB(16, 12, 16, 120), - children: [ - for (final category in _categories) ...[ - _CategoryCard( - category: category, - onTap: () { - Haptics.tap(); - Navigator.push( - context, - MaterialPageRoute(builder: category.builder), - ); - }, - ), - const SizedBox(height: 12), - ], - ], - ), - ), - ); - } -} - -class _CategoryCard extends StatelessWidget { - final _CustomizationCategory category; - final VoidCallback onTap; - - const _CategoryCard({required this.category, required this.onTap}); - - @override - Widget build(BuildContext context) { - final cs = Theme.of(context).colorScheme; - return GlossyPill( - color: cs.surfaceContainerHigh, - borderRadius: BorderRadius.circular(28), - padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 18), - depth: 6, - onTap: onTap, - child: Row( - children: [ - Container( - width: 48, - height: 48, - decoration: BoxDecoration( - color: cs.primaryContainer, - shape: BoxShape.circle, - ), - alignment: Alignment.center, - child: Icon( - category.icon, - color: cs.onPrimaryContainer, - size: 24, - weight: 500, - ), - ), - const SizedBox(width: 16), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - category.title, - style: TextStyle( - color: cs.onSurface, - fontSize: 16, - fontWeight: FontWeight.w700, - ), - ), - const SizedBox(height: 3), - Text( - category.subtitle, - style: TextStyle( - color: cs.onSurfaceVariant, - fontSize: 13, - height: 1.3, - ), - ), - ], - ), - ), - const SizedBox(width: 12), - Icon(Symbols.chevron_right, color: cs.outline, size: 22), - ], - ), - ); - } -} diff --git a/lib/frontend/screens/profile/customization_section.dart b/lib/frontend/screens/profile/customization_section.dart new file mode 100644 index 0000000..1073de2 --- /dev/null +++ b/lib/frontend/screens/profile/customization_section.dart @@ -0,0 +1,170 @@ +import 'package:flutter/material.dart'; +import 'package:material_symbols_icons/symbols.dart'; + +import '../../../core/utils/haptics.dart'; +import '../../widgets/glossy_pill.dart'; +import '../../widgets/settings_card.dart'; +import 'app_icon_screen.dart'; +import 'appearance_screen.dart'; +import 'chat_background_screen.dart'; +import 'font_settings_screen.dart'; +import 'message_actions_screen.dart'; +import 'theme_settings_screen.dart'; + +class _CustomizationCategory { + final IconData icon; + final String title; + final WidgetBuilder builder; + + const _CustomizationCategory({ + required this.icon, + required this.title, + required this.builder, + }); +} + +class CustomizationSection extends StatefulWidget { + const CustomizationSection({super.key}); + + @override + State createState() => _CustomizationSectionState(); +} + +class _CustomizationSectionState extends State { + bool _expanded = false; + + static final List<_CustomizationCategory> _categories = [ + _CustomizationCategory( + icon: Symbols.dark_mode, + title: 'Тема', + builder: (context) => const ThemeSettingsScreen(), + ), + _CustomizationCategory( + icon: Symbols.palette, + title: 'Внешний вид', + builder: (context) => const AppearanceScreen(), + ), + _CustomizationCategory( + icon: Symbols.wallpaper, + title: 'Фон чатов', + builder: (context) => const ChatBackgroundScreen(), + ), + _CustomizationCategory( + icon: Symbols.text_fields, + title: 'Шрифты', + builder: (context) => const FontSettingsScreen(), + ), + _CustomizationCategory( + icon: Symbols.touch_app, + title: 'Меню действий', + builder: (context) => const MessageActionsScreen(), + ), + _CustomizationCategory( + icon: Symbols.apps, + title: 'Иконка приложения', + builder: (context) => const AppIconScreen(), + ), + ]; + + void _toggle() { + Haptics.tap(); + setState(() => _expanded = !_expanded); + } + + void _open(_CustomizationCategory category) { + Haptics.tap(); + Navigator.push(context, MaterialPageRoute(builder: category.builder)); + } + + @override + Widget build(BuildContext context) { + final cs = Theme.of(context).colorScheme; + return GlossyPill( + color: cs.surfaceContainerHigh, + borderRadius: BorderRadius.circular(20), + depth: 6, + child: Column( + children: [ + _buildHeader(cs), + AnimatedSize( + duration: const Duration(milliseconds: 220), + curve: Curves.easeInOut, + alignment: Alignment.topCenter, + child: _expanded + ? Column(children: _buildCategoryTiles(cs)) + : const SizedBox(width: double.infinity), + ), + ], + ), + ); + } + + Widget _buildHeader(ColorScheme cs) { + return Material( + color: Colors.transparent, + child: InkWell( + onTap: _toggle, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 17), + child: Row( + children: [ + Icon( + Symbols.palette, + color: cs.onSurfaceVariant, + size: 22, + weight: 400, + ), + const SizedBox(width: 16), + Expanded( + child: Text( + 'Кастомизация', + style: TextStyle( + color: cs.onSurface, + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + ), + AnimatedRotation( + duration: const Duration(milliseconds: 200), + turns: _expanded ? 0.5 : 0, + child: Icon( + Symbols.expand_more, + color: cs.outline, + size: 22, + weight: 400, + ), + ), + ], + ), + ), + ), + ); + } + + List _buildCategoryTiles(ColorScheme cs) { + final tiles = []; + for (var i = 0; i < _categories.length; i++) { + final category = _categories[i]; + tiles.add( + Padding( + padding: const EdgeInsets.only(left: 58), + child: Divider( + height: 1, + thickness: 1, + color: cs.outlineVariant.withValues(alpha: 0.35), + ), + ), + ); + tiles.add( + SettingsNavTile( + icon: category.icon, + label: category.title, + onTap: () => _open(category), + isLast: i == _categories.length - 1, + ), + ); + } + return tiles; + } +} diff --git a/lib/frontend/screens/profile/settings_tab.dart b/lib/frontend/screens/profile/settings_tab.dart index 624bba2..72833f4 100644 --- a/lib/frontend/screens/profile/settings_tab.dart +++ b/lib/frontend/screens/profile/settings_tab.dart @@ -26,7 +26,7 @@ import '../digital_id/digital_id_screen.dart'; import '../digital_id/digital_id_web_screen.dart'; import '../webapp/web_app_screen.dart'; import 'cloud_storage_screen.dart'; -import 'customization_screen.dart'; +import 'customization_section.dart'; import 'debug_menu_screen.dart'; import 'devices_screen.dart'; import 'edit_profile_screen.dart'; @@ -298,26 +298,10 @@ class _SettingsTabState extends State { ), ), ), - SliverToBoxAdapter( + const SliverToBoxAdapter( child: Padding( - padding: const EdgeInsets.fromLTRB(16, 12, 16, 0), - child: _buildSection( - context, - items: [ - _SettingsItem( - icon: Symbols.palette, - label: 'Кастомизация', - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const CustomizationScreen(), - ), - ); - }, - ), - ], - ), + padding: EdgeInsets.fromLTRB(16, 12, 16, 0), + child: CustomizationSection(), ), ), SliverToBoxAdapter( diff --git a/lib/frontend/widgets/message_bubble.dart b/lib/frontend/widgets/message_bubble.dart index 2871a8c..7062163 100644 --- a/lib/frontend/widgets/message_bubble.dart +++ b/lib/frontend/widgets/message_bubble.dart @@ -530,8 +530,8 @@ class MessageBubble extends StatelessWidget { return Padding( padding: EdgeInsets.only( - left: 12, - right: 12, + left: 8, + right: 8, top: topMargin, bottom: bottomMargin, ), @@ -545,9 +545,7 @@ class MessageBubble extends StatelessWidget { children: [ if (showAvatar) _buildLeadingAvatar(cs) - else if (showAvatarSlot && chatType != "CHAT") - const SizedBox(width: 0) - else if (showAvatarSlot) + else if (showAvatarSlot && chatType == "CHAT") const CircleAvatar( radius: 15, backgroundColor: Color(0x00000000),