import 'dart:async'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../backend/backend.dart'; import '../../../core/utils/result.dart'; import 'search_state.dart'; final searchControllerProvider = AsyncNotifierProvider(SearchController.new); class SearchController extends AsyncNotifier { Timer? _suggestionDebounce; int _suggestionRequestId = 0; @override Future build() async { ref.onDispose(() => _suggestionDebounce?.cancel()); final recent = await _recent(); return SearchState(recent: recent); } Future updateQuery(String query) async { final lastToken = query.trim().isEmpty ? '' : query.trim().split(RegExp(r'\s+')).last; final requestId = ++_suggestionRequestId; _suggestionDebounce?.cancel(); if (lastToken.isEmpty) { state = AsyncData( (state.value ?? const SearchState()).copyWith( query: query, suggestions: const [], ), ); return; } state = AsyncData( (state.value ?? const SearchState()).copyWith( query: query, ), ); _suggestionDebounce = Timer(const Duration(milliseconds: 280), () async { final suggestionsResult = await ref .read(searchServiceProvider) .autocompleteDetailed(lastToken, limit: 16); if (requestId != _suggestionRequestId) return; final normalizedToken = lastToken.trim().toLowerCase(); final suggestions = suggestionsResult is Success> ? suggestionsResult.data .where((item) => item.name.toLowerCase().startsWith( normalizedToken, )) .toList(growable: false) : []; state = AsyncData( (state.value ?? const SearchState()).copyWith( query: query, suggestions: suggestions, ), ); }); } Future clearHistory() async { await ref.read(searchServiceProvider).clearHistory(); state = const AsyncData(SearchState()); } Future> _recent() async { final result = await ref.read(searchServiceProvider).recentSearches(); return result is Success> ? result.data : const []; } }