Skip to content

Commit b6b9a0c

Browse files
committed
docs: plan
1 parent 1e9fb2d commit b6b9a0c

1 file changed

Lines changed: 156 additions & 0 deletions

File tree

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
# Feature: "X of Y" Event Count in Search Bar
2+
3+
**GitHub Issue:** [#241](https://github.com/williscool/CalendarNotification/issues/241)
4+
**Parent Doc:** [event_lookahead_milestone3_filter_pills.md](./event_lookahead_milestone3_filter_pills.md)
5+
6+
## Overview
7+
8+
When filter pills (Calendar, Status, Time) reduce the event list, the search bar hint should show "X of Y" so the user knows they're viewing a subset.
9+
10+
## Current State
11+
12+
| Feature | Status |
13+
|---------|--------|
14+
| Search hint shows event count |`"Search 70 Active events..."` |
15+
| Count reflects filter pills | ✅ Count already updates after filtering |
16+
| User can tell it's a subset | ❌ No indication that 70 is a filtered view of a larger list |
17+
18+
### Current Flow
19+
20+
1. Fragment calls `filterState.filterEvents(db.eventsForDisplay, now)` → filtered events
21+
2. Adapter stores filtered events as `allEvents`
22+
3. `getEventCount()` returns `allEvents.size` (post-filter count)
23+
4. `MainActivityModern.onCreateOptionsMenu()` uses that count for the hint
24+
5. Hint: `"Search %d %s events..."``"Search 70 Active events..."`
25+
26+
The total (unfiltered) count is never tracked — it's discarded during filtering.
27+
28+
## Goal
29+
30+
When filter pills are active and reduce the count:
31+
32+
```
33+
Search 70 of 200 Active events...
34+
```
35+
36+
When no filters are active (or filters match everything):
37+
38+
```
39+
Search 200 Active events...
40+
```
41+
42+
The hint is only visible when the search box is empty (Android replaces it with typed text), so text-search filtering is irrelevant.
43+
44+
## Design Decisions
45+
46+
| Decision | Choice | Rationale |
47+
|----------|--------|-----------|
48+
| Text format | `"Search X of Y Tab events..."` | Clear, concise, standard "X of Y" pattern |
49+
| When to show "of Y" | Only when `hasActiveFilters() && filtered != total` | Don't show "70 of 70" when filters are active but match everything |
50+
| What counts as "total" | Unfiltered DB count for the tab | The number before any filter pills are applied |
51+
| Text search interaction | N/A | Hint is not visible while typing — only filter pills matter |
52+
| Plural key | Keys off filtered count (`X`) | "Search 1 of 200 Active event..." vs "Search 5 of 200 Active events..." |
53+
54+
## Implementation
55+
56+
### Phase 1: Track Total (Unfiltered) Event Count
57+
58+
**Goal:** Each fragment remembers the total event count before filtering.
59+
60+
**Files:**
61+
- `SearchableFragment.kt` — add `getTotalEventCount()` with default impl
62+
- `ActiveEventsFragment.kt` — track and expose total count
63+
- `UpcomingEventsFragment.kt` — track and expose total count
64+
- `DismissedEventsFragment.kt` — track and expose total count
65+
66+
**SearchableFragment.kt** — new method:
67+
68+
```kotlin
69+
/** Get total event count before filter pills (for "X of Y" hint) */
70+
fun getTotalEventCount(): Int = getEventCount()
71+
```
72+
73+
**Each fragment's `loadEvents()`** — capture total before filtering:
74+
75+
```kotlin
76+
// ActiveEventsFragment.loadEvents() example
77+
val allDbEvents = db.eventsForDisplay
78+
totalEventCount = allDbEvents.size // new field
79+
val events = filterState.filterEvents(allDbEvents, now)
80+
```
81+
82+
Add a `private var totalEventCount: Int = 0` field to each fragment and override `getTotalEventCount()`.
83+
84+
### Phase 2: Conditional Search Hint
85+
86+
**Goal:** Show "X of Y" format when filters reduce the count.
87+
88+
**File:** `MainActivityModern.kt` (lines ~256-263)
89+
90+
Replace:
91+
92+
```kotlin
93+
val count = currentFragment?.getEventCount() ?: 0
94+
searchView?.queryHint = resources.getQuantityString(
95+
R.plurals.search_placeholder, count, count, tabName)
96+
```
97+
98+
With:
99+
100+
```kotlin
101+
val count = currentFragment?.getEventCount() ?: 0
102+
val totalCount = currentFragment?.getTotalEventCount() ?: count
103+
val hasActiveFilters = getCurrentFilterState().hasActiveFilters()
104+
105+
searchView?.queryHint = if (hasActiveFilters && count != totalCount) {
106+
resources.getQuantityString(
107+
R.plurals.search_placeholder_filtered, count, count, totalCount, tabName)
108+
} else {
109+
resources.getQuantityString(
110+
R.plurals.search_placeholder, count, count, tabName)
111+
}
112+
```
113+
114+
### Phase 3: String Resources
115+
116+
**File:** `values/strings.xml`
117+
118+
```xml
119+
<plurals name="search_placeholder_filtered">
120+
<item quantity="one">Search %1$d of %3$d %4$s event...</item>
121+
<item quantity="other">Search %1$d of %3$d %4$s events...</item>
122+
</plurals>
123+
```
124+
125+
Note: `%2$d` is skipped intentionally — `getQuantityString(id, quantity, arg1, arg2, arg3)` uses `quantity` for plural selection and `%1$d`/`%3$d`/`%4$s` for formatting. Keeping arg order consistent: filtered count, total count, tab name.
126+
127+
**File:** `values-fr/strings.xml`
128+
129+
```xml
130+
<plurals name="search_placeholder_filtered">
131+
<item quantity="one">Rechercher %1$d sur %3$d événement %4$s…</item>
132+
<item quantity="other">Rechercher %1$d sur %3$d événements %4$s…</item>
133+
</plurals>
134+
```
135+
136+
## Testing
137+
138+
### Unit/Instrumentation Tests
139+
140+
- Search hint shows standard format when no filters active
141+
- Search hint shows "X of Y" format when filter pills reduce count
142+
- Search hint shows standard format when filters are active but match all events (count == totalCount)
143+
- `getTotalEventCount()` returns correct unfiltered count
144+
- `getEventCount()` returns correct filtered count (existing behavior, regression check)
145+
146+
## Files Changed Summary
147+
148+
| File | Change |
149+
|------|--------|
150+
| `SearchableFragment.kt` | Add `getTotalEventCount()` default method |
151+
| `ActiveEventsFragment.kt` | Track `totalEventCount`, override `getTotalEventCount()` |
152+
| `UpcomingEventsFragment.kt` | Track `totalEventCount`, override `getTotalEventCount()` |
153+
| `DismissedEventsFragment.kt` | Track `totalEventCount`, override `getTotalEventCount()` |
154+
| `MainActivityModern.kt` | Conditional hint text in `onCreateOptionsMenu()` |
155+
| `values/strings.xml` | Add `search_placeholder_filtered` plural |
156+
| `values-fr/strings.xml` | Add French translation |

0 commit comments

Comments
 (0)