Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/lib/backend/http/api/conversations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,8 @@ Future<(List<ServerConversation>, int, int)> searchConversationsServer(
int? page,
int? limit,
bool includeDiscarded = true,
DateTime? startDate,
DateTime? endDate,
}) async {
Logger.debug(Env.apiBaseUrl);
var response = await makeApiCall(
Expand All @@ -531,6 +533,8 @@ Future<(List<ServerConversation>, int, int)> searchConversationsServer(
'page': page ?? 1,
'per_page': limit ?? 10,
'include_discarded': includeDiscarded,
if (startDate != null) 'start_date': startDate.toIso8601String(),
if (endDate != null) 'end_date': endDate.toIso8601String(),
}),
);
if (response == null) return (<ServerConversation>[], 0, 0);
Expand Down
48 changes: 33 additions & 15 deletions app/lib/pages/conversations/widgets/search_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -73,22 +73,25 @@ class _SearchWidgetState extends State<SearchWidget> {
}
}

Future<void> _showDatePicker(BuildContext context, {bool hasExistingFilter = false}) async {
Future<void> _showDateRangePicker(BuildContext context, {bool hasExistingFilter = false}) async {
final convoProvider = Provider.of<ConversationProvider>(context, listen: false);
DateTime selectedDate = convoProvider.selectedDate ?? DateTime.now();
DateTime? startDate = convoProvider.searchStartDate;
DateTime? endDate = convoProvider.searchEndDate;
List<DateTime?> selectedRange = [startDate, endDate];
await showCupertinoModalPopup<void>(
context: context,
builder: (BuildContext context) {
return Container(
height: 420,
height: 480,
padding: const EdgeInsets.only(top: 6.0),
margin: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
color: const Color(0xFF1F1F25),
child: SafeArea(
top: false,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Header with Cancel and Done buttons
// Header with Cancel and Apply buttons
Container(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
decoration: const BoxDecoration(
Expand All @@ -104,7 +107,10 @@ class _SearchWidgetState extends State<SearchWidget> {
if (hasExistingFilter) {
final provider = Provider.of<ConversationProvider>(context, listen: false);
Navigator.of(context).pop();
await provider.clearDateFilter();
provider.clearSearchDateRange();
if (provider.previousQuery.isNotEmpty) {
await provider.searchConversations(provider.previousQuery);
}
PlatformManager.instance.analytics.calendarFilterCleared();
} else {
Navigator.of(context).pop();
Expand All @@ -115,14 +121,23 @@ class _SearchWidgetState extends State<SearchWidget> {
style: const TextStyle(color: Colors.white, fontSize: 16),
),
),
const Spacer(),
Text(
'Filter by date',
style: TextStyle(color: Colors.grey.shade400, fontSize: 14),
),
CupertinoButton(
padding: EdgeInsets.zero,
onPressed: () async {
final provider = Provider.of<ConversationProvider>(context, listen: false);
Navigator.of(context).pop();
await provider.filterConversationsByDate(selectedDate);
PlatformManager.instance.analytics.calendarFilterApplied(selectedDate);
if (provider.previousQuery.isNotEmpty) {
provider.setSearchDateRange(startDate, endDate);
await provider.searchConversations(provider.previousQuery);
}
final appliedStart = startDate;
if (appliedStart != null) {
PlatformManager.instance.analytics.calendarFilterApplied(appliedStart);
}
},
child: Text(
context.l10n.done,
Expand All @@ -132,7 +147,7 @@ class _SearchWidgetState extends State<SearchWidget> {
],
),
),
// Date picker
// Date range picker
Expanded(
child: Material(
color: ResponsiveHelper.backgroundSecondary,
Expand All @@ -141,11 +156,13 @@ class _SearchWidgetState extends State<SearchWidget> {
firstDate: DateTime(2020),
lastDate: DateTime.now(),
currentDate: DateTime.now(),
calendarType: CalendarDatePicker2Type.range,
),
value: [selectedDate],
value: selectedRange,
onValueChanged: (dates) {
if (dates.isNotEmpty) {
selectedDate = dates[0];
startDate = dates[0];
endDate = dates.length > 1 ? dates[1] : null;
}
},
),
Expand Down Expand Up @@ -217,25 +234,26 @@ class _SearchWidgetState extends State<SearchWidget> {
// Calendar button - same height as search bar (48px)
Consumer<ConversationProvider>(
builder: (context, convoProvider, _) {
final hasActiveFilter = convoProvider.searchStartDate != null;
return Container(
width: 48,
height: 48,
decoration: BoxDecoration(
color: convoProvider.selectedDate != null
color: hasActiveFilter
? Colors.deepPurple.withValues(alpha: 0.5)
: const Color(0xFF1F1F25),
borderRadius: BorderRadius.circular(24),
),
child: IconButton(
padding: EdgeInsets.zero,
icon: Icon(
convoProvider.selectedDate != null ? FontAwesomeIcons.calendarDay : FontAwesomeIcons.calendarDays,
hasActiveFilter ? FontAwesomeIcons.calendarDay : FontAwesomeIcons.calendarDays,
size: 18,
color: convoProvider.selectedDate != null ? Colors.white : Colors.white70,
color: hasActiveFilter ? Colors.white : Colors.white70,
),
onPressed: () async {
HapticFeedback.mediumImpact();
await _showDatePicker(context, hasExistingFilter: convoProvider.selectedDate != null);
await _showDateRangePicker(context, hasExistingFilter: hasActiveFilter);
},
),
);
Expand Down
28 changes: 27 additions & 1 deletion app/lib/providers/conversation_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ class ConversationProvider extends ChangeNotifier {
int totalSearchPages = 1;
int currentSearchPage = 1;

DateTime? searchStartDate;
DateTime? searchEndDate;

Timer? _processingConversationWatchTimer;

// Add debounce mechanism for refresh
Expand Down Expand Up @@ -103,6 +106,8 @@ class ConversationProvider extends ChangeNotifier {
hasDailySummaries = false;
selectedDate = null;
selectedFolderId = null;
searchStartDate = null;
searchEndDate = null;
previousQuery = '';
totalSearchPages = 1;
currentSearchPage = 1;
Expand Down Expand Up @@ -152,7 +157,12 @@ class ConversationProvider extends ChangeNotifier {
}

previousQuery = query;
var (convos, current, total) = await searchConversationsServer(query, includeDiscarded: showDiscardedConversations);
var (convos, current, total) = await searchConversationsServer(
query,
includeDiscarded: showDiscardedConversations,
startDate: searchStartDate,
endDate: searchEndDate,
);
convos.sort((a, b) => (b.startedAt ?? b.createdAt).compareTo(a.startedAt ?? a.createdAt));
searchedConversations = convos;
currentSearchPage = current;
Expand All @@ -177,6 +187,8 @@ class ConversationProvider extends ChangeNotifier {
previousQuery,
page: currentSearchPage + 1,
includeDiscarded: showDiscardedConversations,
startDate: searchStartDate,
endDate: searchEndDate,
);
searchedConversations.addAll(newConvos);
searchedConversations.sort((a, b) => (b.startedAt ?? b.createdAt).compareTo(a.startedAt ?? a.createdAt));
Expand Down Expand Up @@ -517,6 +529,20 @@ class ConversationProvider extends ChangeNotifier {
}).toList();
}

/// Set search date range (start and end). Null = no limit on that side.
void setSearchDateRange(DateTime? start, DateTime? end) {
searchStartDate = start;
searchEndDate = end;
notifyListeners();
}

/// Clear the search date range filter
void clearSearchDateRange() {
searchStartDate = null;
searchEndDate = null;
notifyListeners();
}

/// Filter conversations by a specific date
Future<void> filterConversationsByDate(DateTime date) async {
selectedDate = date;
Expand Down