117 lines
2.9 KiB
Dart
117 lines
2.9 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import '../../../core/constants.dart';
|
|
import '../../../core/database/app_database.dart';
|
|
import '../data/search_repository.dart';
|
|
|
|
class SearchState {
|
|
final String query;
|
|
final List<SearchResult> results;
|
|
final List<SearchHistoryData> recentSearches;
|
|
final bool isLoading;
|
|
final String? error;
|
|
|
|
const SearchState({
|
|
this.query = '',
|
|
this.results = const [],
|
|
this.recentSearches = const [],
|
|
this.isLoading = false,
|
|
this.error,
|
|
});
|
|
|
|
SearchState copyWith({
|
|
String? query,
|
|
List<SearchResult>? results,
|
|
List<SearchHistoryData>? recentSearches,
|
|
bool? isLoading,
|
|
String? error,
|
|
bool clearError = false,
|
|
}) {
|
|
return SearchState(
|
|
query: query ?? this.query,
|
|
results: results ?? this.results,
|
|
recentSearches: recentSearches ?? this.recentSearches,
|
|
isLoading: isLoading ?? this.isLoading,
|
|
error: clearError ? null : (error ?? this.error),
|
|
);
|
|
}
|
|
}
|
|
|
|
class SearchNotifier extends StateNotifier<SearchState> {
|
|
final SearchRepository _repository;
|
|
Timer? _debounce;
|
|
|
|
SearchNotifier(this._repository) : super(const SearchState()) {
|
|
_loadHistory();
|
|
}
|
|
|
|
Future<void> _loadHistory() async {
|
|
final history = await _repository.getRecentSearches();
|
|
if (mounted) {
|
|
state = state.copyWith(recentSearches: history);
|
|
}
|
|
}
|
|
|
|
void updateQuery(String query) {
|
|
state = state.copyWith(query: query, clearError: true);
|
|
_debounce?.cancel();
|
|
|
|
if (query.trim().isEmpty) {
|
|
state = state.copyWith(results: [], isLoading: false);
|
|
return;
|
|
}
|
|
|
|
state = state.copyWith(isLoading: true);
|
|
_debounce = Timer(
|
|
const Duration(milliseconds: AppConstants.searchDebounceMs),
|
|
() => _performSearch(query),
|
|
);
|
|
}
|
|
|
|
Future<void> _performSearch(String query) async {
|
|
try {
|
|
final results = await _repository.search(query);
|
|
if (mounted && state.query == query) {
|
|
state = state.copyWith(results: results, isLoading: false);
|
|
}
|
|
} catch (e) {
|
|
if (mounted) {
|
|
state = state.copyWith(
|
|
isLoading: false,
|
|
error: 'Search failed. Please try again.',
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<void> selectResult(SearchResult result) async {
|
|
await _repository.saveToHistory(
|
|
result.name,
|
|
lat: result.latitude,
|
|
lon: result.longitude,
|
|
);
|
|
await _loadHistory();
|
|
}
|
|
|
|
Future<void> deleteHistoryEntry(int id) async {
|
|
await _repository.deleteHistoryEntry(id);
|
|
await _loadHistory();
|
|
}
|
|
|
|
Future<void> clearHistory() async {
|
|
await _repository.clearHistory();
|
|
await _loadHistory();
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_debounce?.cancel();
|
|
super.dispose();
|
|
}
|
|
}
|
|
|
|
final searchProvider =
|
|
StateNotifierProvider.autoDispose<SearchNotifier, SearchState>((ref) {
|
|
return SearchNotifier(ref.watch(searchRepositoryProvider));
|
|
});
|