mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-11-02 02:53:59 +00:00
chore: adjust ui
This commit is contained in:
parent
9291236733
commit
846172a709
@ -25,6 +25,7 @@ class SearchResultListBloc
|
||||
state.copyWith(
|
||||
hoveredSummary: event.summary,
|
||||
hoveredResult: null,
|
||||
userHovered: event.userHovered,
|
||||
openPageId: null,
|
||||
),
|
||||
);
|
||||
@ -38,6 +39,7 @@ class SearchResultListBloc
|
||||
state.copyWith(
|
||||
hoveredSummary: null,
|
||||
hoveredResult: event.item,
|
||||
userHovered: event.userHovered,
|
||||
openPageId: null,
|
||||
),
|
||||
);
|
||||
@ -55,9 +57,11 @@ class SearchResultListBloc
|
||||
class SearchResultListEvent with _$SearchResultListEvent {
|
||||
const factory SearchResultListEvent.onHoverSummary({
|
||||
required SearchSummaryPB summary,
|
||||
required bool userHovered,
|
||||
}) = _OnHoverSummary;
|
||||
const factory SearchResultListEvent.onHoverResult({
|
||||
required SearchResultItem item,
|
||||
required bool userHovered,
|
||||
}) = _OnHoverResult;
|
||||
|
||||
const factory SearchResultListEvent.openPage({
|
||||
@ -72,6 +76,7 @@ class SearchResultListState with _$SearchResultListState {
|
||||
@Default(null) SearchSummaryPB? hoveredSummary,
|
||||
@Default(null) SearchResultItem? hoveredResult,
|
||||
@Default(null) String? openPageId,
|
||||
@Default(false) bool userHovered,
|
||||
}) = _SearchResultListState;
|
||||
|
||||
factory SearchResultListState.initial() => const SearchResultListState();
|
||||
|
||||
@ -17,10 +17,12 @@ class SearchResultCell extends StatefulWidget {
|
||||
super.key,
|
||||
required this.item,
|
||||
this.isTrashed = false,
|
||||
this.isHovered = false,
|
||||
});
|
||||
|
||||
final SearchResultItem item;
|
||||
final bool isTrashed;
|
||||
final bool isHovered;
|
||||
|
||||
@override
|
||||
State<SearchResultCell> createState() => _SearchResultCellState();
|
||||
@ -142,7 +144,10 @@ class _SearchResultCellState extends State<SearchResultCell> {
|
||||
onFocusChange: (hasFocus) {
|
||||
setState(() {
|
||||
context.read<SearchResultListBloc>().add(
|
||||
SearchResultListEvent.onHoverResult(item: widget.item),
|
||||
SearchResultListEvent.onHoverResult(
|
||||
item: widget.item,
|
||||
userHovered: true,
|
||||
),
|
||||
);
|
||||
_hasFocus = hasFocus;
|
||||
});
|
||||
@ -150,10 +155,13 @@ class _SearchResultCellState extends State<SearchResultCell> {
|
||||
child: FlowyHover(
|
||||
onHover: (value) {
|
||||
context.read<SearchResultListBloc>().add(
|
||||
SearchResultListEvent.onHoverResult(item: widget.item),
|
||||
SearchResultListEvent.onHoverResult(
|
||||
item: widget.item,
|
||||
userHovered: true,
|
||||
),
|
||||
);
|
||||
},
|
||||
isSelected: () => _hasFocus,
|
||||
isSelected: () => _hasFocus || widget.isHovered,
|
||||
style: HoverStyle(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
hoverColor:
|
||||
|
||||
@ -16,7 +16,7 @@ import 'package:flutter_animate/flutter_animate.dart';
|
||||
import 'search_result_cell.dart';
|
||||
import 'search_summary_cell.dart';
|
||||
|
||||
class SearchResultList extends StatelessWidget {
|
||||
class SearchResultList extends StatefulWidget {
|
||||
const SearchResultList({
|
||||
required this.trash,
|
||||
required this.resultItems,
|
||||
@ -27,6 +27,26 @@ class SearchResultList extends StatelessWidget {
|
||||
final List<TrashPB> trash;
|
||||
final List<SearchResultItem> resultItems;
|
||||
final List<SearchSummaryPB> resultSummaries;
|
||||
|
||||
@override
|
||||
State<SearchResultList> createState() => _SearchResultListState();
|
||||
}
|
||||
|
||||
class _SearchResultListState extends State<SearchResultList> {
|
||||
late final SearchResultListBloc bloc;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
bloc = SearchResultListBloc();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
bloc.close();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Widget _buildSectionHeader(String title) => Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8) +
|
||||
const EdgeInsets.only(left: 8),
|
||||
@ -48,7 +68,21 @@ class SearchResultList extends StatelessWidget {
|
||||
],
|
||||
);
|
||||
}
|
||||
if (resultSummaries.isNotEmpty) {
|
||||
|
||||
if (widget.resultSummaries.isNotEmpty) {
|
||||
if (!bloc.state.userHovered) {
|
||||
WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) {
|
||||
bloc.add(
|
||||
SearchResultListEvent.onHoverSummary(
|
||||
summary: widget.resultSummaries[0],
|
||||
userHovered: false,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -56,10 +90,11 @@ class SearchResultList extends StatelessWidget {
|
||||
ListView.separated(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemCount: resultSummaries.length,
|
||||
itemCount: widget.resultSummaries.length,
|
||||
separatorBuilder: (_, __) => const Divider(height: 0),
|
||||
itemBuilder: (_, index) => SearchSummaryCell(
|
||||
summary: resultSummaries[index],
|
||||
summary: widget.resultSummaries[index],
|
||||
isHovered: bloc.state.hoveredSummary != null,
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -78,13 +113,14 @@ class SearchResultList extends StatelessWidget {
|
||||
ListView.separated(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemCount: resultItems.length,
|
||||
itemCount: widget.resultItems.length,
|
||||
separatorBuilder: (_, __) => const Divider(height: 0),
|
||||
itemBuilder: (_, index) {
|
||||
final item = resultItems[index];
|
||||
final item = widget.resultItems[index];
|
||||
return SearchResultCell(
|
||||
item: item,
|
||||
isTrashed: trash.any((t) => t.id == item.id),
|
||||
isTrashed: widget.trash.any((t) => t.id == item.id),
|
||||
isHovered: bloc.state.hoveredResult?.id == item.id,
|
||||
);
|
||||
},
|
||||
),
|
||||
@ -96,11 +132,9 @@ class SearchResultList extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6),
|
||||
child: BlocProvider(
|
||||
create: (context) => SearchResultListBloc(),
|
||||
child: BlocProvider.value(
|
||||
value: bloc,
|
||||
child: BlocListener<SearchResultListBloc, SearchResultListState>(
|
||||
listenWhen: (previous, current) =>
|
||||
previous.openPageId != current.openPageId,
|
||||
listener: (context, state) {
|
||||
if (state.openPageId != null) {
|
||||
FlowyOverlay.pop(context);
|
||||
@ -116,18 +150,27 @@ class SearchResultList extends StatelessWidget {
|
||||
children: [
|
||||
Flexible(
|
||||
flex: 7,
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
physics: const ClampingScrollPhysics(),
|
||||
children: [
|
||||
_buildAIOverviewSection(context),
|
||||
const VSpace(10),
|
||||
if (resultItems.isNotEmpty) _buildResultsSection(context),
|
||||
],
|
||||
child: BlocBuilder<SearchResultListBloc, SearchResultListState>(
|
||||
buildWhen: (previous, current) =>
|
||||
previous.hoveredResult != current.hoveredResult ||
|
||||
previous.hoveredSummary != current.hoveredSummary,
|
||||
builder: (context, state) {
|
||||
return ListView(
|
||||
shrinkWrap: true,
|
||||
physics: const ClampingScrollPhysics(),
|
||||
children: [
|
||||
_buildAIOverviewSection(context),
|
||||
const VSpace(10),
|
||||
if (widget.resultItems.isNotEmpty)
|
||||
_buildResultsSection(context),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const HSpace(10),
|
||||
if (resultItems.any((item) => item.content.isNotEmpty)) ...[
|
||||
if (widget.resultItems
|
||||
.any((item) => item.content.isNotEmpty)) ...[
|
||||
const VerticalDivider(
|
||||
thickness: 1.0,
|
||||
),
|
||||
|
||||
@ -14,17 +14,23 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
class SearchSummaryCell extends StatelessWidget {
|
||||
const SearchSummaryCell({
|
||||
required this.summary,
|
||||
required this.isHovered,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final SearchSummaryPB summary;
|
||||
final bool isHovered;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FlowyHover(
|
||||
isSelected: () => isHovered,
|
||||
onHover: (value) {
|
||||
context.read<SearchResultListBloc>().add(
|
||||
SearchResultListEvent.onHoverSummary(summary: summary),
|
||||
SearchResultListEvent.onHoverSummary(
|
||||
summary: summary,
|
||||
userHovered: true,
|
||||
),
|
||||
);
|
||||
},
|
||||
style: HoverStyle(
|
||||
|
||||
@ -453,7 +453,6 @@ impl AIManager {
|
||||
if let Some(local_model) = self.local_ai.get_plugin_chat_model() {
|
||||
let model = AIModel::local(local_model, "".to_string());
|
||||
current_active_local_ai_model = Some(model.clone());
|
||||
|
||||
trace!("[Model Selection] current local ai model: {}", model.name);
|
||||
models.push(model);
|
||||
}
|
||||
@ -466,7 +465,7 @@ impl AIManager {
|
||||
}
|
||||
|
||||
// Global active model is the model selected by the user in the workspace settings.
|
||||
let server_active_model = self
|
||||
let mut server_active_model = self
|
||||
.get_workspace_select_model()
|
||||
.await
|
||||
.map(|m| AIModel::server(m, "".to_string()))
|
||||
@ -478,10 +477,14 @@ impl AIManager {
|
||||
);
|
||||
|
||||
let mut user_selected_model = server_active_model.clone();
|
||||
// when current select model is deprecated, reset the model to default
|
||||
if !models.iter().any(|m| m.name == server_active_model.name) {
|
||||
server_active_model = AIModel::default();
|
||||
}
|
||||
|
||||
let source_key = ai_available_models_key(&source);
|
||||
|
||||
// If source is provided, try to get the user-selected model from the store. User selected
|
||||
// model will be used as the active model if it exists.
|
||||
// We use source to identify user selected model. source can be document id or chat id.
|
||||
match self.store_preferences.get_object::<AIModel>(&source_key) {
|
||||
None => {
|
||||
// when there is selected model and current local ai is active, then use local ai
|
||||
@ -491,6 +494,8 @@ impl AIManager {
|
||||
},
|
||||
Some(mut model) => {
|
||||
trace!("[Model Selection] user previous select model: {:?}", model);
|
||||
// If source is provided, try to get the user-selected model from the store. User selected
|
||||
// model will be used as the active model if it exists.
|
||||
if model.is_local {
|
||||
if let Some(local_ai_model) = ¤t_active_local_ai_model {
|
||||
if local_ai_model.name != model.name {
|
||||
@ -508,7 +513,7 @@ impl AIManager {
|
||||
.iter()
|
||||
.find(|m| m.name == user_selected_model.name)
|
||||
.cloned()
|
||||
.or(Some(server_active_model));
|
||||
.or(Some(server_active_model.clone()));
|
||||
|
||||
// Update the stored preference if a different model is used.
|
||||
if let Some(ref active_model) = active_model {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user