Skip to content

Commit

Permalink
Added a search function to the groups screen (#340)
Browse files Browse the repository at this point in the history
Co-authored-by: Dirk Doesburg <[email protected]>
  • Loading branch information
JeeVee11 and DeD1rk authored Feb 9, 2023
1 parent edf8532 commit 4f00baf
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 13 deletions.
70 changes: 57 additions & 13 deletions lib/blocs/groups_cubit.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import 'dart:async';

import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:reaxit/api/api_repository.dart';
import 'package:reaxit/api/exceptions.dart';
import 'package:reaxit/blocs/detail_state.dart';
import 'package:reaxit/models/group.dart';
import 'package:reaxit/blocs.dart';
import 'package:reaxit/models.dart';
import 'package:reaxit/config.dart' as config;

typedef GroupsState = DetailState<List<ListGroup>>;

Expand All @@ -19,19 +22,10 @@ class GroupsCubit extends Cubit<GroupsState> {
if (listResponse.results.isNotEmpty) {
emit(ResultState(listResponse.results));
} else {
emit(const ErrorState('There are no boards.'));
emit(const ErrorState('There are no groups.'));
}
} on ApiException catch (exception) {
emit(ErrorState(_failureMessage(exception)));
}
}

String _failureMessage(ApiException exception) {
switch (exception) {
case ApiException.noInternet:
return 'Not connected to the internet.';
default:
return 'An unknown error occurred.';
emit(ErrorState(exception.message));
}
}
}
Expand All @@ -47,3 +41,53 @@ class CommitteesCubit extends GroupsCubit {
class SocietiesCubit extends GroupsCubit {
SocietiesCubit(ApiRepository api) : super(api, MemberGroupType.society);
}

class AllGroupsCubit extends GroupsCubit {
/// The last used search query. Can be set through `this.search(query)`.
String? _searchQuery;

/// A timer used to debounce calls to `this.load()` from `this.search()`.
Timer? _searchDebounceTimer;

// We pass null as MemberGroupType, so we get all groups.
AllGroupsCubit(ApiRepository api) : super(api, null);

@override
Future<void> load() async {
emit(const LoadingState());

try {
final query = _searchQuery;

final listResponse =
await api.getGroups(limit: 1000, type: groupType, search: query);

// Don't load if the query changed in the meantime
if (query != _searchQuery) return;

if (listResponse.results.isNotEmpty) {
emit(ResultState(listResponse.results));
} else {
if (query?.isEmpty ?? true) {
emit(const ErrorState('There are no results.'));
}
emit(ErrorState('There are no results for "$query".'));
}
} on ApiException catch (exception) {
emit(ErrorState(exception.message));
}
}

void search(String? query) {
if (query != _searchQuery) {
_searchQuery = query;
_searchDebounceTimer?.cancel();
if (query?.isEmpty ?? false) {
// Don't get results when the query is empty.
emit(const LoadingState());
} else {
_searchDebounceTimer = Timer(config.searchDebounceTime, load);
}
}
}
}
84 changes: 84 additions & 0 deletions lib/ui/screens/groups_screen.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:reaxit/blocs/detail_state.dart';
import 'package:reaxit/blocs/groups_cubit.dart';
import 'package:reaxit/models/group.dart';
import 'package:reaxit/ui/widgets.dart';
import 'package:collection/collection.dart';
import 'package:reaxit/api/api_repository.dart';

class GroupsScreen extends StatefulWidget {
final MemberGroupType? currentScreen;
Expand Down Expand Up @@ -68,6 +70,23 @@ class _GroupsScreenState extends State<GroupsScreen>
],
indicatorColor: Theme.of(context).colorScheme.primary,
),
actions: [
IconButton(
padding: const EdgeInsets.all(16),
icon: const Icon(Icons.search),
onPressed: () async {
final searchCubit =
AllGroupsCubit(RepositoryProvider.of<ApiRepository>(context));

await showSearch(
context: context,
delegate: GroupSearchDelegate(searchCubit),
);

searchCubit.close();
},
)
],
),
drawer: MenuDrawer(),
body: TabBarView(
Expand Down Expand Up @@ -173,3 +192,68 @@ class GroupListScrollView extends StatelessWidget {
);
}
}

class GroupSearchDelegate extends SearchDelegate {
final AllGroupsCubit _cubit;

GroupSearchDelegate(this._cubit);

@override
ThemeData appBarTheme(BuildContext context) {
final theme = super.appBarTheme(context);
return theme.copyWith(
textTheme: theme.textTheme.copyWith(
titleLarge: GoogleFonts.openSans(
textStyle: Theme.of(context).textTheme.titleLarge,
),
),
);
}

@override
List<Widget> buildActions(BuildContext context) {
if (query.isNotEmpty) {
return <Widget>[
IconButton(
padding: const EdgeInsets.all(16),
tooltip: 'Clear search bar',
icon: const Icon(Icons.clear),
onPressed: () {
query = '';
},
)
];
} else {
return [];
}
}

@override
Widget buildLeading(BuildContext context) {
return BackButton(
onPressed: () => close(context, null),
);
}

@override
Widget buildResults(BuildContext context) {
return BlocBuilder<AllGroupsCubit, GroupsState>(
bloc: _cubit..search(query),
builder: (context, state) {
if (state is ErrorState) {
return ErrorScrollView(state.message!);
} else {
return GroupListScrollView(
key: const PageStorageKey('groups-search'),
groups: state.result ?? [],
);
}
},
);
}

@override
Widget buildSuggestions(BuildContext context) {
return buildResults(context);
}
}

0 comments on commit 4f00baf

Please sign in to comment.