mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-11-12 00:19:45 +00:00
feat: switch ai mode on mobile (#7391)
* fix: the default page name should be empty when creating * feat: switch ai model on mobile
This commit is contained in:
parent
15b4d496fd
commit
55fbb7522b
@ -3,16 +3,19 @@ import 'package:appflowy/env/env.dart';
|
|||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/mobile/presentation/base/app_bar/app_bar.dart';
|
import 'package:appflowy/mobile/presentation/base/app_bar/app_bar.dart';
|
||||||
import 'package:appflowy/mobile/presentation/presentation.dart';
|
import 'package:appflowy/mobile/presentation/presentation.dart';
|
||||||
|
import 'package:appflowy/mobile/presentation/setting/ai/ai_settings_group.dart';
|
||||||
import 'package:appflowy/mobile/presentation/setting/cloud/cloud_setting_group.dart';
|
import 'package:appflowy/mobile/presentation/setting/cloud/cloud_setting_group.dart';
|
||||||
import 'package:appflowy/mobile/presentation/setting/user_session_setting_group.dart';
|
import 'package:appflowy/mobile/presentation/setting/user_session_setting_group.dart';
|
||||||
import 'package:appflowy/mobile/presentation/setting/workspace/workspace_setting_group.dart';
|
import 'package:appflowy/mobile/presentation/setting/workspace/workspace_setting_group.dart';
|
||||||
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
|
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
|
||||||
import 'package:appflowy/startup/startup.dart';
|
import 'package:appflowy/startup/startup.dart';
|
||||||
import 'package:appflowy/user/application/auth/auth_service.dart';
|
import 'package:appflowy/user/application/auth/auth_service.dart';
|
||||||
|
import 'package:appflowy/workspace/application/user/user_workspace_bloc.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
class MobileHomeSettingPage extends StatefulWidget {
|
class MobileHomeSettingPage extends StatefulWidget {
|
||||||
const MobileHomeSettingPage({
|
const MobileHomeSettingPage({
|
||||||
@ -70,29 +73,47 @@ class _MobileHomeSettingPageState extends State<MobileHomeSettingPage> {
|
|||||||
Widget _buildSettingsWidget(UserProfilePB userProfile) {
|
Widget _buildSettingsWidget(UserProfilePB userProfile) {
|
||||||
// show the third-party sign in buttons if user logged in with local session and auth is enabled.
|
// show the third-party sign in buttons if user logged in with local session and auth is enabled.
|
||||||
|
|
||||||
final showThirdPartyLogin =
|
final isLocalAuthEnabled =
|
||||||
userProfile.authenticator == AuthenticatorPB.Local && isAuthEnabled;
|
userProfile.authenticator == AuthenticatorPB.Local && isAuthEnabled;
|
||||||
return SingleChildScrollView(
|
'';
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(16),
|
return BlocProvider(
|
||||||
child: Column(
|
create: (context) => UserWorkspaceBloc(userProfile: userProfile)
|
||||||
children: [
|
..add(const UserWorkspaceEvent.initial()),
|
||||||
PersonalInfoSettingGroup(
|
child: BlocBuilder<UserWorkspaceBloc, UserWorkspaceState>(
|
||||||
userProfile: userProfile,
|
builder: (context, state) {
|
||||||
|
final currentWorkspaceId = state.currentWorkspace?.workspaceId ?? '';
|
||||||
|
return SingleChildScrollView(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
PersonalInfoSettingGroup(
|
||||||
|
userProfile: userProfile,
|
||||||
|
),
|
||||||
|
const WorkspaceSettingGroup(),
|
||||||
|
const AppearanceSettingGroup(),
|
||||||
|
const LanguageSettingGroup(),
|
||||||
|
if (Env.enableCustomCloud) const CloudSettingGroup(),
|
||||||
|
if (isAuthEnabled)
|
||||||
|
AiSettingsGroup(
|
||||||
|
key: ValueKey(currentWorkspaceId),
|
||||||
|
userProfile: userProfile,
|
||||||
|
workspaceId: currentWorkspaceId,
|
||||||
|
currentWorkspaceMemberRole: state.currentWorkspace?.role,
|
||||||
|
),
|
||||||
|
const SupportSettingGroup(),
|
||||||
|
const AboutSettingGroup(),
|
||||||
|
UserSessionSettingGroup(
|
||||||
|
userProfile: userProfile,
|
||||||
|
showThirdPartyLogin: isLocalAuthEnabled,
|
||||||
|
),
|
||||||
|
const VSpace(20),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const WorkspaceSettingGroup(),
|
);
|
||||||
const AppearanceSettingGroup(),
|
},
|
||||||
const LanguageSettingGroup(),
|
|
||||||
if (Env.enableCustomCloud) const CloudSettingGroup(),
|
|
||||||
const SupportSettingGroup(),
|
|
||||||
const AboutSettingGroup(),
|
|
||||||
UserSessionSettingGroup(
|
|
||||||
userProfile: userProfile,
|
|
||||||
showThirdPartyLogin: showThirdPartyLogin,
|
|
||||||
),
|
|
||||||
const VSpace(20),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,106 @@
|
|||||||
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
|
import 'package:appflowy/mobile/presentation/bottom_sheet/show_mobile_bottom_sheet.dart';
|
||||||
|
import 'package:appflowy/mobile/presentation/setting/widgets/mobile_setting_group_widget.dart';
|
||||||
|
import 'package:appflowy/mobile/presentation/setting/widgets/mobile_setting_item_widget.dart';
|
||||||
|
import 'package:appflowy/mobile/presentation/widgets/flowy_option_tile.dart';
|
||||||
|
import 'package:appflowy/workspace/application/settings/ai/settings_ai_bloc.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-user/workspace.pbenum.dart';
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
|
class AiSettingsGroup extends StatelessWidget {
|
||||||
|
const AiSettingsGroup({
|
||||||
|
super.key,
|
||||||
|
required this.userProfile,
|
||||||
|
required this.workspaceId,
|
||||||
|
this.currentWorkspaceMemberRole,
|
||||||
|
});
|
||||||
|
|
||||||
|
final UserProfilePB userProfile;
|
||||||
|
final String workspaceId;
|
||||||
|
final AFRolePB? currentWorkspaceMemberRole;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => SettingsAIBloc(
|
||||||
|
userProfile,
|
||||||
|
workspaceId,
|
||||||
|
currentWorkspaceMemberRole,
|
||||||
|
)..add(const SettingsAIEvent.started()),
|
||||||
|
child: BlocBuilder<SettingsAIBloc, SettingsAIState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return MobileSettingGroup(
|
||||||
|
groupTitle: LocaleKeys.settings_aiPage_title.tr(),
|
||||||
|
settingItemList: [
|
||||||
|
MobileSettingItem(
|
||||||
|
name: LocaleKeys.settings_aiPage_keys_llmModelType.tr(),
|
||||||
|
trailing: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(maxWidth: 200),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: FlowyText(
|
||||||
|
state.selectedAIModel,
|
||||||
|
color: theme.colorScheme.onSurface,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Icon(Icons.chevron_right),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onTap: () => _onLLMModelTypeTap(context, state),
|
||||||
|
),
|
||||||
|
// enable AI search if needed
|
||||||
|
// MobileSettingItem(
|
||||||
|
// name: LocaleKeys.settings_aiPage_keys_enableAISearchTitle.tr(),
|
||||||
|
// trailing: const Icon(
|
||||||
|
// Icons.chevron_right,
|
||||||
|
// ),
|
||||||
|
// onTap: () => context.push(AppFlowyCloudPage.routeName),
|
||||||
|
// ),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onLLMModelTypeTap(BuildContext context, SettingsAIState state) {
|
||||||
|
final availableModels = state.availableModels;
|
||||||
|
showMobileBottomSheet(
|
||||||
|
context,
|
||||||
|
showHeader: true,
|
||||||
|
showDragHandle: true,
|
||||||
|
showDivider: false,
|
||||||
|
title: LocaleKeys.settings_aiPage_keys_llmModelType.tr(),
|
||||||
|
builder: (_) {
|
||||||
|
return Column(
|
||||||
|
children: availableModels
|
||||||
|
.mapIndexed(
|
||||||
|
(index, model) => FlowyOptionTile.checkbox(
|
||||||
|
text: model,
|
||||||
|
showTopBorder: index == 0,
|
||||||
|
isSelected: state.selectedAIModel == model,
|
||||||
|
onTap: () {
|
||||||
|
context
|
||||||
|
.read<SettingsAIBloc>()
|
||||||
|
.add(SettingsAIEvent.selectModel(model));
|
||||||
|
context.pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -91,7 +91,9 @@ class FavoriteBloc extends Bloc<FavoriteEvent, FavoriteState> {
|
|||||||
await _service.toggleFavorite(view.item.id);
|
await _service.toggleFavorite(view.item.id);
|
||||||
await _service.toggleFavorite(view.item.id);
|
await _service.toggleFavorite(view.item.id);
|
||||||
}
|
}
|
||||||
add(const FavoriteEvent.fetchFavorites());
|
if (!isClosed) {
|
||||||
|
add(const FavoriteEvent.fetchFavorites());
|
||||||
|
}
|
||||||
isReordering = false;
|
isReordering = false;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@ -57,8 +57,8 @@ class SettingsAIBloc extends Bloc<SettingsAIEvent, SettingsAIState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _dispatch() {
|
void _dispatch() {
|
||||||
on<SettingsAIEvent>((event, emit) {
|
on<SettingsAIEvent>((event, emit) async {
|
||||||
event.when(
|
await event.when(
|
||||||
started: () {
|
started: () {
|
||||||
_userListener.start(
|
_userListener.start(
|
||||||
onProfileUpdated: _onProfileUpdated,
|
onProfileUpdated: _onProfileUpdated,
|
||||||
@ -83,13 +83,14 @@ class SettingsAIBloc extends Bloc<SettingsAIEvent, SettingsAIState> {
|
|||||||
!(state.aiSettings?.disableSearchIndexing ?? false),
|
!(state.aiSettings?.disableSearchIndexing ?? false),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
selectModel: (String model) {
|
selectModel: (String model) async {
|
||||||
_updateUserWorkspaceSetting(model: model);
|
await _updateUserWorkspaceSetting(model: model);
|
||||||
},
|
},
|
||||||
didLoadAISetting: (UseAISettingPB settings) {
|
didLoadAISetting: (UseAISettingPB settings) {
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
aiSettings: settings,
|
aiSettings: settings,
|
||||||
|
selectedAIModel: settings.aiModel,
|
||||||
enableSearchIndexing: !settings.disableSearchIndexing,
|
enableSearchIndexing: !settings.disableSearchIndexing,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -129,10 +130,10 @@ class SettingsAIBloc extends Bloc<SettingsAIEvent, SettingsAIState> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateUserWorkspaceSetting({
|
Future<FlowyResult<void, FlowyError>> _updateUserWorkspaceSetting({
|
||||||
bool? disableSearchIndexing,
|
bool? disableSearchIndexing,
|
||||||
String? model,
|
String? model,
|
||||||
}) {
|
}) async {
|
||||||
final payload = UpdateUserWorkspaceSettingPB(
|
final payload = UpdateUserWorkspaceSettingPB(
|
||||||
workspaceId: workspaceId,
|
workspaceId: workspaceId,
|
||||||
);
|
);
|
||||||
@ -142,7 +143,12 @@ class SettingsAIBloc extends Bloc<SettingsAIEvent, SettingsAIState> {
|
|||||||
if (model != null) {
|
if (model != null) {
|
||||||
payload.aiModel = model;
|
payload.aiModel = model;
|
||||||
}
|
}
|
||||||
UserEventUpdateWorkspaceSetting(payload).send();
|
final result = await UserEventUpdateWorkspaceSetting(payload).send();
|
||||||
|
result.fold(
|
||||||
|
(ok) => Log.info('Update workspace setting success'),
|
||||||
|
(err) => Log.error('Update workspace setting failed: $err'),
|
||||||
|
);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onProfileUpdated(
|
void _onProfileUpdated(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user