156 lines
4.1 KiB
Dart
156 lines
4.1 KiB
Dart
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());
|