Skip to content

Commit 50f904d

Browse files
committed
chore: search summary
1 parent 14f4798 commit 50f904d

File tree

6 files changed

+382
-277
lines changed

6 files changed

+382
-277
lines changed

frontend/appflowy_flutter/lib/workspace/application/command_palette/command_palette_bloc.dart

Lines changed: 156 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,16 @@ part 'command_palette_bloc.freezed.dart';
1515
class CommandPaletteBloc
1616
extends Bloc<CommandPaletteEvent, CommandPaletteState> {
1717
CommandPaletteBloc() : super(CommandPaletteState.initial()) {
18+
// Register individual event handlers for clarity
19+
on<_SearchChanged>(_onSearchChanged);
20+
on<_PerformSearch>(_onPerformSearch);
21+
on<_NewSearchStream>(_onNewSearchStream);
22+
on<_ResultsChanged>(_onResultsChanged);
23+
on<_TrashChanged>(_onTrashChanged);
24+
on<_WorkspaceChanged>(_onWorkspaceChanged);
25+
on<_ClearSearch>(_onClearSearch);
26+
1827
_initTrash();
19-
_dispatch();
2028
}
2129

2230
Timer? _debounceOnChanged;
@@ -32,122 +40,6 @@ class CommandPaletteBloc
3240
return super.close();
3341
}
3442

35-
void _dispatch() {
36-
on<CommandPaletteEvent>((event, emit) async {
37-
event.when(
38-
searchChanged: _debounceOnSearchChanged,
39-
trashChanged: (trash) async {
40-
if (trash != null) {
41-
return emit(state.copyWith(trash: trash));
42-
}
43-
44-
final trashOrFailure = await _trashService.readTrash();
45-
final trashRes = trashOrFailure.fold(
46-
(trash) => trash,
47-
(error) => null,
48-
);
49-
50-
if (trashRes != null) {
51-
emit(state.copyWith(trash: trashRes.items));
52-
}
53-
},
54-
performSearch: (search) async {
55-
if (search.isNotEmpty && search != state.query) {
56-
_oldQuery = state.query;
57-
emit(state.copyWith(query: search, isLoading: true));
58-
unawaited(
59-
SearchBackendService.performSearch(
60-
search,
61-
workspaceId: _workspaceId,
62-
).then(
63-
(result) {
64-
result.onSuccess(
65-
(stream) async {
66-
if (!isClosed) {
67-
add(
68-
CommandPaletteEvent.newSearchStream(stream: stream),
69-
);
70-
}
71-
},
72-
);
73-
},
74-
),
75-
);
76-
} else {
77-
emit(
78-
state.copyWith(
79-
query: null,
80-
isLoading: false,
81-
resultItems: [],
82-
resultSummaries: [],
83-
),
84-
);
85-
}
86-
},
87-
newSearchStream: (SearchResponseStream stream) {
88-
if (state.searchResponseStream != null) {
89-
state.searchResponseStream!.dispose();
90-
}
91-
92-
emit(state.copyWith(searchId: stream.searchId));
93-
stream.listen(
94-
onData: (SearchResponsePB response) {
95-
if (!isClosed) {
96-
add(CommandPaletteEvent.resultsChanged(response: response));
97-
}
98-
},
99-
);
100-
},
101-
resultsChanged: (SearchResponsePB response) {
102-
if (state.query != _oldQuery) {
103-
emit(
104-
state.copyWith(
105-
resultItems: [],
106-
resultSummaries: [],
107-
isLoading: response.isLoading,
108-
),
109-
);
110-
_oldQuery = state.query;
111-
}
112-
113-
if (state.searchId != response.searchId) {
114-
return;
115-
}
116-
117-
emit(
118-
state.copyWith(
119-
resultItems: response.result.items,
120-
resultSummaries: response.result.summaries,
121-
isLoading: response.isLoading,
122-
),
123-
);
124-
},
125-
workspaceChanged: (workspaceId) {
126-
_workspaceId = workspaceId;
127-
emit(
128-
state.copyWith(
129-
resultItems: [],
130-
resultSummaries: [],
131-
query: '',
132-
isLoading: false,
133-
),
134-
);
135-
},
136-
clearSearch: () {
137-
emit(
138-
state.copyWith(
139-
resultItems: [],
140-
resultSummaries: [],
141-
query: '',
142-
isLoading: false,
143-
searchId: null,
144-
),
145-
);
146-
},
147-
);
148-
});
149-
}
150-
15143
Future<void> _initTrash() async {
15244
_trashListener.start(
15345
trashUpdated: (trashOrFailed) {
@@ -158,53 +50,188 @@ class CommandPaletteBloc
15850

15951
final trashOrFailure = await _trashService.readTrash();
16052
final trash = trashOrFailure.toNullable();
161-
16253
add(CommandPaletteEvent.trashChanged(trash: trash?.items));
16354
}
16455

165-
void _debounceOnSearchChanged(String value) {
56+
// Handler for debouncing search changes
57+
FutureOr<void> _onSearchChanged(
58+
_SearchChanged event,
59+
Emitter<CommandPaletteState> emit,
60+
) {
16661
_debounceOnChanged?.cancel();
16762
_debounceOnChanged = Timer(
16863
const Duration(milliseconds: 300),
169-
() => _performSearch(value),
64+
() {
65+
if (!isClosed) {
66+
add(CommandPaletteEvent.performSearch(search: event.search));
67+
}
68+
},
69+
);
70+
}
71+
72+
// Handler for performing search
73+
FutureOr<void> _onPerformSearch(
74+
_PerformSearch event,
75+
Emitter<CommandPaletteState> emit,
76+
) async {
77+
if (event.search.isNotEmpty && event.search != state.query) {
78+
_oldQuery = state.query;
79+
emit(state.copyWith(query: event.search, isLoading: true));
80+
81+
// Fire off the search asynchronously without waiting for its result
82+
unawaited(
83+
SearchBackendService.performSearch(
84+
event.search,
85+
workspaceId: _workspaceId,
86+
).then(
87+
(result) {
88+
result.onSuccess((stream) async {
89+
if (!isClosed) {
90+
add(CommandPaletteEvent.newSearchStream(stream: stream));
91+
}
92+
});
93+
},
94+
),
95+
);
96+
} else {
97+
emit(
98+
state.copyWith(
99+
query: null,
100+
isLoading: false,
101+
resultItems: [],
102+
resultSummaries: [],
103+
),
104+
);
105+
}
106+
}
107+
108+
// Handler to listen and forward search stream events
109+
FutureOr<void> _onNewSearchStream(
110+
_NewSearchStream event,
111+
Emitter<CommandPaletteState> emit,
112+
) {
113+
// Dispose previous stream if exists
114+
state.searchResponseStream?.dispose();
115+
emit(
116+
state.copyWith(
117+
searchId: event.stream.searchId,
118+
searchResponseStream: event.stream,
119+
),
120+
);
121+
122+
// Listen to the stream and add incoming results as events
123+
event.stream.listen(
124+
onData: (SearchResponsePB response) {
125+
if (!isClosed) {
126+
add(CommandPaletteEvent.resultsChanged(response: response));
127+
}
128+
},
170129
);
171130
}
172131

173-
void _performSearch(String value) =>
174-
add(CommandPaletteEvent.performSearch(search: value));
132+
// Handler for results update events
133+
FutureOr<void> _onResultsChanged(
134+
_ResultsChanged event,
135+
Emitter<CommandPaletteState> emit,
136+
) async {
137+
// If the query has been updated since the last search, clear previous results
138+
if (state.query != _oldQuery) {
139+
emit(
140+
state.copyWith(
141+
resultItems: [],
142+
resultSummaries: [],
143+
isLoading: event.response.isLoading,
144+
),
145+
);
146+
_oldQuery = state.query;
147+
}
148+
149+
if (state.searchId != event.response.searchId) {
150+
return;
151+
}
152+
153+
emit(
154+
state.copyWith(
155+
resultItems: event.response.result.items,
156+
resultSummaries: event.response.result.summaries,
157+
isLoading: event.response.isLoading,
158+
),
159+
);
160+
}
161+
162+
// Handler for trash update events
163+
FutureOr<void> _onTrashChanged(
164+
_TrashChanged event,
165+
Emitter<CommandPaletteState> emit,
166+
) async {
167+
if (event.trash != null) {
168+
emit(state.copyWith(trash: event.trash!));
169+
} else {
170+
final trashOrFailure = await _trashService.readTrash();
171+
final trashRes = trashOrFailure.fold((trash) => trash, (error) => null);
172+
if (trashRes != null) {
173+
emit(state.copyWith(trash: trashRes.items));
174+
}
175+
}
176+
}
177+
178+
// Handler for workspace changes
179+
FutureOr<void> _onWorkspaceChanged(
180+
_WorkspaceChanged event,
181+
Emitter<CommandPaletteState> emit,
182+
) {
183+
_workspaceId = event.workspaceId;
184+
emit(
185+
state.copyWith(
186+
resultItems: [],
187+
resultSummaries: [],
188+
query: '',
189+
isLoading: false,
190+
),
191+
);
192+
}
193+
194+
// Handler for clearing the search state
195+
FutureOr<void> _onClearSearch(
196+
_ClearSearch event,
197+
Emitter<CommandPaletteState> emit,
198+
) {
199+
emit(
200+
state.copyWith(
201+
resultItems: [],
202+
resultSummaries: [],
203+
query: '',
204+
isLoading: false,
205+
searchId: null,
206+
),
207+
);
208+
}
175209
}
176210

177211
@freezed
178212
class CommandPaletteEvent with _$CommandPaletteEvent {
179213
const factory CommandPaletteEvent.searchChanged({required String search}) =
180214
_SearchChanged;
181-
182215
const factory CommandPaletteEvent.performSearch({required String search}) =
183216
_PerformSearch;
184-
185217
const factory CommandPaletteEvent.newSearchStream({
186218
required SearchResponseStream stream,
187219
}) = _NewSearchStream;
188-
189220
const factory CommandPaletteEvent.resultsChanged({
190221
required SearchResponsePB response,
191222
}) = _ResultsChanged;
192-
193223
const factory CommandPaletteEvent.trashChanged({
194224
@Default(null) List<TrashPB>? trash,
195225
}) = _TrashChanged;
196-
197226
const factory CommandPaletteEvent.workspaceChanged({
198227
@Default(null) String? workspaceId,
199228
}) = _WorkspaceChanged;
200-
201229
const factory CommandPaletteEvent.clearSearch() = _ClearSearch;
202230
}
203231

204232
@freezed
205233
class CommandPaletteState with _$CommandPaletteState {
206234
const CommandPaletteState._();
207-
208235
const factory CommandPaletteState({
209236
@Default(null) String? query,
210237
@Default([]) List<SearchResponseItemPB> resultItems,

0 commit comments

Comments
 (0)