Skip to content

Commit

Permalink
feat(science-clubs): add new filters section
Browse files Browse the repository at this point in the history
  • Loading branch information
simon-the-shark committed Aug 24, 2024
1 parent 9544be4 commit 0b7a3d9
Show file tree
Hide file tree
Showing 47 changed files with 1,456 additions and 679 deletions.
661 changes: 410 additions & 251 deletions lib/api_base/schema.graphql

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions lib/config/config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,7 @@ abstract class GuestPageConfig {
static const contactLink = "https://solvro.pwr.edu.pl/contact/";
static const solvroMailLink = "mailto:kn.solvro@pwr.edu.pl";
}

abstract class ScienceClubCardConfig {
static const trailingPadding = 2.0;
}
1 change: 1 addition & 0 deletions lib/config/firebase.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ abstract class FirebaseConfig {
static const storageRoot = "sci-clubs-images";
static const firestoreSciClubs = "sci_clubs";
static const firestoreTags = "tags";
static const departments = "departments";
}

Future<FirebaseApp> firebaseConfig() async {
Expand Down
47 changes: 47 additions & 0 deletions lib/features/firebase/models/department.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import "package:flutter/material.dart";
import "package:freezed_annotation/freezed_annotation.dart";

import "../../../theme/hex_color.dart";
import "../../../utils/colors_sort.dart";
import "../../topwr_mockup/config/ui_config.dart";

part "department.freezed.dart";
part "department.g.dart";

@freezed
class Department with _$Department {
const factory Department({
required String name,
required String code,
String? betterCode,
String? gradientStart,
String? gradientEnd,
}) = _Department;

const Department._();

factory Department.fromJson(Map<String, Object?> json) =>
_$DepartmentFromJson(json);

LinearGradient get gradient => LinearGradient(
colors: [
HexColor(
gradientStart ?? DepartmentsConfig.defaultColorFirst,
),
HexColor(
gradientEnd ?? DepartmentsConfig.defaultColorSecond,
),
]..sortByLightness(),
);
}

extension GetDepartmentsCodeX on Department? {
static const _fallbackCode = 0;

int extractIntFromStrCode() {
if (this != null && this!.code.length >= 2) {
return int.tryParse(this!.code.substring(1)) ?? _fallbackCode;
}
return _fallbackCode;
}
}
28 changes: 28 additions & 0 deletions lib/features/firebase/repositories/department_repo.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import "package:cloud_firestore/cloud_firestore.dart";
import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:riverpod_annotation/riverpod_annotation.dart";

import "../../../config/firebase.dart";
import "../models/department.dart";

part "department_repo.g.dart";

@Riverpod(keepAlive: true)
class DepartmentRepo extends _$DepartmentRepo {
late final _collection = FirebaseFirestore.instance
.collection(FirebaseConfig.departments)
.withConverter<Department>(
fromFirestore: (snapshots, _) => Department.fromJson(snapshots.data()!),
toFirestore: (model, _) => model.toJson(),
);

@override
FutureOr<IList<Department>> build() async {
final data = await _collection.get();
return data.docs.map((e) => e.data()).toIList().sort(
(a, b) => a.extractIntFromStrCode().compareTo(
b.extractIntFromStrCode(),
),
);
}
}
15 changes: 1 addition & 14 deletions lib/features/firebase/repositories/tags_repo.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@ import "package:cloud_firestore/cloud_firestore.dart";
import "package:riverpod_annotation/riverpod_annotation.dart";

import "../../../config/firebase.dart";
import "../../../utils/watch_locale.dart";
import "../../../utils/where_non_null_iterable.dart";
import "../models/tag.dart";

part "tags_repo.g.dart";

@Riverpod(keepAlive: true)
class RemoteTagsRepository extends _$RemoteTagsRepository {
class TagsRepository extends _$TagsRepository {
late final _collection = FirebaseFirestore.instance
.collection(FirebaseConfig.firestoreTags)
.withConverter<Tag>(
Expand All @@ -24,14 +22,3 @@ class RemoteTagsRepository extends _$RemoteTagsRepository {
..sort((a, b) => a.name.compareTo(b.name));
}
}

@riverpod
Tag allTagSingleton(AllTagSingletonRef ref) =>
Tag(name: ref.watch(watchLocaleProvider).all);

@riverpod
Future<List<Tag>> tagsListRepository(TagsListRepositoryRef ref) async {
final allTag = ref.watch(allTagSingletonProvider);
final restTags = await ref.watch(remoteTagsRepositoryProvider.future);
return [allTag, ...restTags.whereNonNull];
}
4 changes: 2 additions & 2 deletions lib/features/firebase/services/adapter_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class AdapterService {
}

Stream<String> determineTagsFromBools(SciClubFormModel model) async* {
final tags = await ref.read(remoteTagsRepositoryProvider.future);
final tags = await ref.read(tagsRepositoryProvider.future);
for (final pair in IterableZip([tags, model.tags ?? []])) {
if (pair.length >= 2) {
final tagName = (pair.first as Tag).name;
Expand All @@ -73,7 +73,7 @@ class AdapterService {
}

Stream<bool> determinBoolsFromTags(SciClub model) async* {
final tags = await ref.read(remoteTagsRepositoryProvider.future);
final tags = await ref.read(tagsRepositoryProvider.future);
for (final tag in tags) {
if (model.tags.contains(tag.name)) {
yield true;
Expand Down
1 change: 1 addition & 0 deletions lib/features/form/model/enums.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ enum SciClubType {
(element) => element.value == str,
)
?.key;
String? toJsonVal() => _$SciClubTypeEnumMap[this];
}

@JsonEnum(fieldRename: FieldRename.snake, alwaysCreate: true)
Expand Down
2 changes: 1 addition & 1 deletion lib/features/form/widgets/select_tags.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class SelectTags extends ConsumerWidget {

@override
Widget build(BuildContext context, WidgetRef ref) {
final tags = ref.watch(remoteTagsRepositoryProvider);
final tags = ref.watch(tagsRepositoryProvider);
return switch (tags) {
AsyncLoading() => const Center(
child: CircularProgressIndicator.adaptive(),
Expand Down
4 changes: 2 additions & 2 deletions lib/features/topwr_mockup/config/navigator_config.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import "package:flutter/cupertino.dart";

import "../features/home_view /home_view.dart";
import "../features/sci_clubs_tab/scientific_circles_tab.dart";
import "../features/sci_clubs_tab/science_clubs_view.dart";
import "nav_bar_config.dart";

abstract class NavigatorConfig {
Expand All @@ -20,7 +20,7 @@ abstract class TabsConfig {
home: HomeView(),
mapp: Placeholder(),
faculties: Placeholder(),
sciCircles: ScientificCirclesTab(),
sciCircles: ScienceClubsView(),
parkings: Placeholder(),
info: Placeholder(),
);
Expand Down
7 changes: 6 additions & 1 deletion lib/features/topwr_mockup/config/ui_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ abstract class DetailsScreenHeaderConfig {
static const double logoSize = 130;
}

abstract class ScientificCirclesTabConfig {
abstract class ScienceClubsViewConfig {
static const listSeparatorSize = 16.0;
static const microPadding = 4.0;
static const smallPadding = 16.0;
Expand Down Expand Up @@ -110,3 +110,8 @@ abstract class IParkingConfig {
);
static const extraIndentPadd = EdgeInsets.only(left: 2);
}

abstract class FilterConfig {
static const bottomSheetHeightFactor = 0.65;
static const searchFilterPadding = 15.0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class _StudyCirclesDataList extends ConsumerWidget {
final List<SciClub> studyCircles;

static void goToDetailView(WidgetRef ref, String id) {
ref.read(navigatorProvider).navigateToStudyCircleDetails(id);
ref.read(navigatorProvider).navigateToScienceClubsDetails(id);
}

@override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import "tab_bar_navigator.dart";

/// Extracted logic of navigating to detailed views
extension DetailViewNavigator on NestedNavigator {
void navigateToStudyCircleDetails(String argument) {
void navigateToScienceClubsDetails(String argument) {
unawaited(changeTabBar(NavBarEnum.sciCircles));
unawaited(
navigatorKey.currentState?.pushNamed(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import "package:riverpod_annotation/riverpod_annotation.dart";

import "../../../../../utils/contains_lower_case.dart";
import "../../../../../utils/where_non_null_iterable.dart";
import "../../../../firebase/models/sci_club.dart";
import "../../../../firebase/repositories/sci_clubs_repo.dart";
import "../../science_clubs_filters/filters_controller.dart";

part "science_clubs_view_controller.g.dart";

@riverpod
class SearchScienceClubsController extends _$SearchScienceClubsController {
@override
String build() => "";

void onTextChanged(String newValue) {
state = newValue;
}
}

@riverpod
Future<Iterable<SciClub?>?> _sciClubsFilteredByTextQuery(
_SciClubsFilteredByTextQueryRef ref,
) async {
final originalList = await ref.watch(sciClubsRepoProvider.future);
final query = ref.watch(searchScienceClubsControllerProvider);
return originalList.where(
(element) =>
element.name.containsLowerCase(query) ||
element.department.containsLowerCase(query),
);
}

@riverpod
Future<Iterable<SciClub?>?> scienceClubsListController(
ScienceClubsListControllerRef ref,
) async {
final sciClubs =
(await ref.watch(_sciClubsFilteredByTextQueryProvider.future))
.whereNonNull;

if (!ref.watch(areFiltersEnabledProvider)) {
return sciClubs;
}

final selectedTags =
ref.watch(selectedTagControllerProvider).map((it) => it.name);

final selectedDepartments =
ref.watch(selectedDepartmentControllerProvider).map((it) => it.name);

final selectedTypes =
ref.watch(selectedTypeControllerProvider).map((it) => it.toJson());

final filteredByTypes = selectedTypes.isEmpty
? sciClubs
: sciClubs.whereNonNull.where(
(club) => selectedTypes.contains(club.type?.toJsonVal()),
);

final filteredByDepartments = selectedDepartments.isEmpty
? filteredByTypes
: filteredByTypes.where(
(club) => selectedDepartments.contains(club.department),
);

final filteredByTags = selectedTags.isEmpty
? filteredByDepartments
: filteredByDepartments.where(
(club) => club.tags.whereNonNull.any(selectedTags.contains),
);

return filteredByTags;
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import "package:flutter/material.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";

import "../../../../utils/context_extensions.dart";
import "../../widgets/search_box_app_bar.dart";
import "../science_clubs_filters/widgets/filters_fab.dart";
import "controller/science_clubs_view_controller.dart";
import "widgets/science_clubs_list.dart";

class ScienceClubsView extends ConsumerWidget {
const ScienceClubsView({super.key});

@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: SearchBoxAppBar(
context,
title: context.localize.study_circles,
onQueryChanged: ref
.watch(searchScienceClubsControllerProvider.notifier)
.onTextChanged,
),
floatingActionButton: const FiltersFAB(),
body: const ScienceClubsList(),
);
}
}
Loading

0 comments on commit 0b7a3d9

Please sign in to comment.