Add interactive search history
This commit is contained in:
parent
c025e657d4
commit
9f85ed43ae
4 changed files with 92 additions and 10 deletions
|
|
@ -37,7 +37,7 @@ class EmoteMenuPanel extends StatelessWidget {
|
|||
if (globalEmotes.isNotEmpty) ...[
|
||||
const SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: headerPadding,
|
||||
padding: EdgeInsets.all(10.0),
|
||||
child: Text(
|
||||
'Global Emotes',
|
||||
style: headerStyle,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:frosty/screens/channel/video_chat.dart';
|
||||
|
|
@ -45,6 +46,39 @@ class _SearchState extends State<Search> {
|
|||
onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
|
||||
child: Observer(
|
||||
builder: (_) {
|
||||
if (searchStore.textController.text.isEmpty) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(15.0),
|
||||
child: Text(
|
||||
'HISTORY',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: ListView(
|
||||
children: searchStore.searchHistory
|
||||
.mapIndexed((index, searchTerm) => ListTile(
|
||||
leading: const Icon(Icons.history),
|
||||
title: Text(searchTerm),
|
||||
trailing: IconButton(
|
||||
icon: const Icon(Icons.cancel),
|
||||
onPressed: () => searchStore.searchHistory.removeAt(index),
|
||||
),
|
||||
onTap: () {
|
||||
searchStore.textController.text = searchTerm;
|
||||
searchStore.handleQuery(searchTerm);
|
||||
},
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
itemCount: searchStore.searchResults.length + 1,
|
||||
itemBuilder: (context, index) {
|
||||
|
|
@ -54,7 +88,7 @@ class _SearchState extends State<Search> {
|
|||
}
|
||||
return ListTile(
|
||||
title: Text('Go to ${searchStore.textController.text}'),
|
||||
onTap: () => searchStore.handleSearch(searchStore.textController.text, context),
|
||||
onTap: () => searchStore.handleSearch(context, searchStore.textController.text),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import 'package:frosty/models/channel.dart';
|
|||
import 'package:frosty/screens/channel/video_chat.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
part 'search_store.g.dart';
|
||||
|
||||
|
|
@ -16,19 +17,42 @@ abstract class _SearchStoreBase with Store {
|
|||
final AuthStore authStore;
|
||||
|
||||
@readonly
|
||||
var _searchResults = ObservableList<ChannelQuery>();
|
||||
var _searchHistory = ObservableList<String>();
|
||||
|
||||
_SearchStoreBase({required this.authStore});
|
||||
@readonly
|
||||
var _searchResults = <ChannelQuery>[];
|
||||
|
||||
_SearchStoreBase({required this.authStore}) {
|
||||
init();
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> init() async {
|
||||
// Retrieve the instance that will allow us to retrieve local search history.
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
|
||||
_searchHistory = prefs.getStringList('search_history')?.asObservable() ?? ObservableList<String>();
|
||||
|
||||
autorun((_) {
|
||||
if (_searchHistory.length > 8) _searchHistory.removeLast();
|
||||
prefs.setStringList('search_history', _searchHistory);
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> handleQuery(String query) async {
|
||||
if (query.isEmpty) return;
|
||||
|
||||
_searchHistory.remove(query);
|
||||
_searchHistory.insert(0, query);
|
||||
|
||||
final results = await Twitch.searchChannels(query: query, headers: authStore.headersTwitch);
|
||||
results.sort((c1, c2) => c2.isLive ? 1 : -1);
|
||||
|
||||
_searchResults = ObservableList.of(results);
|
||||
_searchResults = results;
|
||||
}
|
||||
|
||||
Future<void> handleSearch(String search, BuildContext context) async {
|
||||
Future<void> handleSearch(BuildContext context, String search) async {
|
||||
final user = await Twitch.getUser(userLogin: search, headers: authStore.headersTwitch);
|
||||
if (user != null) {
|
||||
final channelInfo = await Twitch.getChannel(userId: user.id, headers: context.read<AuthStore>().headersTwitch);
|
||||
|
|
@ -58,7 +82,7 @@ abstract class _SearchStoreBase with Store {
|
|||
|
||||
void clearSearch() {
|
||||
textController.clear();
|
||||
_searchResults.clear();
|
||||
_searchResults = <ChannelQuery>[];
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
|
|
|
|||
|
|
@ -9,23 +9,47 @@ part of 'search_store.dart';
|
|||
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic
|
||||
|
||||
mixin _$SearchStore on _SearchStoreBase, Store {
|
||||
final _$_searchHistoryAtom = Atom(name: '_SearchStoreBase._searchHistory');
|
||||
|
||||
ObservableList<String> get searchHistory {
|
||||
_$_searchHistoryAtom.reportRead();
|
||||
return super._searchHistory;
|
||||
}
|
||||
|
||||
@override
|
||||
ObservableList<String> get _searchHistory => searchHistory;
|
||||
|
||||
@override
|
||||
set _searchHistory(ObservableList<String> value) {
|
||||
_$_searchHistoryAtom.reportWrite(value, super._searchHistory, () {
|
||||
super._searchHistory = value;
|
||||
});
|
||||
}
|
||||
|
||||
final _$_searchResultsAtom = Atom(name: '_SearchStoreBase._searchResults');
|
||||
|
||||
ObservableList<ChannelQuery> get searchResults {
|
||||
List<ChannelQuery> get searchResults {
|
||||
_$_searchResultsAtom.reportRead();
|
||||
return super._searchResults;
|
||||
}
|
||||
|
||||
@override
|
||||
ObservableList<ChannelQuery> get _searchResults => searchResults;
|
||||
List<ChannelQuery> get _searchResults => searchResults;
|
||||
|
||||
@override
|
||||
set _searchResults(ObservableList<ChannelQuery> value) {
|
||||
set _searchResults(List<ChannelQuery> value) {
|
||||
_$_searchResultsAtom.reportWrite(value, super._searchResults, () {
|
||||
super._searchResults = value;
|
||||
});
|
||||
}
|
||||
|
||||
final _$initAsyncAction = AsyncAction('_SearchStoreBase.init');
|
||||
|
||||
@override
|
||||
Future<void> init() {
|
||||
return _$initAsyncAction.run(() => super.init());
|
||||
}
|
||||
|
||||
final _$handleQueryAsyncAction = AsyncAction('_SearchStoreBase.handleQuery');
|
||||
|
||||
@override
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue