Add interactive search history

This commit is contained in:
tommyxchow 2021-12-23 00:26:33 -05:00
parent c025e657d4
commit 9f85ed43ae
4 changed files with 92 additions and 10 deletions

View file

@ -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,

View file

@ -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),
);
}

View file

@ -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() {

View file

@ -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