Skip to content

Commit 54493d9

Browse files
committed
feat: switch ai model on mobile
1 parent f299737 commit 54493d9

File tree

3 files changed

+157
-28
lines changed

3 files changed

+157
-28
lines changed

frontend/appflowy_flutter/lib/mobile/presentation/home/mobile_home_setting_page.dart

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,19 @@ import 'package:appflowy/env/env.dart';
33
import 'package:appflowy/generated/locale_keys.g.dart';
44
import 'package:appflowy/mobile/presentation/base/app_bar/app_bar.dart';
55
import 'package:appflowy/mobile/presentation/presentation.dart';
6+
import 'package:appflowy/mobile/presentation/setting/ai/ai_settings_group.dart';
67
import 'package:appflowy/mobile/presentation/setting/cloud/cloud_setting_group.dart';
78
import 'package:appflowy/mobile/presentation/setting/user_session_setting_group.dart';
89
import 'package:appflowy/mobile/presentation/setting/workspace/workspace_setting_group.dart';
910
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
1011
import 'package:appflowy/startup/startup.dart';
1112
import 'package:appflowy/user/application/auth/auth_service.dart';
13+
import 'package:appflowy/workspace/application/user/user_workspace_bloc.dart';
1214
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
1315
import 'package:easy_localization/easy_localization.dart';
1416
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
1517
import 'package:flutter/material.dart';
18+
import 'package:flutter_bloc/flutter_bloc.dart';
1619

1720
class MobileHomeSettingPage extends StatefulWidget {
1821
const MobileHomeSettingPage({
@@ -70,29 +73,47 @@ class _MobileHomeSettingPageState extends State<MobileHomeSettingPage> {
7073
Widget _buildSettingsWidget(UserProfilePB userProfile) {
7174
// show the third-party sign in buttons if user logged in with local session and auth is enabled.
7275

73-
final showThirdPartyLogin =
76+
final isLocalAuthEnabled =
7477
userProfile.authenticator == AuthenticatorPB.Local && isAuthEnabled;
75-
return SingleChildScrollView(
76-
child: Padding(
77-
padding: const EdgeInsets.all(16),
78-
child: Column(
79-
children: [
80-
PersonalInfoSettingGroup(
81-
userProfile: userProfile,
82-
),
83-
const WorkspaceSettingGroup(),
84-
const AppearanceSettingGroup(),
85-
const LanguageSettingGroup(),
86-
if (Env.enableCustomCloud) const CloudSettingGroup(),
87-
const SupportSettingGroup(),
88-
const AboutSettingGroup(),
89-
UserSessionSettingGroup(
90-
userProfile: userProfile,
91-
showThirdPartyLogin: showThirdPartyLogin,
78+
'';
79+
80+
return BlocProvider(
81+
create: (context) => UserWorkspaceBloc(userProfile: userProfile)
82+
..add(const UserWorkspaceEvent.initial()),
83+
child: BlocBuilder<UserWorkspaceBloc, UserWorkspaceState>(
84+
builder: (context, state) {
85+
final currentWorkspaceId = state.currentWorkspace?.workspaceId ?? '';
86+
return SingleChildScrollView(
87+
child: Padding(
88+
padding: const EdgeInsets.all(16),
89+
child: Column(
90+
children: [
91+
PersonalInfoSettingGroup(
92+
userProfile: userProfile,
93+
),
94+
const WorkspaceSettingGroup(),
95+
const AppearanceSettingGroup(),
96+
const LanguageSettingGroup(),
97+
if (Env.enableCustomCloud) const CloudSettingGroup(),
98+
if (isAuthEnabled)
99+
AiSettingsGroup(
100+
key: ValueKey(currentWorkspaceId),
101+
userProfile: userProfile,
102+
workspaceId: currentWorkspaceId,
103+
currentWorkspaceMemberRole: state.currentWorkspace?.role,
104+
),
105+
const SupportSettingGroup(),
106+
const AboutSettingGroup(),
107+
UserSessionSettingGroup(
108+
userProfile: userProfile,
109+
showThirdPartyLogin: isLocalAuthEnabled,
110+
),
111+
const VSpace(20),
112+
],
113+
),
92114
),
93-
const VSpace(20),
94-
],
95-
),
115+
);
116+
},
96117
),
97118
);
98119
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import 'package:appflowy/generated/locale_keys.g.dart';
2+
import 'package:appflowy/mobile/presentation/bottom_sheet/show_mobile_bottom_sheet.dart';
3+
import 'package:appflowy/mobile/presentation/setting/widgets/mobile_setting_group_widget.dart';
4+
import 'package:appflowy/mobile/presentation/setting/widgets/mobile_setting_item_widget.dart';
5+
import 'package:appflowy/mobile/presentation/widgets/flowy_option_tile.dart';
6+
import 'package:appflowy/workspace/application/settings/ai/settings_ai_bloc.dart';
7+
import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart';
8+
import 'package:appflowy_backend/protobuf/flowy-user/workspace.pbenum.dart';
9+
import 'package:collection/collection.dart';
10+
import 'package:easy_localization/easy_localization.dart';
11+
import 'package:flowy_infra_ui/style_widget/text.dart';
12+
import 'package:flutter/material.dart';
13+
import 'package:flutter_bloc/flutter_bloc.dart';
14+
import 'package:go_router/go_router.dart';
15+
16+
class AiSettingsGroup extends StatelessWidget {
17+
const AiSettingsGroup({
18+
super.key,
19+
required this.userProfile,
20+
required this.workspaceId,
21+
this.currentWorkspaceMemberRole,
22+
});
23+
24+
final UserProfilePB userProfile;
25+
final String workspaceId;
26+
final AFRolePB? currentWorkspaceMemberRole;
27+
28+
@override
29+
Widget build(BuildContext context) {
30+
final theme = Theme.of(context);
31+
return BlocProvider(
32+
create: (context) => SettingsAIBloc(
33+
userProfile,
34+
workspaceId,
35+
currentWorkspaceMemberRole,
36+
)..add(const SettingsAIEvent.started()),
37+
child: BlocBuilder<SettingsAIBloc, SettingsAIState>(
38+
builder: (context, state) {
39+
return MobileSettingGroup(
40+
groupTitle: LocaleKeys.settings_aiPage_title.tr(),
41+
settingItemList: [
42+
MobileSettingItem(
43+
name: LocaleKeys.settings_aiPage_keys_llmModelType.tr(),
44+
trailing: Row(
45+
mainAxisSize: MainAxisSize.min,
46+
children: [
47+
FlowyText(
48+
state.selectedAIModel,
49+
color: theme.colorScheme.onSurface,
50+
),
51+
const Icon(Icons.chevron_right),
52+
],
53+
),
54+
onTap: () => _onLLMModelTypeTap(context, state),
55+
),
56+
// enable AI search if needed
57+
// MobileSettingItem(
58+
// name: LocaleKeys.settings_aiPage_keys_enableAISearchTitle.tr(),
59+
// trailing: const Icon(
60+
// Icons.chevron_right,
61+
// ),
62+
// onTap: () => context.push(AppFlowyCloudPage.routeName),
63+
// ),
64+
],
65+
);
66+
},
67+
),
68+
);
69+
}
70+
71+
void _onEnableAISearchTap(BuildContext context) {}
72+
73+
void _onLLMModelTypeTap(BuildContext context, SettingsAIState state) {
74+
final availableModels = state.availableModels;
75+
showMobileBottomSheet(
76+
context,
77+
showHeader: true,
78+
showDragHandle: true,
79+
showDivider: false,
80+
title: LocaleKeys.settings_aiPage_keys_llmModelType.tr(),
81+
builder: (_) {
82+
return Column(
83+
children: availableModels
84+
.mapIndexed(
85+
(index, model) => FlowyOptionTile.checkbox(
86+
text: model,
87+
showTopBorder: index == 0,
88+
isSelected: state.selectedAIModel == model,
89+
onTap: () {
90+
context
91+
.read<SettingsAIBloc>()
92+
.add(SettingsAIEvent.selectModel(model));
93+
context.pop();
94+
},
95+
),
96+
)
97+
.toList(),
98+
);
99+
},
100+
);
101+
}
102+
}

frontend/appflowy_flutter/lib/workspace/application/settings/ai/settings_ai_bloc.dart

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ class SettingsAIBloc extends Bloc<SettingsAIEvent, SettingsAIState> {
5757
}
5858

5959
void _dispatch() {
60-
on<SettingsAIEvent>((event, emit) {
61-
event.when(
60+
on<SettingsAIEvent>((event, emit) async {
61+
await event.when(
6262
started: () {
6363
_userListener.start(
6464
onProfileUpdated: _onProfileUpdated,
@@ -83,13 +83,14 @@ class SettingsAIBloc extends Bloc<SettingsAIEvent, SettingsAIState> {
8383
!(state.aiSettings?.disableSearchIndexing ?? false),
8484
);
8585
},
86-
selectModel: (String model) {
87-
_updateUserWorkspaceSetting(model: model);
86+
selectModel: (String model) async {
87+
await _updateUserWorkspaceSetting(model: model);
8888
},
8989
didLoadAISetting: (UseAISettingPB settings) {
9090
emit(
9191
state.copyWith(
9292
aiSettings: settings,
93+
selectedAIModel: settings.aiModel,
9394
enableSearchIndexing: !settings.disableSearchIndexing,
9495
),
9596
);
@@ -129,10 +130,10 @@ class SettingsAIBloc extends Bloc<SettingsAIEvent, SettingsAIState> {
129130
});
130131
}
131132

132-
void _updateUserWorkspaceSetting({
133+
Future<FlowyResult<void, FlowyError>> _updateUserWorkspaceSetting({
133134
bool? disableSearchIndexing,
134135
String? model,
135-
}) {
136+
}) async {
136137
final payload = UpdateUserWorkspaceSettingPB(
137138
workspaceId: workspaceId,
138139
);
@@ -142,7 +143,12 @@ class SettingsAIBloc extends Bloc<SettingsAIEvent, SettingsAIState> {
142143
if (model != null) {
143144
payload.aiModel = model;
144145
}
145-
UserEventUpdateWorkspaceSetting(payload).send();
146+
final result = await UserEventUpdateWorkspaceSetting(payload).send();
147+
result.fold(
148+
(ok) => Log.info('Update workspace setting success'),
149+
(err) => Log.error('Update workspace setting failed: $err'),
150+
);
151+
return result;
146152
}
147153

148154
void _onProfileUpdated(

0 commit comments

Comments
 (0)