diff --git a/lib/screens/chats_screen.dart b/lib/screens/chats_screen.dart index 434e869..751af6b 100644 --- a/lib/screens/chats_screen.dart +++ b/lib/screens/chats_screen.dart @@ -2000,40 +2000,10 @@ class _ChatsScreenState extends State if (widget.hasScaffold) { return Builder( builder: (context) { - final theme = context.watch(); - - BoxDecoration? chatsListDecoration; - if (theme.chatsListBackgroundType == ChatsListBackgroundType.gradient) { - chatsListDecoration = BoxDecoration( - gradient: LinearGradient( - colors: [ - theme.chatsListGradientColor1, - theme.chatsListGradientColor2, - ], - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - ), - ); - } else if (theme.chatsListBackgroundType == ChatsListBackgroundType.image && - theme.chatsListImagePath != null && - theme.chatsListImagePath!.isNotEmpty) { - chatsListDecoration = BoxDecoration( - image: DecorationImage( - image: FileImage(File(theme.chatsListImagePath!)), - fit: BoxFit.cover, - ), - ); - } - return Scaffold( appBar: _buildAppBar(context), drawer: _buildAppDrawer(context), - body: chatsListDecoration != null - ? Container( - decoration: chatsListDecoration, - child: Row(children: [Expanded(child: bodyContent)]), - ) - : Row(children: [Expanded(child: bodyContent)]), + body: Row(children: [Expanded(child: bodyContent)]), floatingActionButton: FloatingActionButton( onPressed: () { _showAddMenu(context); @@ -2078,30 +2048,7 @@ class _ChatsScreenState extends State right: 16.0, bottom: 16.0, ), - decoration: () { - if (themeProvider.drawerBackgroundType == DrawerBackgroundType.gradient) { - return BoxDecoration( - gradient: LinearGradient( - colors: [ - themeProvider.drawerGradientColor1, - themeProvider.drawerGradientColor2, - ], - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - ), - ); - } else if (themeProvider.drawerBackgroundType == DrawerBackgroundType.image && - themeProvider.drawerImagePath != null && - themeProvider.drawerImagePath!.isNotEmpty) { - return BoxDecoration( - image: DecorationImage( - image: FileImage(File(themeProvider.drawerImagePath!)), - fit: BoxFit.cover, - ), - ); - } - return BoxDecoration(color: colors.primaryContainer); - }(), + decoration: BoxDecoration(color: colors.primaryContainer), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -2326,32 +2273,18 @@ class _ChatsScreenState extends State ); }).toList(), - Container( - decoration: themeProvider.useGradientForAddAccountButton - ? BoxDecoration( - gradient: LinearGradient( - colors: [ - themeProvider.addAccountButtonGradientColor1, - themeProvider.addAccountButtonGradientColor2, - ], - begin: Alignment.centerLeft, - end: Alignment.centerRight, - ), - ) - : null, - child: ListTile( - leading: const Icon(Icons.add_circle_outline), - title: const Text('Добавить аккаунт'), - onTap: () { - Navigator.pop(context); - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => - const PhoneEntryScreen(), - ), - ); - }, - ), + ListTile( + leading: const Icon(Icons.add_circle_outline), + title: const Text('Добавить аккаунт'), + onTap: () { + Navigator.pop(context); + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => + const PhoneEntryScreen(), + ), + ); + }, ), ], ) @@ -2363,90 +2296,89 @@ class _ChatsScreenState extends State }, ), Expanded( - child: () { - final menuColumn = Column( - children: [ - _buildAccountsSection(context, colors), - ListTile( - leading: const Icon(Icons.person_outline), - title: const Text('Мой профиль'), - onTap: () { - Navigator.pop(context); - _navigateToProfileEdit(); - }, - ), - ListTile( - leading: const Icon(Icons.call_outlined), - title: const Text('Звонки'), - onTap: () { - Navigator.pop(context); - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => const CallsScreen(), - ), - ); - }, - ), - ListTile( - leading: const Icon(Icons.music_note), - title: const Text('Музыка'), - onTap: () { - Navigator.pop(context); - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => const MusicLibraryScreen(), - ), - ); - }, - ), - ListTile( - leading: _isReconnecting - ? SizedBox( - width: 24, - height: 24, - child: CircularProgressIndicator( - strokeWidth: 2, - valueColor: AlwaysStoppedAnimation( - colors.primary, - ), + child: Column( + children: [ + _buildAccountsSection(context, colors), + ListTile( + leading: const Icon(Icons.person_outline), + title: const Text('Мой профиль'), + onTap: () { + Navigator.pop(context); // Закрыть Drawer + _navigateToProfileEdit(); // Этот метод у вас уже есть + }, + ), + ListTile( + leading: const Icon(Icons.call_outlined), + title: const Text('Звонки'), + onTap: () { + Navigator.pop(context); // Закрыть Drawer + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => const CallsScreen(), + ), + ); + }, + ), + ListTile( + leading: const Icon(Icons.music_note), + title: const Text('Музыка'), + onTap: () { + Navigator.pop(context); + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => const MusicLibraryScreen(), + ), + ); + }, + ), + ListTile( + leading: _isReconnecting + ? SizedBox( + width: 24, + height: 24, + child: CircularProgressIndicator( + strokeWidth: 2, + valueColor: AlwaysStoppedAnimation( + colors.primary, ), - ) - : const Icon(Icons.refresh), - title: const Text('Переподключиться'), - enabled: !_isReconnecting, - onTap: () async { - if (_isReconnecting) return; + ), + ) + : const Icon(Icons.refresh), + title: const Text('Переподключиться'), + enabled: !_isReconnecting, + onTap: () async { + if (_isReconnecting) return; - setState(() { - _isReconnecting = true; - }); + setState(() { + _isReconnecting = true; + }); - try { - await ApiService.instance.performFullReconnection(); - if (mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: const Text( - 'Переподключение выполнено успешно', - ), - backgroundColor: colors.primaryContainer, - duration: const Duration(seconds: 2), + try { + await ApiService.instance.performFullReconnection(); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: const Text( + 'Переподключение выполнено успешно', ), - ); - Navigator.pop(context); - } - } catch (e) { - if (mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Ошибка переподключения: $e'), - backgroundColor: colors.error, - duration: const Duration(seconds: 3), - ), - ); - } - } finally { - if (mounted) { + backgroundColor: colors.primaryContainer, + duration: const Duration(seconds: 2), + ), + ); + Navigator.pop(context); + } + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Ошибка переподключения: $e'), + backgroundColor: colors.error, + duration: const Duration(seconds: 3), + ), + ); + } + } finally { + if (mounted) { setState(() { _isReconnecting = false; }); @@ -2496,49 +2428,21 @@ class _ChatsScreenState extends State } }, ), + const Spacer(), + const Divider(height: 1, indent: 16, endIndent: 16), ListTile( leading: Icon(Icons.logout, color: colors.error), title: Text('Выйти', style: TextStyle(color: colors.error)), onTap: () { - Navigator.pop(context); + Navigator.pop(context); // Закрыть Drawer _showLogoutDialog(); }, ), - const SizedBox(height: 8), + const SizedBox(height: 8), // Небольшой отступ снизу ], - ); - - if (themeProvider.drawerBackgroundType == DrawerBackgroundType.gradient) { - return Container( - decoration: BoxDecoration( - gradient: LinearGradient( - colors: [ - themeProvider.drawerGradientColor2, - themeProvider.drawerGradientColor1, - ], - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - ), - ), - child: menuColumn, - ); - } else if (themeProvider.drawerBackgroundType == DrawerBackgroundType.image && - themeProvider.drawerImagePath != null && - themeProvider.drawerImagePath!.isNotEmpty) { - return Container( - decoration: BoxDecoration( - image: DecorationImage( - image: FileImage(File(themeProvider.drawerImagePath!)), - fit: BoxFit.cover, - ), - ), - child: menuColumn, - ); - } - return menuColumn; - }(), + ), ), ], ), @@ -3131,46 +3035,14 @@ class _ChatsScreenState extends State ), ]; - final themeProvider = context.watch(); - - BoxDecoration? folderTabsDecoration; - if (themeProvider.folderTabsBackgroundType == FolderTabsBackgroundType.gradient) { - folderTabsDecoration = BoxDecoration( - gradient: LinearGradient( - colors: [ - themeProvider.folderTabsGradientColor1, - themeProvider.folderTabsGradientColor2, - ], - begin: Alignment.topLeft, - end: Alignment.bottomRight, - ), - border: Border( - bottom: BorderSide(color: colors.outline.withOpacity(0.2), width: 1), - ), - ); - } else if (themeProvider.folderTabsBackgroundType == FolderTabsBackgroundType.image && - themeProvider.folderTabsImagePath != null && - themeProvider.folderTabsImagePath!.isNotEmpty) { - folderTabsDecoration = BoxDecoration( - image: DecorationImage( - image: FileImage(File(themeProvider.folderTabsImagePath!)), - fit: BoxFit.cover, - ), - border: Border( - bottom: BorderSide(color: colors.outline.withOpacity(0.2), width: 1), - ), - ); - } - return Container( height: 48, - decoration: folderTabsDecoration ?? - BoxDecoration( - color: colors.surface, - border: Border( - bottom: BorderSide(color: colors.outline.withOpacity(0.2), width: 1), - ), - ), + decoration: BoxDecoration( + color: colors.surface, + border: Border( + bottom: BorderSide(color: colors.outline.withOpacity(0.2), width: 1), + ), + ), child: Stack( children: [ Row( @@ -3879,36 +3751,9 @@ class _ChatsScreenState extends State AppBar _buildAppBar(BuildContext context) { final colors = Theme.of(context).colorScheme; - final themeProvider = context.watch(); - - BoxDecoration? appBarDecoration; - if (themeProvider.appBarBackgroundType == AppBarBackgroundType.gradient) { - appBarDecoration = BoxDecoration( - gradient: LinearGradient( - colors: [ - themeProvider.appBarGradientColor1, - themeProvider.appBarGradientColor2, - ], - begin: Alignment.topLeft, - end: Alignment.bottomRight, - ), - ); - } else if (themeProvider.appBarBackgroundType == AppBarBackgroundType.image && - themeProvider.appBarImagePath != null && - themeProvider.appBarImagePath!.isNotEmpty) { - appBarDecoration = BoxDecoration( - image: DecorationImage( - image: FileImage(File(themeProvider.appBarImagePath!)), - fit: BoxFit.cover, - ), - ); - } return AppBar( titleSpacing: 4.0, - flexibleSpace: appBarDecoration != null - ? Container(decoration: appBarDecoration) - : null, leading: _isSearchExpanded ? IconButton( diff --git a/lib/screens/profile_menu_dialog.dart b/lib/screens/profile_menu_dialog.dart index 9c64453..a71031c 100644 --- a/lib/screens/profile_menu_dialog.dart +++ b/lib/screens/profile_menu_dialog.dart @@ -6,7 +6,6 @@ import 'package:gwid/screens/settings/settings_screen.dart'; import 'package:gwid/screens/phone_entry_screen.dart'; import 'package:provider/provider.dart'; import 'package:gwid/utils/theme_provider.dart'; -import 'package:gwid/api/api_service.dart'; class ProfileMenuDialog extends StatefulWidget { final Profile? myProfile; @@ -196,26 +195,12 @@ class _ProfileMenuDialogState extends State { onTap: () async { if (context.mounted) { Navigator.of(context).pop(); - try { - await ApiService.instance.logout(); - if (context.mounted) { - Navigator.of(context).pushAndRemoveUntil( - MaterialPageRoute( - builder: (_) => const PhoneEntryScreen(), - ), - (route) => false, - ); - } - } catch (e) { - if (context.mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Ошибка при выходе: $e'), - backgroundColor: Theme.of(context).colorScheme.error, - ), - ); - } - } + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute( + builder: (_) => const PhoneEntryScreen(), + ), + (route) => false, + ); } }, ), diff --git a/lib/screens/settings/customization_screen.dart b/lib/screens/settings/customization_screen.dart index f059636..e0f9d76 100644 --- a/lib/screens/settings/customization_screen.dart +++ b/lib/screens/settings/customization_screen.dart @@ -242,6 +242,7 @@ class _CustomizationScreenState extends State { title: const Text("Выбрать видео"), trailing: const Icon(Icons.chevron_right), onTap: () async { + final result = await FilePicker.platform.pickFiles( type: FileType.video, ); @@ -277,7 +278,7 @@ class _CustomizationScreenState extends State { // Предпросмотр баблов const _MessageBubblesPreview(), const SizedBox(height: 16), - + // Прозрачность (сворачиваемый, по умолчанию свернут) _ExpandableSection( title: "Прозрачность", @@ -291,8 +292,7 @@ class _CustomizationScreenState extends State { max: 1.0, divisions: 18, onChanged: (value) => theme.setMessageTextOpacity(value), - displayValue: - "${(theme.messageTextOpacity * 100).round()}%", + displayValue: "${(theme.messageTextOpacity * 100).round()}%", ), _SliderTile( icon: Icons.blur_circular, @@ -301,8 +301,7 @@ class _CustomizationScreenState extends State { min: 0.0, max: 0.5, divisions: 10, - onChanged: (value) => - theme.setMessageShadowIntensity(value), + onChanged: (value) => theme.setMessageShadowIntensity(value), displayValue: "${(theme.messageShadowIntensity * 100).round()}%", ), @@ -314,8 +313,7 @@ class _CustomizationScreenState extends State { max: 1.0, divisions: 18, onChanged: (value) => theme.setMessageMenuOpacity(value), - displayValue: - "${(theme.messageMenuOpacity * 100).round()}%", + displayValue: "${(theme.messageMenuOpacity * 100).round()}%", ), _SliderTile( icon: Icons.blur_on, @@ -342,7 +340,7 @@ class _CustomizationScreenState extends State { ], ), const SizedBox(height: 8), - + // Вид (сворачиваемый) _ExpandableSection( title: "Вид", @@ -370,8 +368,7 @@ class _CustomizationScreenState extends State { value: theme.messageBubbleType, underline: const SizedBox.shrink(), onChanged: (value) { - if (value != null) - theme.setMessageBubbleType(value); + if (value != null) theme.setMessageBubbleType(value); }, items: MessageBubbleType.values.map((type) { return DropdownMenuItem( @@ -393,8 +390,7 @@ class _CustomizationScreenState extends State { opacity: isSystemTheme ? 0.5 : 1.0, child: GestureDetector( onTap: () async { - final initial = - myBubbleColorToShow ?? myBubbleFallback; + final initial = myBubbleColorToShow ?? myBubbleFallback; _showColorPicker( context, initialColor: initial, @@ -429,16 +425,14 @@ class _CustomizationScreenState extends State { _showColorPicker( context, initialColor: initial, - onColorChanged: (color) => - theirBubbleSetter(color), + onColorChanged: (color) => theirBubbleSetter(color), ); }, child: Container( width: 40, height: 40, decoration: BoxDecoration( - color: - theirBubbleColorToShow ?? theirBubbleFallback, + color: theirBubbleColorToShow ?? theirBubbleFallback, borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.grey), ), @@ -463,8 +457,7 @@ class _CustomizationScreenState extends State { title: "Цвет панели ответа", subtitle: "Фиксированный цвет", color: theme.customReplyColor ?? Colors.blue, - onColorChanged: (color) => - theme.setCustomReplyColor(color), + onColorChanged: (color) => theme.setCustomReplyColor(color), ), ], ], @@ -478,7 +471,7 @@ class _CustomizationScreenState extends State { // Предпросмотр всплывающего окна _DialogPreview(), const SizedBox(height: 16), - + // Развернуть настройки _ExpandableSection( title: "Настройки", @@ -492,8 +485,7 @@ class _CustomizationScreenState extends State { max: 1.0, divisions: 20, onChanged: (value) => theme.setProfileDialogOpacity(value), - displayValue: - "${(theme.profileDialogOpacity * 100).round()}%", + displayValue: "${(theme.profileDialogOpacity * 100).round()}%", ), _SliderTile( icon: Icons.blur_on, @@ -525,330 +517,13 @@ class _CustomizationScreenState extends State { ], ), const SizedBox(height: 24), - _ExpandableSection( - title: "Кастомизация+", - initiallyExpanded: false, - children: [ - _CustomSettingTile( - icon: Icons.format_color_fill, - title: "Фон списка чатов", - subtitle: "Выберите тип фона для списка чатов", - child: DropdownButton( - value: theme.chatsListBackgroundType, - underline: const SizedBox.shrink(), - onChanged: (value) { - if (value != null) theme.setChatsListBackgroundType(value); - }, - items: ChatsListBackgroundType.values.map((type) { - return DropdownMenuItem( - value: type, - child: Text(type.displayName), - ); - }).toList(), - ), - ), - if (theme.chatsListBackgroundType == - ChatsListBackgroundType.gradient) ...[ - const SizedBox(height: 16), - _ColorPickerTile( - title: "Цвет 1", - subtitle: "Начальный цвет градиента", - color: theme.chatsListGradientColor1, - onColorChanged: (color) => - theme.setChatsListGradientColor1(color), - ), - const SizedBox(height: 16), - _ColorPickerTile( - title: "Цвет 2", - subtitle: "Конечный цвет градиента", - color: theme.chatsListGradientColor2, - onColorChanged: (color) => - theme.setChatsListGradientColor2(color), - ), - ], - if (theme.chatsListBackgroundType == - ChatsListBackgroundType.image) ...[ - const Divider(height: 24), - ListTile( - contentPadding: EdgeInsets.zero, - leading: const Icon(Icons.photo_library_outlined), - title: const Text("Выбрать изображение"), - trailing: const Icon(Icons.chevron_right), - onTap: () async { - final picker = ImagePicker(); - final image = await picker.pickImage( - source: ImageSource.gallery, - ); - if (image != null) { - theme.setChatsListImagePath(image.path); - } - }, - ), - if (theme.chatsListImagePath?.isNotEmpty == true) ...[ - ListTile( - contentPadding: EdgeInsets.zero, - leading: const Icon( - Icons.delete_outline, - color: Colors.redAccent, - ), - title: const Text( - "Удалить изображение", - style: TextStyle(color: Colors.redAccent), - ), - onTap: () => theme.setChatsListImagePath(null), - ), - ], - ], - const Divider(height: 24), - _CustomSettingTile( - icon: Icons.view_sidebar, - title: "Фон боковой панели", - subtitle: "Выберите тип фона для боковой панели", - child: DropdownButton( - value: theme.drawerBackgroundType, - underline: const SizedBox.shrink(), - onChanged: (value) { - if (value != null) theme.setDrawerBackgroundType(value); - }, - items: DrawerBackgroundType.values.map((type) { - return DropdownMenuItem( - value: type, - child: Text(type.displayName), - ); - }).toList(), - ), - ), - if (theme.drawerBackgroundType == - DrawerBackgroundType.gradient) ...[ - const SizedBox(height: 16), - _ColorPickerTile( - title: "Цвет 1", - subtitle: "Начальный цвет градиента", - color: theme.drawerGradientColor1, - onColorChanged: (color) => - theme.setDrawerGradientColor1(color), - ), - const SizedBox(height: 16), - _ColorPickerTile( - title: "Цвет 2", - subtitle: "Конечный цвет градиента", - color: theme.drawerGradientColor2, - onColorChanged: (color) => - theme.setDrawerGradientColor2(color), - ), - ], - if (theme.drawerBackgroundType == DrawerBackgroundType.image) ...[ - const Divider(height: 24), - ListTile( - contentPadding: EdgeInsets.zero, - leading: const Icon(Icons.photo_library_outlined), - title: const Text("Выбрать изображение"), - trailing: const Icon(Icons.chevron_right), - onTap: () async { - final picker = ImagePicker(); - final image = await picker.pickImage( - source: ImageSource.gallery, - ); - if (image != null) { - theme.setDrawerImagePath(image.path); - } - }, - ), - if (theme.drawerImagePath?.isNotEmpty == true) ...[ - ListTile( - contentPadding: EdgeInsets.zero, - leading: const Icon( - Icons.delete_outline, - color: Colors.redAccent, - ), - title: const Text( - "Удалить изображение", - style: TextStyle(color: Colors.redAccent), - ), - onTap: () => theme.setDrawerImagePath(null), - ), - ], - ], - const Divider(height: 24), - _CustomSettingTile( - icon: Icons.person_add, - title: "Градиент для кнопки добавления аккаунта", - subtitle: "Применить градиент к кнопке в drawer", - child: Switch( - value: theme.useGradientForAddAccountButton, - onChanged: (value) => - theme.setUseGradientForAddAccountButton(value), - ), - ), - if (theme.useGradientForAddAccountButton) ...[ - const SizedBox(height: 16), - _ColorPickerTile( - title: "Цвет 1", - subtitle: "Начальный цвет градиента", - color: theme.addAccountButtonGradientColor1, - onColorChanged: (color) => - theme.setAddAccountButtonGradientColor1(color), - ), - const SizedBox(height: 16), - _ColorPickerTile( - title: "Цвет 2", - subtitle: "Конечный цвет градиента", - color: theme.addAccountButtonGradientColor2, - onColorChanged: (color) => - theme.setAddAccountButtonGradientColor2(color), - ), - ], - const Divider(height: 24), - _CustomSettingTile( - icon: Icons.view_headline, - title: "Фон верхней панели", - subtitle: "Выберите тип фона для AppBar (поиск, Сферум и т.д.)", - child: DropdownButton( - value: theme.appBarBackgroundType, - underline: const SizedBox.shrink(), - onChanged: (value) { - if (value != null) theme.setAppBarBackgroundType(value); - }, - items: AppBarBackgroundType.values.map((type) { - return DropdownMenuItem( - value: type, - child: Text(type.displayName), - ); - }).toList(), - ), - ), - if (theme.appBarBackgroundType == - AppBarBackgroundType.gradient) ...[ - const SizedBox(height: 16), - _ColorPickerTile( - title: "Цвет 1", - subtitle: "Начальный цвет градиента", - color: theme.appBarGradientColor1, - onColorChanged: (color) => - theme.setAppBarGradientColor1(color), - ), - const SizedBox(height: 16), - _ColorPickerTile( - title: "Цвет 2", - subtitle: "Конечный цвет градиента", - color: theme.appBarGradientColor2, - onColorChanged: (color) => - theme.setAppBarGradientColor2(color), - ), - ], - if (theme.appBarBackgroundType == AppBarBackgroundType.image) ...[ - const Divider(height: 24), - ListTile( - contentPadding: EdgeInsets.zero, - leading: const Icon(Icons.photo_library_outlined), - title: const Text("Выбрать изображение"), - trailing: const Icon(Icons.chevron_right), - onTap: () async { - final picker = ImagePicker(); - final image = await picker.pickImage( - source: ImageSource.gallery, - ); - if (image != null) { - theme.setAppBarImagePath(image.path); - } - }, - ), - if (theme.appBarImagePath?.isNotEmpty == true) ...[ - ListTile( - contentPadding: EdgeInsets.zero, - leading: const Icon( - Icons.delete_outline, - color: Colors.redAccent, - ), - title: const Text( - "Удалить изображение", - style: TextStyle(color: Colors.redAccent), - ), - onTap: () => theme.setAppBarImagePath(null), - ), - ], - ], - const Divider(height: 24), - _CustomSettingTile( - icon: Icons.folder, - title: "Фон панели папок", - subtitle: "Выберите тип фона для панели с именами папок", - child: DropdownButton( - value: theme.folderTabsBackgroundType, - underline: const SizedBox.shrink(), - onChanged: (value) { - if (value != null) theme.setFolderTabsBackgroundType(value); - }, - items: FolderTabsBackgroundType.values.map((type) { - return DropdownMenuItem( - value: type, - child: Text(type.displayName), - ); - }).toList(), - ), - ), - if (theme.folderTabsBackgroundType == - FolderTabsBackgroundType.gradient) ...[ - const SizedBox(height: 16), - _ColorPickerTile( - title: "Цвет 1", - subtitle: "Начальный цвет градиента", - color: theme.folderTabsGradientColor1, - onColorChanged: (color) => - theme.setFolderTabsGradientColor1(color), - ), - const SizedBox(height: 16), - _ColorPickerTile( - title: "Цвет 2", - subtitle: "Конечный цвет градиента", - color: theme.folderTabsGradientColor2, - onColorChanged: (color) => - theme.setFolderTabsGradientColor2(color), - ), - ], - if (theme.folderTabsBackgroundType == - FolderTabsBackgroundType.image) ...[ - const Divider(height: 24), - ListTile( - contentPadding: EdgeInsets.zero, - leading: const Icon(Icons.photo_library_outlined), - title: const Text("Выбрать изображение"), - trailing: const Icon(Icons.chevron_right), - onTap: () async { - final picker = ImagePicker(); - final image = await picker.pickImage( - source: ImageSource.gallery, - ); - if (image != null) { - theme.setFolderTabsImagePath(image.path); - } - }, - ), - if (theme.folderTabsImagePath?.isNotEmpty == true) ...[ - ListTile( - contentPadding: EdgeInsets.zero, - leading: const Icon( - Icons.delete_outline, - color: Colors.redAccent, - ), - title: const Text( - "Удалить изображение", - style: TextStyle(color: Colors.redAccent), - ), - onTap: () => theme.setFolderTabsImagePath(null), - ), - ], - ], - ], - ), - const SizedBox(height: 24), _ModernSection( title: "Панели чата", children: [ // Предпросмотр панелей _PanelsPreview(), const SizedBox(height: 16), - + // Галочка включения эффекта стекла _CustomSettingTile( icon: Icons.tune, @@ -860,7 +535,7 @@ class _CustomizationScreenState extends State { ), ), const SizedBox(height: 8), - + // Развернуть настройки _ExpandableSection( title: "Настройки", @@ -1713,6 +1388,7 @@ class _ChatWallpaperPreview extends StatelessWidget { ); } case ChatWallpaperType.video: + if (Platform.isWindows) { return Container( color: isDarkTheme ? Colors.grey[850] : Colors.grey[200], @@ -1896,10 +1572,7 @@ class _ExpandableSectionState extends State<_ExpandableSection> { onTap: () => setState(() => _isExpanded = !_isExpanded), borderRadius: BorderRadius.circular(8), child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 12.0, - horizontal: 4.0, - ), + padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 4.0), child: Row( children: [ Text( @@ -1918,7 +1591,10 @@ class _ExpandableSectionState extends State<_ExpandableSection> { ), ), ), - if (_isExpanded) ...[const SizedBox(height: 8), ...widget.children], + if (_isExpanded) ...[ + const SizedBox(height: 8), + ...widget.children, + ], ], ); } @@ -1970,7 +1646,10 @@ class _MessageBubblesPreview extends StatelessWidget { children: [ const Spacer(), Expanded( - child: ChatMessageBubble(message: mockMyMessage, isMe: true), + child: ChatMessageBubble( + message: mockMyMessage, + isMe: true, + ), ), ], ), @@ -2029,10 +1708,16 @@ class _DialogPreview extends StatelessWidget { decoration: BoxDecoration( color: colors.surface.withOpacity(theme.profileDialogOpacity), borderRadius: BorderRadius.circular(12), - border: Border.all(color: colors.outline.withOpacity(0.2)), + border: Border.all( + color: colors.outline.withOpacity(0.2), + ), ), child: Center( - child: Icon(Icons.person, color: colors.onSurface, size: 32), + child: Icon( + Icons.person, + color: colors.onSurface, + size: 32, + ), ), ), ), @@ -2146,9 +1831,7 @@ class _PanelsPreview extends StatelessWidget { ), child: Container( height: 30, - color: colors.surface.withOpacity( - theme.bottomBarOpacity, - ), + color: colors.surface.withOpacity(theme.bottomBarOpacity), child: Row( children: [ const SizedBox(width: 12), diff --git a/lib/services/avatar_cache_service.dart b/lib/services/avatar_cache_service.dart index d86ed99..9ea5cf1 100644 --- a/lib/services/avatar_cache_service.dart +++ b/lib/services/avatar_cache_service.dart @@ -40,12 +40,7 @@ class AvatarCacheService { final timestamp = _imageCacheTimestamps[cacheKey]; if (timestamp != null && !_isExpired(timestamp, _imageTTL)) { final imageData = _imageMemoryCache[cacheKey]!; - if (_isValidImageData(imageData)) { - return MemoryImage(imageData); - } else { - _imageMemoryCache.remove(cacheKey); - _imageCacheTimestamps.remove(cacheKey); - } + return MemoryImage(imageData); } else { _imageMemoryCache.remove(cacheKey); _imageCacheTimestamps.remove(cacheKey); @@ -57,25 +52,20 @@ class AvatarCacheService { customKey: cacheKey, ); if (cachedFile != null && await cachedFile.exists()) { - try { - final imageData = await cachedFile.readAsBytes(); - if (_isValidImageData(imageData)) { - _imageMemoryCache[cacheKey] = imageData; - _imageCacheTimestamps[cacheKey] = DateTime.now(); + final imageData = await cachedFile.readAsBytes(); - if (_imageMemoryCache.length > _maxMemoryImages) { - await _evictOldestImages(); - } + _imageMemoryCache[cacheKey] = imageData; + _imageCacheTimestamps[cacheKey] = DateTime.now(); - return MemoryImage(imageData); - } - } catch (e) { - print('Ошибка чтения кешированного файла аватарки: $e'); + if (_imageMemoryCache.length > _maxMemoryImages) { + await _evictOldestImages(); } + + return MemoryImage(imageData); } final imageData = await _downloadImage(avatarUrl); - if (imageData != null && _isValidImageData(imageData)) { + if (imageData != null) { await _cacheService.cacheFile(avatarUrl, customKey: cacheKey); _imageMemoryCache[cacheKey] = imageData; @@ -83,8 +73,6 @@ class AvatarCacheService { return MemoryImage(imageData); } - - return NetworkImage(avatarUrl); } catch (e) { print('Ошибка получения аватарки: $e'); } @@ -123,11 +111,6 @@ class AvatarCacheService { return null; } - if (!_isValidImageData(imageData)) { - print('Невалидные данные изображения для $url'); - return null; - } - return imageData; } } catch (e) { @@ -136,34 +119,6 @@ class AvatarCacheService { return null; } - bool _isValidImageData(Uint8List data) { - if (data.isEmpty) return false; - if (data.length < 4) return false; - - final header = data.sublist(0, 4); - final pngHeader = [0x89, 0x50, 0x4E, 0x47]; - final jpegHeader = [0xFF, 0xD8, 0xFF]; - final gifHeader = [0x47, 0x49, 0x46, 0x38]; - final webpHeader = [0x52, 0x49, 0x46, 0x46]; - - bool isValid = false; - if (header[0] == pngHeader[0] && header[1] == pngHeader[1] && - header[2] == pngHeader[2] && header[3] == pngHeader[3]) { - isValid = true; - } else if (header[0] == jpegHeader[0] && header[1] == jpegHeader[1] && - header[2] == jpegHeader[2]) { - isValid = true; - } else if (header[0] == gifHeader[0] && header[1] == gifHeader[1] && - header[2] == gifHeader[2] && header[3] == gifHeader[3]) { - isValid = true; - } else if (header[0] == webpHeader[0] && header[1] == webpHeader[1] && - header[2] == webpHeader[2] && header[3] == webpHeader[3]) { - isValid = true; - } - - return isValid; - } - String _generateCacheKey(String url, int? userId) { if (userId != null) { return 'avatar_${userId}_${_hashUrl(url)}'; diff --git a/lib/utils/theme_provider.dart b/lib/utils/theme_provider.dart index d00a5c5..8923963 100644 --- a/lib/utils/theme_provider.dart +++ b/lib/utils/theme_provider.dart @@ -6,14 +6,6 @@ enum AppTheme { system, light, dark, black } enum ChatWallpaperType { solid, gradient, image, video } -enum FolderTabsBackgroundType { none, gradient, image } - -enum DrawerBackgroundType { none, gradient, image } - -enum ChatsListBackgroundType { none, gradient, image } - -enum AppBarBackgroundType { none, gradient, image } - enum TransitionOption { systemDefault, slide } enum UIMode { both, burgerOnly, panelOnly } @@ -55,58 +47,6 @@ extension ChatWallpaperTypeExtension on ChatWallpaperType { } } -extension FolderTabsBackgroundTypeExtension on FolderTabsBackgroundType { - String get displayName { - switch (this) { - case FolderTabsBackgroundType.none: - return 'Нет'; - case FolderTabsBackgroundType.gradient: - return 'Градиент'; - case FolderTabsBackgroundType.image: - return 'Фото'; - } - } -} - -extension DrawerBackgroundTypeExtension on DrawerBackgroundType { - String get displayName { - switch (this) { - case DrawerBackgroundType.none: - return 'Нет'; - case DrawerBackgroundType.gradient: - return 'Градиент'; - case DrawerBackgroundType.image: - return 'Фото'; - } - } -} - -extension ChatsListBackgroundTypeExtension on ChatsListBackgroundType { - String get displayName { - switch (this) { - case ChatsListBackgroundType.none: - return 'Нет'; - case ChatsListBackgroundType.gradient: - return 'Градиент'; - case ChatsListBackgroundType.image: - return 'Фото'; - } - } -} - -extension AppBarBackgroundTypeExtension on AppBarBackgroundType { - String get displayName { - switch (this) { - case AppBarBackgroundType.none: - return 'Нет'; - case AppBarBackgroundType.gradient: - return 'Градиент'; - case AppBarBackgroundType.image: - return 'Фото'; - } - } -} - class CustomThemePreset { String id; String name; @@ -165,30 +105,6 @@ class CustomThemePreset { bool useAutoReplyColor; Color? customReplyColor; - bool useGradientForChatsList; - ChatsListBackgroundType chatsListBackgroundType; - String? chatsListImagePath; - bool useGradientForDrawer; - DrawerBackgroundType drawerBackgroundType; - String? drawerImagePath; - bool useGradientForAddAccountButton; - bool useGradientForAppBar; - AppBarBackgroundType appBarBackgroundType; - String? appBarImagePath; - bool useGradientForFolderTabs; - FolderTabsBackgroundType folderTabsBackgroundType; - String? folderTabsImagePath; - Color chatsListGradientColor1; - Color chatsListGradientColor2; - Color drawerGradientColor1; - Color drawerGradientColor2; - Color addAccountButtonGradientColor1; - Color addAccountButtonGradientColor2; - Color appBarGradientColor1; - Color appBarGradientColor2; - Color folderTabsGradientColor1; - Color folderTabsGradientColor2; - CustomThemePreset({ required this.id, required this.name, @@ -239,29 +155,6 @@ class CustomThemePreset { this.useDesktopLayout = true, this.useAutoReplyColor = true, this.customReplyColor, - this.useGradientForChatsList = false, - this.chatsListBackgroundType = ChatsListBackgroundType.none, - this.chatsListImagePath, - this.useGradientForDrawer = false, - this.drawerBackgroundType = DrawerBackgroundType.none, - this.drawerImagePath, - this.useGradientForAddAccountButton = false, - this.useGradientForAppBar = false, - this.appBarBackgroundType = AppBarBackgroundType.none, - this.appBarImagePath, - this.useGradientForFolderTabs = false, - this.folderTabsBackgroundType = FolderTabsBackgroundType.none, - this.folderTabsImagePath, - this.chatsListGradientColor1 = const Color(0xFF1E1E1E), - this.chatsListGradientColor2 = const Color(0xFF2D2D2D), - this.drawerGradientColor1 = const Color(0xFF1E1E1E), - this.drawerGradientColor2 = const Color(0xFF2D2D2D), - this.addAccountButtonGradientColor1 = const Color(0xFF1E1E1E), - this.addAccountButtonGradientColor2 = const Color(0xFF2D2D2D), - this.appBarGradientColor1 = const Color(0xFF1E1E1E), - this.appBarGradientColor2 = const Color(0xFF2D2D2D), - this.folderTabsGradientColor1 = const Color(0xFF1E1E1E), - this.folderTabsGradientColor2 = const Color(0xFF2D2D2D), }); factory CustomThemePreset.createDefault() { @@ -318,29 +211,6 @@ class CustomThemePreset { bool? useDesktopLayout, bool? useAutoReplyColor, Color? customReplyColor, - bool? useGradientForChatsList, - ChatsListBackgroundType? chatsListBackgroundType, - String? chatsListImagePath, - bool? useGradientForDrawer, - DrawerBackgroundType? drawerBackgroundType, - String? drawerImagePath, - bool? useGradientForAddAccountButton, - bool? useGradientForAppBar, - AppBarBackgroundType? appBarBackgroundType, - String? appBarImagePath, - bool? useGradientForFolderTabs, - FolderTabsBackgroundType? folderTabsBackgroundType, - String? folderTabsImagePath, - Color? chatsListGradientColor1, - Color? chatsListGradientColor2, - Color? drawerGradientColor1, - Color? drawerGradientColor2, - Color? addAccountButtonGradientColor1, - Color? addAccountButtonGradientColor2, - Color? appBarGradientColor1, - Color? appBarGradientColor2, - Color? folderTabsGradientColor1, - Color? folderTabsGradientColor2, }) { return CustomThemePreset( id: id ?? this.id, @@ -401,29 +271,6 @@ class CustomThemePreset { useDesktopLayout: useDesktopLayout ?? this.useDesktopLayout, useAutoReplyColor: useAutoReplyColor ?? this.useAutoReplyColor, customReplyColor: customReplyColor ?? this.customReplyColor, - useGradientForChatsList: useGradientForChatsList ?? this.useGradientForChatsList, - chatsListBackgroundType: chatsListBackgroundType ?? this.chatsListBackgroundType, - chatsListImagePath: chatsListImagePath ?? this.chatsListImagePath, - useGradientForDrawer: useGradientForDrawer ?? this.useGradientForDrawer, - drawerBackgroundType: drawerBackgroundType ?? this.drawerBackgroundType, - drawerImagePath: drawerImagePath ?? this.drawerImagePath, - useGradientForAddAccountButton: useGradientForAddAccountButton ?? this.useGradientForAddAccountButton, - useGradientForAppBar: useGradientForAppBar ?? this.useGradientForAppBar, - appBarBackgroundType: appBarBackgroundType ?? this.appBarBackgroundType, - appBarImagePath: appBarImagePath ?? this.appBarImagePath, - useGradientForFolderTabs: useGradientForFolderTabs ?? this.useGradientForFolderTabs, - folderTabsBackgroundType: folderTabsBackgroundType ?? this.folderTabsBackgroundType, - folderTabsImagePath: folderTabsImagePath ?? this.folderTabsImagePath, - chatsListGradientColor1: chatsListGradientColor1 ?? this.chatsListGradientColor1, - chatsListGradientColor2: chatsListGradientColor2 ?? this.chatsListGradientColor2, - drawerGradientColor1: drawerGradientColor1 ?? this.drawerGradientColor1, - drawerGradientColor2: drawerGradientColor2 ?? this.drawerGradientColor2, - addAccountButtonGradientColor1: addAccountButtonGradientColor1 ?? this.addAccountButtonGradientColor1, - addAccountButtonGradientColor2: addAccountButtonGradientColor2 ?? this.addAccountButtonGradientColor2, - appBarGradientColor1: appBarGradientColor1 ?? this.appBarGradientColor1, - appBarGradientColor2: appBarGradientColor2 ?? this.appBarGradientColor2, - folderTabsGradientColor1: folderTabsGradientColor1 ?? this.folderTabsGradientColor1, - folderTabsGradientColor2: folderTabsGradientColor2 ?? this.folderTabsGradientColor2, ); } @@ -478,29 +325,6 @@ class CustomThemePreset { 'useDesktopLayout': useDesktopLayout, 'useAutoReplyColor': useAutoReplyColor, 'customReplyColor': customReplyColor?.value, - 'useGradientForChatsList': useGradientForChatsList, - 'chatsListBackgroundType': chatsListBackgroundType.index, - 'chatsListImagePath': chatsListImagePath, - 'useGradientForDrawer': useGradientForDrawer, - 'drawerBackgroundType': drawerBackgroundType.index, - 'drawerImagePath': drawerImagePath, - 'useGradientForAddAccountButton': useGradientForAddAccountButton, - 'useGradientForAppBar': useGradientForAppBar, - 'appBarBackgroundType': appBarBackgroundType.index, - 'appBarImagePath': appBarImagePath, - 'useGradientForFolderTabs': useGradientForFolderTabs, - 'folderTabsBackgroundType': folderTabsBackgroundType.index, - 'folderTabsImagePath': folderTabsImagePath, - 'chatsListGradientColor1': chatsListGradientColor1.value, - 'chatsListGradientColor2': chatsListGradientColor2.value, - 'drawerGradientColor1': drawerGradientColor1.value, - 'drawerGradientColor2': drawerGradientColor2.value, - 'addAccountButtonGradientColor1': addAccountButtonGradientColor1.value, - 'addAccountButtonGradientColor2': addAccountButtonGradientColor2.value, - 'appBarGradientColor1': appBarGradientColor1.value, - 'appBarGradientColor2': appBarGradientColor2.value, - 'folderTabsGradientColor1': folderTabsGradientColor1.value, - 'folderTabsGradientColor2': folderTabsGradientColor2.value, }; } @@ -598,57 +422,6 @@ class CustomThemePreset { customReplyColor: json['customReplyColor'] != null ? Color(json['customReplyColor'] as int) : null, - useGradientForChatsList: json['useGradientForChatsList'] as bool? ?? false, - chatsListBackgroundType: ChatsListBackgroundType.values[ - json['chatsListBackgroundType'] as int? ?? 0 - ], - chatsListImagePath: json['chatsListImagePath'] as String?, - useGradientForDrawer: json['useGradientForDrawer'] as bool? ?? false, - drawerBackgroundType: DrawerBackgroundType.values[ - json['drawerBackgroundType'] as int? ?? 0 - ], - drawerImagePath: json['drawerImagePath'] as String?, - useGradientForAddAccountButton: json['useGradientForAddAccountButton'] as bool? ?? false, - useGradientForAppBar: json['useGradientForAppBar'] as bool? ?? false, - appBarBackgroundType: AppBarBackgroundType.values[ - json['appBarBackgroundType'] as int? ?? 0 - ], - appBarImagePath: json['appBarImagePath'] as String?, - useGradientForFolderTabs: json['useGradientForFolderTabs'] as bool? ?? false, - folderTabsBackgroundType: FolderTabsBackgroundType.values[ - json['folderTabsBackgroundType'] as int? ?? 0 - ], - folderTabsImagePath: json['folderTabsImagePath'] as String?, - chatsListGradientColor1: Color( - json['chatsListGradientColor1'] as int? ?? const Color(0xFF1E1E1E).value, - ), - chatsListGradientColor2: Color( - json['chatsListGradientColor2'] as int? ?? const Color(0xFF2D2D2D).value, - ), - drawerGradientColor1: Color( - json['drawerGradientColor1'] as int? ?? const Color(0xFF1E1E1E).value, - ), - drawerGradientColor2: Color( - json['drawerGradientColor2'] as int? ?? const Color(0xFF2D2D2D).value, - ), - addAccountButtonGradientColor1: Color( - json['addAccountButtonGradientColor1'] as int? ?? const Color(0xFF1E1E1E).value, - ), - addAccountButtonGradientColor2: Color( - json['addAccountButtonGradientColor2'] as int? ?? const Color(0xFF2D2D2D).value, - ), - appBarGradientColor1: Color( - json['appBarGradientColor1'] as int? ?? const Color(0xFF1E1E1E).value, - ), - appBarGradientColor2: Color( - json['appBarGradientColor2'] as int? ?? const Color(0xFF2D2D2D).value, - ), - folderTabsGradientColor1: Color( - json['folderTabsGradientColor1'] as int? ?? const Color(0xFF1E1E1E).value, - ), - folderTabsGradientColor2: Color( - json['folderTabsGradientColor2'] as int? ?? const Color(0xFF2D2D2D).value, - ), ); } } @@ -779,29 +552,6 @@ class ThemeProvider with ChangeNotifier { bool get useDesktopLayout => _activeTheme.useDesktopLayout; bool get useAutoReplyColor => _activeTheme.useAutoReplyColor; Color? get customReplyColor => _activeTheme.customReplyColor; - bool get useGradientForChatsList => _activeTheme.useGradientForChatsList; - ChatsListBackgroundType get chatsListBackgroundType => _activeTheme.chatsListBackgroundType; - String? get chatsListImagePath => _activeTheme.chatsListImagePath; - bool get useGradientForDrawer => _activeTheme.useGradientForDrawer; - DrawerBackgroundType get drawerBackgroundType => _activeTheme.drawerBackgroundType; - String? get drawerImagePath => _activeTheme.drawerImagePath; - bool get useGradientForAddAccountButton => _activeTheme.useGradientForAddAccountButton; - bool get useGradientForAppBar => _activeTheme.useGradientForAppBar; - AppBarBackgroundType get appBarBackgroundType => _activeTheme.appBarBackgroundType; - String? get appBarImagePath => _activeTheme.appBarImagePath; - bool get useGradientForFolderTabs => _activeTheme.useGradientForFolderTabs; - FolderTabsBackgroundType get folderTabsBackgroundType => _activeTheme.folderTabsBackgroundType; - String? get folderTabsImagePath => _activeTheme.folderTabsImagePath; - Color get chatsListGradientColor1 => _activeTheme.chatsListGradientColor1; - Color get chatsListGradientColor2 => _activeTheme.chatsListGradientColor2; - Color get drawerGradientColor1 => _activeTheme.drawerGradientColor1; - Color get drawerGradientColor2 => _activeTheme.drawerGradientColor2; - Color get addAccountButtonGradientColor1 => _activeTheme.addAccountButtonGradientColor1; - Color get addAccountButtonGradientColor2 => _activeTheme.addAccountButtonGradientColor2; - Color get appBarGradientColor1 => _activeTheme.appBarGradientColor1; - Color get appBarGradientColor2 => _activeTheme.appBarGradientColor2; - Color get folderTabsGradientColor1 => _activeTheme.folderTabsGradientColor1; - Color get folderTabsGradientColor2 => _activeTheme.folderTabsGradientColor2; bool get highQualityPhotos => _highQualityPhotos; bool get blockBypass => _blockBypass; @@ -1468,144 +1218,6 @@ class ThemeProvider with ChangeNotifier { await _saveActiveTheme(); } - Future setUseGradientForChatsList(bool value) async { - _activeTheme = _activeTheme.copyWith(useGradientForChatsList: value); - notifyListeners(); - await _saveActiveTheme(); - } - - Future setChatsListBackgroundType(ChatsListBackgroundType type) async { - _activeTheme = _activeTheme.copyWith(chatsListBackgroundType: type); - notifyListeners(); - await _saveActiveTheme(); - } - - Future setChatsListImagePath(String? path) async { - _activeTheme = _activeTheme.copyWith(chatsListImagePath: path); - notifyListeners(); - await _saveActiveTheme(); - } - - Future setUseGradientForDrawer(bool value) async { - _activeTheme = _activeTheme.copyWith(useGradientForDrawer: value); - notifyListeners(); - await _saveActiveTheme(); - } - - Future setDrawerBackgroundType(DrawerBackgroundType type) async { - _activeTheme = _activeTheme.copyWith(drawerBackgroundType: type); - notifyListeners(); - await _saveActiveTheme(); - } - - Future setDrawerImagePath(String? path) async { - _activeTheme = _activeTheme.copyWith(drawerImagePath: path); - notifyListeners(); - await _saveActiveTheme(); - } - - Future setChatsListGradientColor1(Color color) async { - _activeTheme = _activeTheme.copyWith(chatsListGradientColor1: color); - notifyListeners(); - await _saveActiveTheme(); - } - - Future setChatsListGradientColor2(Color color) async { - _activeTheme = _activeTheme.copyWith(chatsListGradientColor2: color); - notifyListeners(); - await _saveActiveTheme(); - } - - Future setDrawerGradientColor1(Color color) async { - _activeTheme = _activeTheme.copyWith(drawerGradientColor1: color); - notifyListeners(); - await _saveActiveTheme(); - } - - Future setDrawerGradientColor2(Color color) async { - _activeTheme = _activeTheme.copyWith(drawerGradientColor2: color); - notifyListeners(); - await _saveActiveTheme(); - } - - Future setUseGradientForAddAccountButton(bool value) async { - _activeTheme = _activeTheme.copyWith(useGradientForAddAccountButton: value); - notifyListeners(); - await _saveActiveTheme(); - } - - Future setAddAccountButtonGradientColor1(Color color) async { - _activeTheme = _activeTheme.copyWith(addAccountButtonGradientColor1: color); - notifyListeners(); - await _saveActiveTheme(); - } - - Future setAddAccountButtonGradientColor2(Color color) async { - _activeTheme = _activeTheme.copyWith(addAccountButtonGradientColor2: color); - notifyListeners(); - await _saveActiveTheme(); - } - - Future setUseGradientForAppBar(bool value) async { - _activeTheme = _activeTheme.copyWith(useGradientForAppBar: value); - notifyListeners(); - await _saveActiveTheme(); - } - - Future setAppBarGradientColor1(Color color) async { - _activeTheme = _activeTheme.copyWith(appBarGradientColor1: color); - notifyListeners(); - await _saveActiveTheme(); - } - - Future setAppBarGradientColor2(Color color) async { - _activeTheme = _activeTheme.copyWith(appBarGradientColor2: color); - notifyListeners(); - await _saveActiveTheme(); - } - - Future setAppBarBackgroundType(AppBarBackgroundType type) async { - _activeTheme = _activeTheme.copyWith(appBarBackgroundType: type); - notifyListeners(); - await _saveActiveTheme(); - } - - Future setAppBarImagePath(String? path) async { - _activeTheme = _activeTheme.copyWith(appBarImagePath: path); - notifyListeners(); - await _saveActiveTheme(); - } - - Future setUseGradientForFolderTabs(bool value) async { - _activeTheme = _activeTheme.copyWith(useGradientForFolderTabs: value); - notifyListeners(); - await _saveActiveTheme(); - } - - Future setFolderTabsGradientColor1(Color color) async { - _activeTheme = _activeTheme.copyWith(folderTabsGradientColor1: color); - notifyListeners(); - await _saveActiveTheme(); - } - - Future setFolderTabsGradientColor2(Color color) async { - _activeTheme = _activeTheme.copyWith(folderTabsGradientColor2: color); - notifyListeners(); - await _saveActiveTheme(); - } - - Future setFolderTabsBackgroundType(FolderTabsBackgroundType type) async { - _activeTheme = _activeTheme.copyWith(folderTabsBackgroundType: type); - notifyListeners(); - await _saveActiveTheme(); - } - - Future setFolderTabsImagePath(String? path) async { - _activeTheme = _activeTheme.copyWith(folderTabsImagePath: path); - notifyListeners(); - await _saveActiveTheme(); - } - void toggleTheme() { if (appTheme == AppTheme.light) { setTheme(AppTheme.dark); diff --git a/lib/widgets/chat_message_bubble.dart b/lib/widgets/chat_message_bubble.dart index 8cc9afa..787e8ab 100644 --- a/lib/widgets/chat_message_bubble.dart +++ b/lib/widgets/chat_message_bubble.dart @@ -657,7 +657,9 @@ class ChatMessageBubble extends StatelessWidget { Uint8List? lowQualityBytes, int? videoType, }) { + // Логика открытия плеера void openFullScreenVideo() async { + // Показываем индикатор загрузки, пока получаем URL showDialog( context: context, barrierDismissible: false, @@ -667,12 +669,12 @@ class ChatMessageBubble extends StatelessWidget { try { final videoUrl = await ApiService.instance.getVideoUrl( videoId, - chatId!, + chatId!, // chatId из `build` messageId, ); - if (!context.mounted) return; - Navigator.pop(context); + if (!context.mounted) return; // [!code ++] Проверка правильным способом + Navigator.pop(context); // Убираем индикатор Navigator.push( context, MaterialPageRoute( @@ -680,8 +682,8 @@ class ChatMessageBubble extends StatelessWidget { ), ); } catch (e) { - if (!context.mounted) return; - Navigator.pop(context); + if (!context.mounted) return; // [!code ++] Проверка правильным способом + Navigator.pop(context); // Убираем индикатор ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Не удалось загрузить видео: $e'), @@ -703,58 +705,55 @@ class ChatMessageBubble extends StatelessWidget { ); } - return RepaintBoundary( - key: ValueKey('video_preview_boundary_${messageId}_$videoId'), - child: GestureDetector( - onTap: openFullScreenVideo, - child: AspectRatio( - aspectRatio: 16 / 9, - child: ClipRRect( - borderRadius: BorderRadius.circular(12), - child: Stack( - alignment: Alignment.center, - fit: StackFit.expand, - children: [ - (highQualityUrl != null && highQualityUrl.isNotEmpty) || - (lowQualityBytes != null) - ? _ProgressiveNetworkImage( - key: ValueKey('video_preview_image_${messageId}_$videoId'), - url: highQualityUrl ?? '', - previewBytes: lowQualityBytes, - width: 220, - height: 160, - fit: BoxFit.cover, - keepAlive: true, - ) - : Container( - color: Colors.black26, - child: const Center( - child: Icon( - Icons.video_library_outlined, - color: Colors.white, - size: 40, - ), + return GestureDetector( + onTap: openFullScreenVideo, + child: AspectRatio( + aspectRatio: 16 / 9, + child: ClipRRect( + borderRadius: BorderRadius.circular(12), + child: Stack( + alignment: Alignment.center, + fit: StackFit.expand, + children: [ + // Если у нас есть ХОТЬ ЧТО-ТО (блюр или URL), показываем ProgressiveImage + (highQualityUrl != null && highQualityUrl.isNotEmpty) || + (lowQualityBytes != null) + ? _ProgressiveNetworkImage( + url: highQualityUrl ?? '', + previewBytes: lowQualityBytes, + width: 220, + height: 160, + fit: BoxFit.cover, + keepAlive: false, + ) + : Container( + color: Colors.black26, + child: const Center( + child: Icon( + Icons.video_library_outlined, + color: Colors.white, + size: 40, ), ), - Container( - decoration: BoxDecoration( - color: Colors.black.withOpacity(0.15), - ), - child: Icon( - Icons.play_circle_filled_outlined, - color: Colors.white.withOpacity(0.95), - size: 50, - shadows: const [ - Shadow( - color: Colors.black38, - blurRadius: 4, - offset: Offset(0, 2), - ), - ], - ), + ), + Container( + decoration: BoxDecoration( + color: Colors.black.withOpacity(0.15), ), - ], - ), + child: Icon( + Icons.play_circle_filled_outlined, + color: Colors.white.withOpacity(0.95), + size: 50, + shadows: const [ + Shadow( + color: Colors.black38, + blurRadius: 4, + offset: Offset(0, 2), + ), + ], + ), + ), + ], ), ), ), @@ -2005,16 +2004,13 @@ class ChatMessageBubble extends StatelessWidget { padding: const EdgeInsets.only(bottom: 4.0), child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 300), - child: RepaintBoundary( - key: ValueKey('video_preview_${message.id}_$videoId'), - child: _buildVideoPreview( - context: context, - videoId: videoId, - messageId: message.id, - highQualityUrl: highQualityThumbnailUrl, - lowQualityBytes: previewBytes, - videoType: videoType, - ), + child: _buildVideoPreview( + context: context, + videoId: videoId, + messageId: message.id, + highQualityUrl: highQualityThumbnailUrl, + lowQualityBytes: previewBytes, + videoType: videoType, ), ), ), @@ -2183,16 +2179,13 @@ class ChatMessageBubble extends StatelessWidget { widgets.add( Padding( padding: const EdgeInsets.only(bottom: 4.0), - child: RepaintBoundary( - key: ValueKey('video_preview_${message.id}_$videoId'), - child: _buildVideoPreview( - context: context, - videoId: videoId, - messageId: message.id, - highQualityUrl: highQualityThumbnailUrl, - lowQualityBytes: previewBytes, - videoType: videoType, - ), + child: _buildVideoPreview( + context: context, + videoId: videoId, + messageId: message.id, + highQualityUrl: highQualityThumbnailUrl, + lowQualityBytes: previewBytes, + videoType: videoType, ), ), ); diff --git a/lib/widgets/group_avatars.dart b/lib/widgets/group_avatars.dart index 450c4bf..ecefb6f 100644 --- a/lib/widgets/group_avatars.dart +++ b/lib/widgets/group_avatars.dart @@ -2,7 +2,6 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:gwid/models/chat.dart'; import 'package:gwid/models/contact.dart'; -import 'package:gwid/services/avatar_cache_service.dart'; class GroupAvatars extends StatelessWidget { final Chat chat; @@ -40,13 +39,13 @@ class GroupAvatars extends StatelessWidget { double adaptiveAvatarSize; if (totalParticipants <= 2) { adaptiveAvatarSize = - avatarSize * 1.5; + avatarSize * 1.5; // Большие аватары для 1-2 участников } else if (totalParticipants <= 4) { adaptiveAvatarSize = - avatarSize * 1.2; + avatarSize * 1.2; // Средние аватары для 3-4 участников } else { adaptiveAvatarSize = - avatarSize * 0.8; + avatarSize * 0.8; // Маленькие аватары для 5+ участников } return SizedBox( @@ -115,91 +114,45 @@ class GroupAvatars extends StatelessWidget { ) { final colors = Theme.of(context).colorScheme; - if (contact == null || contact.photoBaseUrl == null || contact.photoBaseUrl!.isEmpty) { - return Container( - width: size, - height: size, - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all(color: colors.surface, width: 2), - boxShadow: [ - BoxShadow( - color: colors.shadow.withOpacity(0.3), - blurRadius: 4, - offset: const Offset(0, 2), - ), - ], - ), - child: CircleAvatar( - radius: size / 2, - backgroundColor: contact != null - ? colors.primaryContainer - : colors.secondaryContainer, - child: Text( - contact?.name.isNotEmpty == true - ? contact!.name[0].toUpperCase() - : participantId.toString().substring( - participantId.toString().length - 1, - ), - style: TextStyle( - color: contact != null - ? colors.onPrimaryContainer - : colors.onSecondaryContainer, - fontSize: size * 0.5, - fontWeight: FontWeight.w600, - ), + return Container( + width: size, + height: size, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all(color: colors.surface, width: 2), + boxShadow: [ + BoxShadow( + color: colors.shadow.withOpacity(0.3), + blurRadius: 4, + offset: const Offset(0, 2), ), - ), - ); - } - - return FutureBuilder( - future: AvatarCacheService().getAvatar(contact.photoBaseUrl, userId: participantId), - builder: (context, snapshot) { - ImageProvider? imageProvider; - if (snapshot.hasData && snapshot.data != null) { - imageProvider = snapshot.data; - } else { - imageProvider = NetworkImage(contact.photoBaseUrl!); - } - - return Container( - width: size, - height: size, - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all(color: colors.surface, width: 2), - boxShadow: [ - BoxShadow( - color: colors.shadow.withOpacity(0.3), - blurRadius: 4, - offset: const Offset(0, 2), - ), - ], - ), - child: CircleAvatar( - radius: size / 2, - backgroundColor: colors.primaryContainer, - backgroundImage: imageProvider, - onBackgroundImageError: (exception, stackTrace) { - }, - child: imageProvider == null - ? Text( - contact.name.isNotEmpty - ? contact.name[0].toUpperCase() - : participantId.toString().substring( - participantId.toString().length - 1, - ), - style: TextStyle( - color: colors.onPrimaryContainer, - fontSize: size * 0.5, - fontWeight: FontWeight.w600, - ), - ) - : null, - ), - ); - }, + ], + ), + child: CircleAvatar( + radius: size / 2, + backgroundColor: contact != null + ? colors.primaryContainer + : colors.secondaryContainer, + backgroundImage: contact?.photoBaseUrl != null + ? NetworkImage(contact!.photoBaseUrl!) + : null, + child: contact?.photoBaseUrl == null + ? Text( + contact?.name.isNotEmpty == true + ? contact!.name[0].toUpperCase() + : participantId.toString().substring( + participantId.toString().length - 1, + ), // Последняя цифра ID + style: TextStyle( + color: contact != null + ? colors.onPrimaryContainer + : colors.onSecondaryContainer, + fontSize: size * 0.5, + fontWeight: FontWeight.w600, + ), + ) + : null, + ), ); }