feat: add ai bubble button on mobile home page (#5992)

* chore: skip check list test if the task is not found

* feat: add ai bubble button in home page

* feat: only show the ai bubble button for the cloud user

* chore: add border color to ai bubble button

* Revert "chore: skip check list test if the task is not found"

This reverts commit 961f594a31.

* fix: only display ai bubble button on home page
This commit is contained in:
Lucas.Xu 2024-08-19 09:50:42 +08:00 committed by GitHub
parent d0ce65f711
commit e460120a1c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 197 additions and 75 deletions

View File

@ -134,7 +134,27 @@ class _MobileHomePageState extends State<MobileHomePage> {
value: getIt<ReminderBloc>()..add(const ReminderEvent.started()), value: getIt<ReminderBloc>()..add(const ReminderEvent.started()),
), ),
], ],
child: BlocConsumer<UserWorkspaceBloc, UserWorkspaceState>( child: _HomePage(userProfile: widget.userProfile),
);
}
void _onLatestViewChange() async {
final id = getIt<MenuSharedState>().latestOpenView?.id;
if (id == null) {
return;
}
await FolderEventSetLatestView(ViewIdPB(value: id)).send();
}
}
class _HomePage extends StatelessWidget {
const _HomePage({required this.userProfile});
final UserProfilePB userProfile;
@override
Widget build(BuildContext context) {
return BlocConsumer<UserWorkspaceBloc, UserWorkspaceState>(
buildWhen: (previous, current) => buildWhen: (previous, current) =>
previous.currentWorkspace?.workspaceId != previous.currentWorkspace?.workspaceId !=
current.currentWorkspace?.workspaceId, current.currentWorkspace?.workspaceId,
@ -159,7 +179,7 @@ class _MobileHomePageState extends State<MobileHomePage> {
top: Platform.isAndroid ? 8.0 : 0.0, top: Platform.isAndroid ? 8.0 : 0.0,
), ),
child: MobileHomePageHeader( child: MobileHomePageHeader(
userProfile: widget.userProfile, userProfile: userProfile,
), ),
), ),
@ -167,14 +187,14 @@ class _MobileHomePageState extends State<MobileHomePage> {
child: MultiBlocProvider( child: MultiBlocProvider(
providers: [ providers: [
BlocProvider( BlocProvider(
create: (_) => SpaceOrderBloc() create: (_) =>
..add(const SpaceOrderEvent.initial()), SpaceOrderBloc()..add(const SpaceOrderEvent.initial()),
), ),
BlocProvider( BlocProvider(
create: (_) => SidebarSectionsBloc() create: (_) => SidebarSectionsBloc()
..add( ..add(
SidebarSectionsEvent.initial( SidebarSectionsEvent.initial(
widget.userProfile, userProfile,
workspaceId, workspaceId,
), ),
), ),
@ -187,7 +207,7 @@ class _MobileHomePageState extends State<MobileHomePage> {
create: (_) => SpaceBloc() create: (_) => SpaceBloc()
..add( ..add(
SpaceEvent.initial( SpaceEvent.initial(
widget.userProfile, userProfile,
workspaceId, workspaceId,
openFirstPage: false, openFirstPage: false,
), ),
@ -195,22 +215,13 @@ class _MobileHomePageState extends State<MobileHomePage> {
), ),
], ],
child: MobileSpaceTab( child: MobileSpaceTab(
userProfile: widget.userProfile, userProfile: userProfile,
), ),
), ),
), ),
], ],
); );
}, },
),
); );
} }
void _onLatestViewChange() async {
final id = getIt<MenuSharedState>().latestOpenView?.id;
if (id == null) {
return;
}
await FolderEventSetLatestView(ViewIdPB(value: id)).send();
}
} }

View File

@ -0,0 +1,81 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/base/gesture.dart';
import 'package:appflowy/mobile/presentation/home/tab/mobile_space_tab.dart';
import 'package:appflowy/util/theme_extension.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
class FloatingAIEntry extends StatelessWidget {
const FloatingAIEntry({super.key});
@override
Widget build(BuildContext context) {
return AnimatedGestureDetector(
scaleFactor: 0.99,
onTapUp: () => mobileCreateNewAIChatNotifier.value =
mobileCreateNewAIChatNotifier.value + 1,
child: DecoratedBox(
decoration: _buildShadowDecoration(context),
child: Container(
decoration: _buildWrapperDecoration(context),
height: 48,
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.only(left: 18),
child: _buildHintText(context),
),
),
),
);
}
BoxDecoration _buildShadowDecoration(BuildContext context) {
return BoxDecoration(
borderRadius: BorderRadius.circular(30),
boxShadow: [
BoxShadow(
blurRadius: 20,
spreadRadius: 1,
offset: const Offset(0, 4),
color: Colors.black.withOpacity(0.05),
),
],
);
}
BoxDecoration _buildWrapperDecoration(BuildContext context) {
final outlineColor = Theme.of(context).colorScheme.outline;
final borderColor = Theme.of(context).isLightMode
? outlineColor.withOpacity(0.7)
: outlineColor.withOpacity(0.3);
return BoxDecoration(
borderRadius: BorderRadius.circular(30),
color: Theme.of(context).colorScheme.surface,
border: Border.fromBorderSide(
BorderSide(
color: borderColor,
),
),
);
}
Widget _buildHintText(BuildContext context) {
return Row(
children: [
FlowySvg(
FlowySvgs.toolbar_item_ai_s,
size: const Size.square(16.0),
color: Theme.of(context).hintColor,
opacity: 0.7,
),
const HSpace(8),
FlowyText(
LocaleKeys.chat_inputMessageHint.tr(),
color: Theme.of(context).hintColor,
),
],
);
}
}

View File

@ -20,6 +20,10 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'ai_bubble_button.dart';
final ValueNotifier<int> mobileCreateNewAIChatNotifier = ValueNotifier(0);
class MobileSpaceTab extends StatefulWidget { class MobileSpaceTab extends StatefulWidget {
const MobileSpaceTab({ const MobileSpaceTab({
super.key, super.key,
@ -40,7 +44,8 @@ class _MobileSpaceTabState extends State<MobileSpaceTab>
void initState() { void initState() {
super.initState(); super.initState();
mobileCreateNewPageNotifier.addListener(_createNewPage); mobileCreateNewPageNotifier.addListener(_createNewDocument);
mobileCreateNewAIChatNotifier.addListener(_createNewAIChat);
mobileLeaveWorkspaceNotifier.addListener(_leaveWorkspace); mobileLeaveWorkspaceNotifier.addListener(_leaveWorkspace);
} }
@ -48,7 +53,9 @@ class _MobileSpaceTabState extends State<MobileSpaceTab>
void dispose() { void dispose() {
tabController?.removeListener(_onTabChange); tabController?.removeListener(_onTabChange);
tabController?.dispose(); tabController?.dispose();
mobileCreateNewPageNotifier.removeListener(_createNewPage);
mobileCreateNewPageNotifier.removeListener(_createNewDocument);
mobileCreateNewAIChatNotifier.removeListener(_createNewAIChat);
mobileLeaveWorkspaceNotifier.removeListener(_leaveWorkspace); mobileLeaveWorkspaceNotifier.removeListener(_leaveWorkspace);
super.dispose(); super.dispose();
@ -145,7 +152,20 @@ class _MobileSpaceTabState extends State<MobileSpaceTab>
case MobileSpaceTabType.recent: case MobileSpaceTabType.recent:
return const MobileRecentSpace(); return const MobileRecentSpace();
case MobileSpaceTabType.spaces: case MobileSpaceTabType.spaces:
return MobileHomeSpace(userProfile: widget.userProfile); return Stack(
children: [
MobileHomeSpace(userProfile: widget.userProfile),
// only show ai chat button for cloud user
if (widget.userProfile.authenticator ==
AuthenticatorPB.AppFlowyCloud)
Positioned(
bottom: MediaQuery.of(context).padding.bottom + 16,
left: 20,
right: 20,
child: const FloatingAIEntry(),
),
],
);
case MobileSpaceTabType.favorites: case MobileSpaceTabType.favorites:
return MobileFavoriteSpace(userProfile: widget.userProfile); return MobileFavoriteSpace(userProfile: widget.userProfile);
default: default:
@ -155,15 +175,24 @@ class _MobileSpaceTabState extends State<MobileSpaceTab>
} }
// quick create new page when clicking the add button in navigation bar // quick create new page when clicking the add button in navigation bar
void _createNewPage() { void _createNewDocument() {
_createNewPage(ViewLayoutPB.Document);
}
void _createNewAIChat() {
_createNewPage(ViewLayoutPB.Chat);
}
void _createNewPage(ViewLayoutPB layout) {
if (context.read<SpaceBloc>().state.spaces.isNotEmpty) { if (context.read<SpaceBloc>().state.spaces.isNotEmpty) {
context.read<SpaceBloc>().add( context.read<SpaceBloc>().add(
SpaceEvent.createPage( SpaceEvent.createPage(
name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(), name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
layout: ViewLayoutPB.Document, layout: layout,
), ),
); );
} else { } else if (layout == ViewLayoutPB.Document) {
// only support create document in section
context.read<SidebarSectionsBloc>().add( context.read<SidebarSectionsBloc>().add(
SidebarSectionsEvent.createRootViewInSection( SidebarSectionsEvent.createRootViewInSection(
name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(), name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),

View File

@ -73,6 +73,7 @@ class ChatWelcomePage extends StatelessWidget {
const VSpace(8), const VSpace(8),
Wrap( Wrap(
direction: Axis.vertical, direction: Axis.vertical,
spacing: isMobile ? 12.0 : 0.0,
children: items children: items
.map( .map(
(i) => WelcomeQuestionWidget( (i) => WelcomeQuestionWidget(