Fixed search function

This commit is contained in:
Balazs Toldi 2022-05-15 22:35:19 +02:00
parent 676e1540c7
commit 3a1acc7794
Signed by: Bazsalanszky
GPG key ID: 6C7D440036F99D58
8 changed files with 150 additions and 104 deletions

View file

@ -1,5 +1,4 @@
import 'package:bloc/bloc.dart';
import 'package:bloc_concurrency/bloc_concurrency.dart';
import 'package:equatable/equatable.dart';
import 'package:gitea_client/cubit/repo_event.dart';
import 'package:gitea_client/model/repository.dart';
@ -8,10 +7,6 @@ import 'package:stream_transform/stream_transform.dart';
part 'repo_state.dart';
class RepoCubit extends Cubit<RepoState> {
RepoCubit() : super(RepoState());
}
class RepoBloc extends Bloc<RepoEvent, RepoState> {
RepoBloc({required this.giteaService}) : super(const RepoState()) {
on<RepoFetched>(_onRepoFetched,

View file

@ -24,61 +24,90 @@ class SearchBloc extends Bloc<SearchEvent, SearchState> {
transformer: (events, mapper) => events.switchMap(mapper),
);
}
Future<void> _SearchFetchedEvent(
SearchFetchedEvent event, Emitter<SearchState> emit) async {
Future<void> _SearchFetchedEvent(SearchFetchedEvent event,
Emitter<SearchState> emit) async {
if (state.hasReachedMax) return;
if (state.queryString.isEmpty) return;
try {
if (state.status == SearchStatus.initial) {
final repos = await giteaService.searchRepo(
final searchResult = await giteaService.searchRepo(
query: state.queryString, page: 1, limit: 100);
return emit(state.copyWith(
if(searchResult.ok) {
return emit(state.copyWith(
status: SearchStatus.success,
repos: repos,
repos: searchResult.data,
loadedPages: 1,
hasReachedMax: false,
error_message: null,
));
}else {
emit(state.copyWith(
status: SearchStatus.failure, error_message: "No results"));
}
}
final repos = await giteaService.searchRepo(
final searchResult = await giteaService.searchRepo(
query: state.queryString, page: state.loadedPages + 1, limit: 100);
final repoList = List.of(state.repos);
repos.forEach((element) {
if (repoList.where((selement) => selement.id == element.id).isEmpty) {
repoList.add(element);
}
});
emit(repos.isEmpty
? state.copyWith(hasReachedMax: true)
: state.copyWith(
status: SearchStatus.success,
repos: repoList,
loadedPages: state.loadedPages + 1,
hasReachedMax: false,
error_message: null,
));
if(searchResult.ok) {
searchResult.data.forEach((element) {
if (repoList
.where((selement) => selement.id == element.id)
.isEmpty) {
repoList.add(element);
}
});
emit(searchResult.data.isEmpty
? state.copyWith(hasReachedMax: true)
: state.copyWith(
status: SearchStatus.success,
repos: repoList,
loadedPages: state.loadedPages + 1,
hasReachedMax: false,
error_message: null,
));
}else {
emit(state.copyWith(
status: SearchStatus.failure, error_message: "No results"));
}
} on Exception catch (e) {
emit(state.copyWith(
status: SearchStatus.failure, error_message: e.toString()));
}
}
Future<void> _SearchInputEvent(
SearchInputEvent event, Emitter<SearchState> emit) async {
if (state.queryString.isEmpty) return;
print(event.queryText);
Future<void> _SearchInputEvent(SearchInputEvent event,
Emitter<SearchState> emit) async {
if (event.queryText.isEmpty) {
emit(state.copyWith(
queryString: event.queryText,
status: SearchStatus.success,
repos: [],
loadedPages: 0,
hasReachedMax: false,
error_message: null,
));
return;
}
print("New state: ${event.queryText}");
try {
final repos = await giteaService.searchRepo(
query: event.queryText, page: 1, limit: 100);
final searchResult = await giteaService.searchRepo(
query: event.queryText, page: 1, limit: 100);
if(searchResult.ok) {
return emit(state.copyWith(
queryString: event.queryText,
status: SearchStatus.success,
repos: repos,
repos: searchResult.data,
loadedPages: 1,
hasReachedMax: false,
error_message: null,
));
}else {
emit(state.copyWith(
status: SearchStatus.failure, error_message: "No results"));
}
} on Exception catch (e) {
emit(state.copyWith(
status: SearchStatus.failure, error_message: e.toString()));

View file

@ -0,0 +1,16 @@
import 'package:gitea_client/model/repository.dart';
class SearchResult {
final bool ok;
final List<Repository> data;
const SearchResult(this.ok, this.data);
SearchResult.fromJson(Map<String, dynamic> jsonM) : ok=jsonM['ok'], data = []{
jsonM['data'].map((dynamic json) {
Repository repo = Repository.fromJson(json);
data.add(repo);
return repo;
}).toList() as List;
}
}

View file

@ -7,6 +7,7 @@ import 'package:gitea_client/model/user.dart';
import 'package:http/http.dart' as http;
import '../model/File.dart';
import '../model/SearchResult.dart';
User _parseAuthenticatedUserResponse(String message){
return User.fromJson(jsonDecode(message));
@ -73,9 +74,9 @@ class GiteaService {
throw Exception('error fetching posts');
}
Future<List<Repository>> searchRepo({required String query,int page = 1, int limit = 10}) async{
Future<SearchResult> searchRepo({required String query,int page = 1, int limit = 10}) async{
var response = await http.get(
Uri.https(apiAccess.instance, "api/v1/user/repos/search", {
Uri.https(apiAccess.instance, "api/v1/repos/search", {
"token": apiAccess.token,
"q": query,
"page" : page.toString(),
@ -83,10 +84,9 @@ class GiteaService {
}),
);
if (response.statusCode == 200) {
final body = json.decode(response.body) as List;
var result = body.map((dynamic json) {
return Repository.fromJson(json);
}).toList();
final body = json.decode(response.body);
var result = SearchResult.fromJson(body);
return result;
}

View file

@ -22,37 +22,68 @@ class _SearchListState extends State<SearchList> {
super.initState();
_scrollController.addListener(_onScroll);
}
BuildContext? blocContex;
final search = TextEditingController();
@override
Widget build(BuildContext context) {
return BlocBuilder<SearchBloc, SearchState>(
builder: (context, state) {
switch (state.status) {
case SearchStatus.failure:
String error_message = state.error_message!;
return Center(child: Text('failed to fetch $error_message'));
case SearchStatus.success:
if (state.repos.isEmpty) {
return const Center(child: Text('no repos'));
final media = MediaQuery.of(context).size;
return Column(
children: [
Text(
"Search",
style: Theme.of(context).textTheme.headline4,
),
TextField(
decoration: const InputDecoration(
labelText: "Search text",
),
controller: search,
onChanged: (text) {
if (blocContex != null) {
print("Input: ${search.text}");
blocContex!
.read<SearchBloc>()
.add(SearchInputEvent(search.text));
}
if (state.repos.length < 5) {
context.read<SearchBloc>().add(SearchFetchedEvent());
},
),
BlocBuilder<SearchBloc, SearchState>(
builder: (context, state) {
blocContex = context;
switch (state.status) {
case SearchStatus.failure:
String error_message = state.error_message!;
return Center(child: Text('failed to fetch $error_message'));
case SearchStatus.success:
if (state.repos.isEmpty) {
return const Center(child: Text('no repos'));
}
if (state.repos.length < 5) {
context.read<SearchBloc>().add(SearchFetchedEvent());
}
return SizedBox(
height: media.height -kToolbarHeight-130,
child: ListView.builder(
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return index >= state.repos.length
? BottomLoader()
: RepoListItem(repo: state.repos[index],user: widget.user,);
},
itemCount: state.hasReachedMax
? state.repos.length
: state.repos.length + 1,
controller: _scrollController,
),
);
default:
return const Center(child: CircularProgressIndicator());
}
return ListView.builder(
itemBuilder: (BuildContext context, int index) {
return index >= state.repos.length
? BottomLoader()
: RepoListItem(repo: state.repos[index],user: widget.user,);
},
itemCount: state.hasReachedMax
? state.repos.length
: state.repos.length + 1,
controller: _scrollController,
);
default:
return const Center(child: CircularProgressIndicator());
}
},
},
),
],
);
}
@ -96,7 +127,6 @@ class RepoListItem extends StatelessWidget {
final SavedUser user;
@override
Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme;
return Material(
child: Container(

View file

@ -16,18 +16,10 @@ class SearchPage extends StatefulWidget {
}
class _SearchPage extends State<SearchPage> {
final search = TextEditingController();
@override
void initState() {
search.addListener(_onSearch);
}
void initState() {}
void _onSearch() {
print("Input: ${search.text}");
context.read<SearchBloc>().add(SearchInputEvent(search.text));
}
@override
Widget build(BuildContext context) {
@ -36,22 +28,14 @@ class _SearchPage extends State<SearchPage> {
child: SizedBox(
width: (media.width > 600) ? media.width * 0.5 : media.width * 0.9,
child: BlocProvider(
create: (_) => SearchBloc(
giteaService: GiteaService(apiAccess: widget.user.apiAccess))
..add(SearchInputEvent(search.text)),
create: (context) {
return SearchBloc(
giteaService: GiteaService(apiAccess: widget.user.apiAccess))
..add(SearchInputEvent(""));
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
"Search",
style: Theme.of(context).textTheme.headline4,
),
TextField(
decoration: const InputDecoration(
labelText: "Search text",
),
controller: search,
),
SearchList(
user: widget.user,
)

View file

@ -6,9 +6,6 @@ list(APPEND FLUTTER_PLUGIN_LIST
url_launcher_linux
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
@ -17,8 +14,3 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST})
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
endforeach(ffi_plugin)

View file

@ -105,7 +105,7 @@ packages:
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "1.16.0"
version: "1.15.0"
convert:
dependency: transitive
description:
@ -147,7 +147,7 @@ packages:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
version: "1.2.0"
ffi:
dependency: transitive
description:
@ -251,7 +251,7 @@ packages:
name: js
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.4"
version: "0.6.3"
json_annotation:
dependency: transitive
description:
@ -300,7 +300,7 @@ packages:
name: material_color_utilities
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.4"
version: "0.1.3"
meta:
dependency: transitive
description:
@ -328,7 +328,7 @@ packages:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.1"
version: "1.8.0"
path_provider_linux:
dependency: transitive
description:
@ -473,7 +473,7 @@ packages:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.2"
version: "1.8.1"
stack_trace:
dependency: transitive
description:
@ -515,7 +515,7 @@ packages:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.9"
version: "0.4.8"
typed_data:
dependency: transitive
description:
@ -585,7 +585,7 @@ packages:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.2"
version: "2.1.1"
watcher:
dependency: transitive
description:
@ -615,5 +615,5 @@ packages:
source: hosted
version: "3.1.0"
sdks:
dart: ">=2.17.0-0 <3.0.0"
dart: ">=2.16.2 <3.0.0"
flutter: ">=2.10.0"