mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
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:
parent
d0ce65f711
commit
e460120a1c
@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -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(),
|
||||||
|
@ -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(
|
||||||
|
Loading…
Reference in New Issue
Block a user