import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../core/storage/search_history_store.dart'; import '../models/video_item.dart'; import '../services/search_service.dart'; /// 搜索排序(《03-接口文档》5.10) enum SearchSort { hot('hot', '热度'), latest('latest', '最新'); const SearchSort(this.apiValue, this.label); final String apiValue; final String label; } /// 搜索页状态 class SearchState { const SearchState({ this.keyword = '', this.sort = SearchSort.hot, this.items = const [], this.total = 0, this.page = 1, this.isSearching = false, this.isLoadingMore = false, this.searched = false, this.hotWords = const [], this.history = const [], }); final String keyword; final SearchSort sort; final List items; final int total; final int page; final bool isSearching; final bool isLoadingMore; /// 是否已执行过搜索(区分默认页与结果页) final bool searched; /// 热搜榜(进入页面时加载) final List hotWords; /// 本地搜索历史(最新在前) final List history; bool get hasMore => items.length < total; SearchState copyWith({ String? keyword, SearchSort? sort, List? items, int? total, int? page, bool? isSearching, bool? isLoadingMore, bool? searched, List? hotWords, List? history, }) => SearchState( keyword: keyword ?? this.keyword, sort: sort ?? this.sort, items: items ?? this.items, total: total ?? this.total, page: page ?? this.page, isSearching: isSearching ?? this.isSearching, isLoadingMore: isLoadingMore ?? this.isLoadingMore, searched: searched ?? this.searched, hotWords: hotWords ?? this.hotWords, history: history ?? this.history, ); } class SearchNotifier extends StateNotifier { SearchNotifier() : super(SearchState(history: SearchHistoryStore.I.history)); /// 加载热搜榜(静默失败,空列表兜底) Future loadHot() async { try { final words = await SearchService.hotWords(); state = state.copyWith(hotWords: words); } catch (_) {} } /// 执行搜索(换关键词或排序时全量重来) Future search(String keyword, {SearchSort? sort}) async { final word = keyword.trim(); if (word.isEmpty) return; final nextSort = sort ?? state.sort; state = state.copyWith( keyword: word, sort: nextSort, isSearching: true, searched: true, items: const [], total: 0, page: 1, ); // 写入本地历史(去重置顶,最多 10 条) await SearchHistoryStore.I.add(word); state = state.copyWith(history: SearchHistoryStore.I.history); try { final page = await SearchService.search(word, sort: nextSort.apiValue); state = state.copyWith( items: page.items, total: page.total, isSearching: false, page: 1, ); } catch (_) { state = state.copyWith(isSearching: false); } } /// 触底加载更多 Future loadMore() async { if (!state.hasMore || state.isLoadingMore || state.isSearching) return; state = state.copyWith(isLoadingMore: true); try { final page = await SearchService.search( state.keyword, sort: state.sort.apiValue, page: state.page + 1, ); state = state.copyWith( items: [...state.items, ...page.items], total: page.total, isLoadingMore: false, page: state.page + 1, ); } catch (_) { state = state.copyWith(isLoadingMore: false); } } /// 删除单条历史 Future removeHistory(String word) async { await SearchHistoryStore.I.remove(word); state = state.copyWith(history: SearchHistoryStore.I.history); } /// 清空历史 Future clearHistory() async { await SearchHistoryStore.I.clear(); state = state.copyWith(history: const []); } } final searchProvider = StateNotifierProvider((ref) => SearchNotifier());