diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/chat.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/chat.dart index cdb183ae91..c34b0abcfe 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/chat.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/chat.dart @@ -118,4 +118,7 @@ class AIChatPagePluginWidgetBuilder extends PluginWidgetBuilder @override List get navigationItems => [this]; + + @override + EdgeInsets get contentPadding => EdgeInsets.zero; } diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/chat_page.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/chat_page.dart index 1a7393a0a9..c6a5e832c3 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/chat_page.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/chat_page.dart @@ -1,3 +1,6 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/plugins/ai_chat/application/chat_bloc.dart'; import 'package:appflowy/plugins/ai_chat/presentation/ai_message_bubble.dart'; @@ -10,12 +13,10 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra/theme_extension.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_chat_types/flutter_chat_types.dart'; -import 'package:flutter_chat_ui/flutter_chat_ui.dart' show Chat; import 'package:flutter_chat_types/flutter_chat_types.dart' as types; +import 'package:flutter_chat_ui/flutter_chat_ui.dart' show Chat; import 'presentation/chat_input.dart'; import 'presentation/chat_popmenu.dart'; @@ -27,11 +28,11 @@ import 'presentation/message/user_text_message.dart'; class AIChatUILayout { static EdgeInsets get chatPadding => - isMobile ? EdgeInsets.zero : const EdgeInsets.symmetric(horizontal: 70); + isMobile ? EdgeInsets.zero : const EdgeInsets.symmetric(horizontal: 20); static EdgeInsets get welcomePagePadding => isMobile ? const EdgeInsets.symmetric(horizontal: 20) - : const EdgeInsets.symmetric(horizontal: 100); + : const EdgeInsets.symmetric(horizontal: 50); static double get messageWidthRatio => 0.85; @@ -44,7 +45,8 @@ class AIChatUILayout { query.padding.right, query.viewInsets.bottom + query.padding.bottom, ) - : const EdgeInsets.symmetric(horizontal: 70); + : const EdgeInsets.symmetric(horizontal: 50) + + const EdgeInsets.only(bottom: 20); } } @@ -77,92 +79,89 @@ class _AIChatPageState extends State { Widget build(BuildContext context) { if (widget.userProfile.authenticator == AuthenticatorPB.AppFlowyCloud) { return buildChatWidget(); - } else { - return Center( - child: FlowyText( - LocaleKeys.chat_unsupportedCloudPrompt.tr(), - fontSize: 20, - ), - ); } + + return Center( + child: FlowyText( + LocaleKeys.chat_unsupportedCloudPrompt.tr(), + fontSize: 20, + ), + ); } Widget buildChatWidget() { - return SizedBox.expand( - child: Padding( - padding: AIChatUILayout.chatPadding, - child: BlocProvider( - create: (context) => ChatBloc( - view: widget.view, - userProfile: widget.userProfile, - )..add(const ChatEvent.initialLoad()), - child: BlocBuilder( - builder: (blocContext, state) { - return Chat( - messages: state.messages, - onAttachmentPressed: () {}, - onSendPressed: (types.PartialText message) { - // We use custom bottom widget for chat input, so - // do not need to handle this event. - }, - customBottomWidget: buildChatInput(blocContext), - user: _user, - theme: buildTheme(context), - onEndReached: () async { - if (state.hasMorePrevMessage && - state.loadingPreviousStatus != - const LoadingState.loading()) { - blocContext - .read() - .add(const ChatEvent.startLoadingPrevMessage()); - } - }, - emptyState: BlocBuilder( - builder: (context, state) { - return state.initialLoadingStatus == + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Flexible( + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 784), + child: BlocProvider( + create: (_) => ChatBloc( + view: widget.view, + userProfile: widget.userProfile, + )..add(const ChatEvent.initialLoad()), + child: BlocBuilder( + builder: (blocContext, state) => Chat( + messages: state.messages, + onSendPressed: (_) { + // We use custom bottom widget for chat input, so + // do not need to handle this event. + }, + customBottomWidget: buildChatInput(blocContext), + user: _user, + theme: buildTheme(context), + onEndReached: () async { + if (state.hasMorePrevMessage && + state.loadingPreviousStatus != + const LoadingState.loading()) { + blocContext + .read() + .add(const ChatEvent.startLoadingPrevMessage()); + } + }, + emptyState: BlocBuilder( + builder: (_, state) => state.initialLoadingStatus == const LoadingState.finish() ? Padding( padding: AIChatUILayout.welcomePagePadding, child: ChatWelcomePage( - onSelectedQuestion: (question) { - blocContext - .read() - .add(ChatEvent.sendMessage(question)); - }, + onSelectedQuestion: (question) => blocContext + .read() + .add(ChatEvent.sendMessage(question)), ), ) : const Center( child: CircularProgressIndicator.adaptive(), - ); + ), + ), + messageWidthRatio: AIChatUILayout.messageWidthRatio, + textMessageBuilder: ( + textMessage, { + required messageWidth, + required showName, + }) => + _buildAITextMessage(blocContext, textMessage), + bubbleBuilder: ( + child, { + required message, + required nextMessageInGroup, + }) { + if (message.author.id == _user.id) { + return ChatUserMessageBubble( + message: message, + child: child, + ); + } + + return _buildAIBubble(message, blocContext, state, child); }, ), - messageWidthRatio: AIChatUILayout.messageWidthRatio, - textMessageBuilder: ( - textMessage, { - required messageWidth, - required showName, - }) { - return _buildAITextMessage(blocContext, textMessage); - }, - bubbleBuilder: ( - child, { - required message, - required nextMessageInGroup, - }) { - if (message.author.id == _user.id) { - return ChatUserMessageBubble( - message: message, - child: child, - ); - } else { - return _buildAIBubble(message, blocContext, state, child); - } - }, - ); - }, + ), + ), ), ), - ), + ], ); } diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_welcome_page.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_welcome_page.dart index 5aceca2025..48c69eef97 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_welcome_page.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_welcome_page.dart @@ -1,9 +1,10 @@ +import 'package:flutter/material.dart'; + import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; -import 'package:flutter/material.dart'; import 'chat_input.dart'; @@ -31,19 +32,15 @@ class ChatWelcomePage extends StatelessWidget { size: Size.square(44), ), const SizedBox(height: 40), - GridView.builder( - shrinkWrap: true, - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: isMobile ? 2 : 4, - crossAxisSpacing: 6, - mainAxisSpacing: 6, - childAspectRatio: 16.0 / 9.0, - ), - itemCount: items.length, - itemBuilder: (context, index) => WelcomeQuestion( - question: items[index], - onSelected: onSelectedQuestion, - ), + Wrap( + children: items + .map( + (i) => WelcomeQuestion( + question: i, + onSelected: onSelectedQuestion, + ), + ) + .toList(), ), ], ), diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_ai_view.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_ai_view.dart index 25f1005fc8..a047afff0f 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_ai_view.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_ai_view.dart @@ -144,7 +144,7 @@ class _AISearchToggle extends StatelessWidget { child: SizedBox( height: 26, width: 26, - child: CircularProgressIndicator.adaptive(strokeWidth: 4), + child: CircularProgressIndicator.adaptive(), ), ); } else {