From d7a67c0efbb707508a59a8a7047462b97668e54b Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Thu, 30 Nov 2023 16:56:44 +0800 Subject: [PATCH] feat: support editing database icon (#4052) --- .../page_item/mobile_view_item.dart | 4 +- .../tab_bar/mobile/mobile_tab_bar_header.dart | 87 ++++++++++++- .../lib/style_widget/text_field.dart | 114 +++++++++--------- 3 files changed, 143 insertions(+), 62 deletions(-) diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/page_item/mobile_view_item.dart b/frontend/appflowy_flutter/lib/mobile/presentation/page_item/mobile_view_item.dart index 3b1cb98b3e..310922977e 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/page_item/mobile_view_item.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/page_item/mobile_view_item.dart @@ -304,7 +304,7 @@ class _SingleMobileInnerViewItemState extends State { _buildLeftIcon(), const HSpace(4), // icon - _buildViewIconButton(), + _buildViewIcon(), const HSpace(8), // title Expanded( @@ -354,7 +354,7 @@ class _SingleMobileInnerViewItemState extends State { return child; } - Widget _buildViewIconButton() { + Widget _buildViewIcon() { final icon = widget.view.icon.value.isNotEmpty ? EmojiText( emoji: widget.view.icon.value, diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/tab_bar/mobile/mobile_tab_bar_header.dart b/frontend/appflowy_flutter/lib/plugins/database_view/tab_bar/mobile/mobile_tab_bar_header.dart index d95231dcc5..96d34953f0 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/tab_bar/mobile/mobile_tab_bar_header.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/tab_bar/mobile/mobile_tab_bar_header.dart @@ -1,13 +1,36 @@ +import 'package:appflowy/plugins/base/emoji/emoji_picker_screen.dart'; +import 'package:appflowy/plugins/base/emoji/emoji_text.dart'; +import 'package:appflowy/plugins/base/icon/icon_picker.dart'; import 'package:appflowy/plugins/database_view/application/tab_bar_bloc.dart'; import 'package:appflowy/plugins/database_view/grid/presentation/grid_page.dart'; import 'package:appflowy/plugins/database_view/widgets/setting/mobile_database_settings_button.dart'; +import 'package:appflowy/workspace/application/view/view_bloc.dart'; +import 'package:appflowy/workspace/application/view/view_ext.dart'; +import 'package:appflowy/workspace/application/view/view_service.dart'; +import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart'; import 'package:collection/collection.dart'; +import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; -class MobileTabBarHeader extends StatelessWidget { +class MobileTabBarHeader extends StatefulWidget { const MobileTabBarHeader({super.key}); + @override + State createState() => _MobileTabBarHeaderState(); +} + +class _MobileTabBarHeaderState extends State { + final controller = TextEditingController(); + + @override + void dispose() { + controller.dispose(); + + super.dispose(); + } + @override Widget build(BuildContext context) { return BlocBuilder( @@ -20,6 +43,8 @@ class MobileTabBarHeader extends StatelessWidget { return const SizedBox.shrink(); } + controller.text = currentView.view.name; + return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -27,11 +52,31 @@ class MobileTabBarHeader extends StatelessWidget { padding: const EdgeInsets.symmetric(vertical: 14), child: Row( children: [ + _buildViewIconButton(currentView.view), + const HSpace(8.0), Expanded( - child: Text( - currentView.view.name, - style: Theme.of(context).textTheme.titleLarge, - overflow: TextOverflow.ellipsis, + child: FlowyTextField( + autoFocus: false, + maxLines: null, + controller: controller, + textAlignVertical: TextAlignVertical.top, + decoration: const InputDecoration( + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + contentPadding: EdgeInsets.zero, + ), + textStyle: Theme.of(context).textTheme.titleLarge, + onSubmitted: (value) { + if (value.isNotEmpty) { + context.read().add( + ViewEvent.rename(value), + ); + } + }, + onCanceled: () { + controller.text = currentView.view.name; + }, ), ), MobileDatabaseSettingsButton( @@ -43,10 +88,40 @@ class MobileTabBarHeader extends StatelessWidget { ], ), ), - const Divider(height: 1, thickness: 1), + const Divider( + height: 1, + thickness: 1, + ), ], ); }, ); } + + Widget _buildViewIconButton(ViewPB view) { + final icon = view.icon.value.isNotEmpty + ? EmojiText( + emoji: view.icon.value, + fontSize: 24.0, + ) + : SizedBox.square( + dimension: 26.0, + child: view.defaultIcon(), + ); + return FlowyButton( + text: icon, + useIntrinsicWidth: true, + onTap: () async { + final result = await context.push( + MobileEmojiPickerScreen.routeName, + ); + if (context.mounted && result != null) { + await ViewBackendService.updateViewIcon( + viewId: view.id, + viewIcon: result.emoji, + ); + } + }, + ); + } } diff --git a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/text_field.dart b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/text_field.dart index dc07eab03c..5289edb89d 100644 --- a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/text_field.dart +++ b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/text_field.dart @@ -20,7 +20,7 @@ class FlowyTextField extends StatefulWidget { final bool submitOnLeave; final Duration? debounceDuration; final String? errorText; - final int maxLines; + final int? maxLines; final bool showCounter; final Widget? prefixIcon; final Widget? suffixIcon; @@ -28,6 +28,8 @@ class FlowyTextField extends StatefulWidget { final BoxConstraints? suffixIconConstraints; final BoxConstraints? hintTextConstraints; final TextStyle? hintStyle; + final InputDecoration? decoration; + final TextAlignVertical? textAlignVertical; const FlowyTextField({ super.key, @@ -54,6 +56,8 @@ class FlowyTextField extends StatefulWidget { this.suffixIconConstraints, this.hintTextConstraints, this.hintStyle, + this.decoration, + this.textAlignVertical, }); @override @@ -130,64 +134,66 @@ class FlowyTextFieldState extends State { maxLength: widget.maxLength, maxLengthEnforcement: MaxLengthEnforcement.truncateAfterCompositionEnds, style: widget.textStyle ?? Theme.of(context).textTheme.bodySmall, - textAlignVertical: TextAlignVertical.center, + textAlignVertical: widget.textAlignVertical ?? TextAlignVertical.center, keyboardType: TextInputType.multiline, - decoration: InputDecoration( - constraints: widget.hintTextConstraints ?? - BoxConstraints( - maxHeight: widget.errorText?.isEmpty ?? true ? 32 : 58, + decoration: widget.decoration ?? + InputDecoration( + constraints: widget.hintTextConstraints ?? + BoxConstraints( + maxHeight: widget.errorText?.isEmpty ?? true ? 32 : 58, + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 12, + vertical: + (widget.maxLines == null || widget.maxLines! > 1) ? 12 : 0, ), - contentPadding: EdgeInsets.symmetric( - horizontal: 12, - vertical: widget.maxLines > 1 ? 12 : 0, - ), - enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: Theme.of(context).colorScheme.outline, - width: 1.0, - ), - borderRadius: Corners.s8Border, - ), - isDense: false, - hintText: widget.hintText, - errorText: widget.errorText, - errorStyle: Theme.of(context) - .textTheme - .bodySmall! - .copyWith(color: Theme.of(context).colorScheme.error), - hintStyle: widget.hintStyle ?? - Theme.of(context) + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Theme.of(context).colorScheme.outline, + width: 1.0, + ), + borderRadius: Corners.s8Border, + ), + isDense: false, + hintText: widget.hintText, + errorText: widget.errorText, + errorStyle: Theme.of(context) .textTheme .bodySmall! - .copyWith(color: Theme.of(context).hintColor), - suffixText: widget.showCounter ? _suffixText() : "", - counterText: "", - focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: Theme.of(context).colorScheme.primary, - width: 1.0, + .copyWith(color: Theme.of(context).colorScheme.error), + hintStyle: widget.hintStyle ?? + Theme.of(context) + .textTheme + .bodySmall! + .copyWith(color: Theme.of(context).hintColor), + suffixText: widget.showCounter ? _suffixText() : "", + counterText: "", + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Theme.of(context).colorScheme.primary, + width: 1.0, + ), + borderRadius: Corners.s8Border, + ), + errorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Theme.of(context).colorScheme.error, + width: 1.0, + ), + borderRadius: Corners.s8Border, + ), + focusedErrorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Theme.of(context).colorScheme.error, + width: 1.0, + ), + borderRadius: Corners.s8Border, + ), + prefixIcon: widget.prefixIcon, + suffixIcon: widget.suffixIcon, + prefixIconConstraints: widget.prefixIconConstraints, + suffixIconConstraints: widget.suffixIconConstraints, ), - borderRadius: Corners.s8Border, - ), - errorBorder: OutlineInputBorder( - borderSide: BorderSide( - color: Theme.of(context).colorScheme.error, - width: 1.0, - ), - borderRadius: Corners.s8Border, - ), - focusedErrorBorder: OutlineInputBorder( - borderSide: BorderSide( - color: Theme.of(context).colorScheme.error, - width: 1.0, - ), - borderRadius: Corners.s8Border, - ), - prefixIcon: widget.prefixIcon, - suffixIcon: widget.suffixIcon, - prefixIconConstraints: widget.prefixIconConstraints, - suffixIconConstraints: widget.suffixIconConstraints, - ), ); }