add
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
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<VideoItem> items;
|
||||
final int total;
|
||||
final int page;
|
||||
final bool isSearching;
|
||||
final bool isLoadingMore;
|
||||
|
||||
/// 是否已执行过搜索(区分默认页与结果页)
|
||||
final bool searched;
|
||||
|
||||
/// 热搜榜(进入页面时加载)
|
||||
final List<String> hotWords;
|
||||
|
||||
/// 本地搜索历史(最新在前)
|
||||
final List<String> history;
|
||||
|
||||
bool get hasMore => items.length < total;
|
||||
|
||||
SearchState copyWith({
|
||||
String? keyword,
|
||||
SearchSort? sort,
|
||||
List<VideoItem>? items,
|
||||
int? total,
|
||||
int? page,
|
||||
bool? isSearching,
|
||||
bool? isLoadingMore,
|
||||
bool? searched,
|
||||
List<String>? hotWords,
|
||||
List<String>? 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<SearchState> {
|
||||
SearchNotifier() : super(SearchState(history: SearchHistoryStore.I.history));
|
||||
|
||||
/// 加载热搜榜(静默失败,空列表兜底)
|
||||
Future<void> loadHot() async {
|
||||
try {
|
||||
final words = await SearchService.hotWords();
|
||||
state = state.copyWith(hotWords: words);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
/// 执行搜索(换关键词或排序时全量重来)
|
||||
Future<void> 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<void> 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<void> removeHistory(String word) async {
|
||||
await SearchHistoryStore.I.remove(word);
|
||||
state = state.copyWith(history: SearchHistoryStore.I.history);
|
||||
}
|
||||
|
||||
/// 清空历史
|
||||
Future<void> clearHistory() async {
|
||||
await SearchHistoryStore.I.clear();
|
||||
state = state.copyWith(history: const []);
|
||||
}
|
||||
}
|
||||
|
||||
final searchProvider =
|
||||
StateNotifierProvider<SearchNotifier, SearchState>((ref) => SearchNotifier());
|
||||
Reference in New Issue
Block a user