mirror of
				https://github.com/AppFlowy-IO/AppFlowy.git
				synced 2025-11-03 19:43:52 +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