Skip to content

Commit

Permalink
Merge pull request #2338 from acterglobal/kumar/generic-event-list
Browse files Browse the repository at this point in the history
Create generic event list widget and also manage it with Search Page
  • Loading branch information
kumarpalsinh25 authored Nov 5, 2024
2 parents b937d58 + 664b0c6 commit 4d77a69
Show file tree
Hide file tree
Showing 20 changed files with 829 additions and 332 deletions.
2 changes: 2 additions & 0 deletions .changes/2338-search-page-includes-events.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- [New] : Search page now also includes Events Search.
- [New] : Search page now have Quick Filters which make your searching experience event better
3 changes: 0 additions & 3 deletions app/lib/common/widgets/event/event_selector_drawer.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'package:acter/features/events/providers/event_providers.dart';
import 'package:acter/features/events/providers/event_type_provider.dart';
import 'package:acter/features/events/widgets/event_item.dart';
import 'package:acter/features/events/widgets/skeletons/event_list_skeleton_widget.dart';
import 'package:atlas_icons/atlas_icons.dart';
Expand Down Expand Up @@ -63,8 +62,6 @@ Future<String?> selectEventDrawer({
itemBuilder: (context, index) => EventItem(
event: calEvents[index],
isShowRsvp: false,
eventType:
ref.read(eventTypeProvider(calEvents[index])),
onTapEventItem: (event) {
Navigator.pop(context, event);
},
Expand Down
173 changes: 52 additions & 121 deletions app/lib/features/events/pages/event_list_page.dart
Original file line number Diff line number Diff line change
@@ -1,44 +1,38 @@
import 'dart:math';

import 'package:acter/common/providers/common_providers.dart';
import 'package:acter/common/providers/space_providers.dart';
import 'package:acter/common/toolkit/buttons/primary_action_button.dart';
import 'package:acter/common/toolkit/errors/error_page.dart';
import 'package:acter/common/utils/routes.dart';
import 'package:acter/common/widgets/acter_search_widget.dart';
import 'package:acter/common/widgets/add_button_with_can_permission.dart';
import 'package:acter/common/widgets/empty_state_widget.dart';
import 'package:acter/common/widgets/space_name_widget.dart';
import 'package:acter/features/events/providers/event_providers.dart';
import 'package:acter/features/events/providers/event_type_provider.dart';
import 'package:acter/features/events/widgets/event_item.dart';
import 'package:acter/features/events/widgets/skeletons/event_list_skeleton_widget.dart';
import 'package:acter_flutter_sdk/acter_flutter_sdk_ffi.dart';
import 'package:acter/features/events/widgets/event_list_empty_state.dart';
import 'package:acter/features/events/widgets/event_list_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:go_router/go_router.dart';
import 'package:logging/logging.dart';

final _log = Logger('a3::cal_event::list');

class EventListPage extends ConsumerStatefulWidget {
final String? spaceId;
final String? searchQuery;

const EventListPage({
super.key,
this.spaceId,
this.searchQuery,
});

@override
ConsumerState<EventListPage> createState() => _EventListPageState();
}

class _EventListPageState extends ConsumerState<EventListPage> {
String get searchValue => ref.watch(searchValueProvider);

EventFilters get eventFilterValue => ref.watch(eventFilterProvider);
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((Duration duration) {
ref.read(eventListSearchTermProvider(widget.spaceId).notifier).state =
widget.searchQuery ?? '';
});
}

@override
Widget build(BuildContext context) {
Expand Down Expand Up @@ -74,42 +68,34 @@ class _EventListPageState extends ConsumerState<EventListPage> {
}

Widget _buildBody() {
final calEventsLoader = ref.watch(
eventListSearchFilterProvider(
(spaceId: widget.spaceId, searchText: searchValue),
),
);

return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
ActerSearchWidget(
initialText: widget.searchQuery,
onChanged: (value) {
final notifier = ref.read(searchValueProvider.notifier);
notifier.state = value;
ref
.read(eventListSearchTermProvider(widget.spaceId).notifier)
.state = value;
},
onClear: () {
ref
.read(eventListSearchTermProvider(widget.spaceId).notifier)
.state = '';
},
onClear: () => ref.read(searchValueProvider.notifier).state = '',
),
filterChipsButtons(),
Expanded(
child: calEventsLoader.when(
data: (calEvents) => _buildEventList(calEvents),
error: (error, stack) {
_log.severe('Failed to search events in space', error, stack);
return ErrorPage(
background: const EventListSkeleton(),
error: error,
stack: stack,
onRetryTap: () {
ref.invalidate(
eventListSearchFilterProvider(
(spaceId: widget.spaceId, searchText: searchValue),
),
);
},
);
},
loading: () => const EventListSkeleton(),
child: EventListWidget(
isShowSpaceName: widget.spaceId == null,
shrinkWrap: false,
listProvider: eventListSearchedAndFilterProvider(widget.spaceId),
emptyStateBuilder: () => EventListEmptyState(
spaceId: widget.spaceId,
isSearchApplied: ref
.read(eventListSearchTermProvider(widget.spaceId))
.isNotEmpty,
),
),
),
],
Expand All @@ -118,6 +104,7 @@ class _EventListPageState extends ConsumerState<EventListPage> {

Widget filterChipsButtons() {
final lang = L10n.of(context);
final currentFilter = ref.watch(eventListFilterProvider(widget.spaceId));
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Container(
Expand All @@ -128,103 +115,47 @@ class _EventListPageState extends ConsumerState<EventListPage> {
child: Wrap(
children: [
FilterChip(
selected: eventFilterValue == EventFilters.all,
selected: currentFilter == EventFilters.all,
label: Text(lang.all),
onSelected: (value) {
final notifier = ref.read(eventFilterProvider.notifier);
notifier.state = EventFilters.all;
},
onSelected: (value) => ref
.read(eventListFilterProvider(widget.spaceId).notifier)
.state = EventFilters.all,
),
const SizedBox(width: 10),
FilterChip(
selected: eventFilterValue == EventFilters.bookmarked,
selected: currentFilter == EventFilters.bookmarked,
label: Text(lang.bookmarked),
onSelected: (value) {
final notifier = ref.read(eventFilterProvider.notifier);
notifier.state = EventFilters.bookmarked;
},
onSelected: (value) => ref
.read(eventListFilterProvider(widget.spaceId).notifier)
.state = EventFilters.bookmarked,
),
const SizedBox(width: 10),
FilterChip(
selected: eventFilterValue == EventFilters.ongoing,
selected: currentFilter == EventFilters.ongoing,
label: Text(lang.happeningNow),
onSelected: (value) {
final notifier = ref.read(eventFilterProvider.notifier);
notifier.state = EventFilters.ongoing;
},
onSelected: (value) => ref
.read(eventListFilterProvider(widget.spaceId).notifier)
.state = EventFilters.ongoing,
),
const SizedBox(width: 10),
FilterChip(
selected: eventFilterValue == EventFilters.upcoming,
selected: currentFilter == EventFilters.upcoming,
label: Text(lang.upcoming),
onSelected: (value) {
final notifier = ref.read(eventFilterProvider.notifier);
notifier.state = EventFilters.upcoming;
},
onSelected: (value) => ref
.read(eventListFilterProvider(widget.spaceId).notifier)
.state = EventFilters.upcoming,
),
const SizedBox(width: 10),
FilterChip(
selected: eventFilterValue == EventFilters.past,
selected: currentFilter == EventFilters.past,
label: Text(lang.past),
onSelected: (value) {
final notifier = ref.read(eventFilterProvider.notifier);
notifier.state = EventFilters.past;
},
onSelected: (value) => ref
.read(eventListFilterProvider(widget.spaceId).notifier)
.state = EventFilters.past,
),
],
),
),
);
}

Widget _buildEventList(List<CalendarEvent> events) {
final size = MediaQuery.of(context).size;
final widthCount = (size.width ~/ 500).toInt();
const int minCount = 2;

if (events.isEmpty) return _buildEventsEmptyState();

return SingleChildScrollView(
child: StaggeredGrid.count(
crossAxisCount: max(1, min(widthCount, minCount)),
children: [
for (final event in events)
EventItem(
event: event,
isShowSpaceName: widget.spaceId == null,
eventType: ref.watch(eventTypeProvider(event)),
),
],
),
);
}

Widget _buildEventsEmptyState() {
var canAdd = false;
if (searchValue.isEmpty) {
final canPostLoader =
ref.watch(hasSpaceWithPermissionProvider('CanPostEvent'));
if (canPostLoader.valueOrNull == true) canAdd = true;
}
final lang = L10n.of(context);
return Center(
heightFactor: 1,
child: EmptyState(
title: searchValue.isNotEmpty
? lang.noMatchingEventsFound
: lang.noEventsFound,
subtitle: lang.noEventAvailableDescription,
image: 'assets/images/empty_event.svg',
primaryButton: canAdd
? ActerPrimaryActionButton(
onPressed: () => context.pushNamed(
Routes.createEvent.name,
queryParameters: {'spaceId': widget.spaceId},
),
child: Text(lang.addEvent),
)
: null,
),
);
}
}
Loading

0 comments on commit 4d77a69

Please sign in to comment.