feat: Added Date-based filtering and sorting feature for the gallery#249
feat: Added Date-based filtering and sorting feature for the gallery#249Prriiiyankaaa wants to merge 13 commits into
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthroughPR adds gallery sort order (newest/oldest) and date-range filtering (presets: last 30/60/90 days; custom start/end) across backend and frontend. Backend parses dates to UTC bounds, filters ChangesGallery sorting and date-range filtering
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Hi @Abhash-Chakraborty , I have made the required changes, kindly have a look and let me know if any changes required. |
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds date-based filtering and upload-date sorting to the gallery across the frontend (URL/state + UI + API params) and backend (query parsing + SQL filtering), with accompanying tests.
Changes:
- Extend gallery API parameters to include
sort_orderand date range filtering (date_range,date_start,date_end) - Add a new
GalleryDateFilterUI component and integrate it into the gallery page with URL-state support - Add frontend and backend tests for the new sort/date filtering behavior
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| frontend/src/lib/api.ts | Adds sort/date query params and exports shared types for gallery filtering |
| frontend/src/components/gallery-date-filter.tsx | New client component for sort order + date range selection (including custom dates) |
| frontend/src/app/gallery/page.tsx | Wires URL params → typed state → getGallery params; integrates the new filter UI |
| frontend/src/tests/gallery-url-state.test.tsx | Adds URL-state restoration tests for sort/date filtering |
| backend/src/find_api/routers/gallery.py | Implements sort_order and date filtering in /api/gallery |
| backend/tests/test_gallery.py | Adds backend tests covering sorting and date-range filter combinations |
| const getDateRangeParam = (dateRange: DateRangePreset | null): string | null => { | ||
| return dateRange || null; | ||
| }; | ||
|
|
||
| return "all"; | ||
| }; |
| const nextFilter = nextState.filter ?? filter; | ||
| const nextLikedOnly = nextState.likedOnly ?? likedOnly; | ||
| const nextSortOrder = nextState.sortOrder ?? sortOrder; | ||
| const nextDateRange = nextState.dateRange ?? dateRange; | ||
| const nextDateStart = nextState.dateStart ?? dateStart; | ||
| const nextDateEnd = nextState.dateEnd ?? dateEnd; |
| limit?: number; | ||
| status?: MediaStatus; | ||
| liked?: boolean; | ||
| sortOrder?: SortOrder; | ||
| dateRange?: DateRangePreset; | ||
| dateStart?: string; | ||
| dateEnd?: string; |
| const [customStart, setCustomStart] = useState(dateStart || ""); | ||
| const [customEnd, setCustomEnd] = useState(dateEnd || ""); |
| const sortMenuRef = useRef<HTMLDivElement>(null); | ||
| const datePickerRef = useRef<HTMLDivElement>(null); |
| // Handle date range | ||
| const dateRangeParam = getDateRangeParam(nextDateRange); | ||
| if (dateRangeParam) { | ||
| nextParams.set("date_range", dateRangeParam); | ||
| } else { | ||
| nextParams.delete("date_range"); | ||
| } | ||
|
|
||
| if (nextDateStart) { | ||
| nextParams.set("date_start", nextDateStart); | ||
| } else { | ||
| nextParams.delete("date_start"); | ||
| } | ||
|
|
||
| if (nextDateEnd) { | ||
| nextParams.set("date_end", nextDateEnd); | ||
| } else { | ||
| nextParams.delete("date_end"); | ||
| } |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@frontend/src/app/gallery/page.tsx`:
- Around line 208-210: Remove the stray `return "all";` and the extra `};` that
follow the end of `getDateRangeParam` so the file parses correctly; edit the
block after `getDateRangeParam` to delete that standalone return and the stray
closing brace, leaving a single properly-terminated function and allowing
`getStatusParamFromFilter` to start immediately afterward with correct braces.
In `@frontend/src/components/gallery-date-filter.tsx`:
- Around line 28-29: The local state variables customStart/customEnd are only
set once from props (dateStart/dateEnd) and can go stale when URL params change;
add a useEffect in the gallery-date-filter component that watches [dateStart,
dateEnd] and calls setCustomStart(dateStart || "") and setCustomEnd(dateEnd ||
"") to mirror prop updates into local state so the inputs and labels reflect
navigation/deep-link changes.
- Around line 145-151: The component currently calls both onDateRangeChange(...)
and onCustomDateChange(...) for a single user action (e.g., when option.value is
truthy or falsy), causing duplicate router.push calls and race conditions; fix
by emitting a single callback per interaction—either stop calling
onCustomDateChange from the option branch and only call onDateRangeChange with a
unified payload (e.g., include range, start, end) or consolidate into one
exported handler (e.g., a unified setDateFilter(range, start, end)) and update
callers to use that instead; update all places referencing onDateRangeChange and
onCustomDateChange (including the branch around option.value and the similar
47–54 block) so only the single chosen callback updates URL state.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 826b71cd-dbe1-444f-a769-893772a48f4a
📒 Files selected for processing (6)
backend/src/find_api/routers/gallery.pybackend/tests/test_gallery.pyfrontend/src/__tests__/gallery-url-state.test.tsxfrontend/src/app/gallery/page.tsxfrontend/src/components/gallery-date-filter.tsxfrontend/src/lib/api.ts
PR Context Summary
Suggested issue links
Use |
|
@Prriiiyankaaa please fix the CI |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
frontend/src/app/gallery/page.tsx (1)
33-44:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winRun Biome on this file before merge.
frontend-checkis still failing on the@/lib/apiimport ordering and formatter output in this hunk, so this file will keep CI red until the Biome fixes are applied. As per coding guidelines,frontend/src/**/*.{ts,tsx}: Frontend code uses TypeScript strict mode, 2-space indentation, double quotes, and Biome formatting.Also applies to: 175-185, 204-206, 253-253, 370-372, 426-426
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/app/gallery/page.tsx` around lines 33 - 44, Run Biome on frontend/src/app/gallery/page.tsx and fix formatting and import ordering for the import block that currently lists api, deleteImage, deleteImagesBulk, DateRangePreset, GalleryResponse, SortOrder, getGallery, getImageDetail, reprocessImage, and toggleLike from "`@/lib/api`"; ensure Biome's rules are applied (TypeScript strict settings, 2-space indentation, double quotes) and reorder/format the import to satisfy the linter/formatter so frontend-check passes for the reported hunks (including the other affected ranges 175-185, 204-206, 253, 370-372, 426).frontend/src/components/gallery-date-filter.tsx (1)
194-211:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAssociate the custom date controls with accessible labels.
The standalone
labelat Line 194 is not bound to either date input, so CI fails and screen readers won't get a proper name for these fields. Use afieldset/legendfor the group, or add separatelabel+idpairs for each input.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/components/gallery-date-filter.tsx` around lines 194 - 211, The "Custom date range" label is not associated with the two date inputs (customStart/customEnd), causing accessibility CI failure; update the JSX in gallery-date-filter.tsx to either wrap the inputs in a fieldset with a legend or add individual <label for="..."> paired with unique ids on the date inputs (e.g., id="custom-start" and id="custom-end") and keep the existing onChange handlers (setCustomStart/setCustomEnd) intact so screen readers can correctly announce each control.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@frontend/src/components/gallery-date-filter.tsx`:
- Around line 3-5: This file's imports and formatting violate the project's
Biome rules (import ordering, 2-space indentation, double quotes); run Biome to
auto-fix formatting and import order for
frontend/src/components/gallery-date-filter.tsx (affecting the import statements
for ChevronDown, Calendar, useEffect, useRef, useState and the type imports
DateRangePreset, SortOrder) and reformat the other affected hunks so the file
conforms to the frontend TypeScript style (TypeScript strict mode, 2-space
indent, double quotes) before merging.
---
Outside diff comments:
In `@frontend/src/app/gallery/page.tsx`:
- Around line 33-44: Run Biome on frontend/src/app/gallery/page.tsx and fix
formatting and import ordering for the import block that currently lists api,
deleteImage, deleteImagesBulk, DateRangePreset, GalleryResponse, SortOrder,
getGallery, getImageDetail, reprocessImage, and toggleLike from "`@/lib/api`";
ensure Biome's rules are applied (TypeScript strict settings, 2-space
indentation, double quotes) and reorder/format the import to satisfy the
linter/formatter so frontend-check passes for the reported hunks (including the
other affected ranges 175-185, 204-206, 253, 370-372, 426).
In `@frontend/src/components/gallery-date-filter.tsx`:
- Around line 194-211: The "Custom date range" label is not associated with the
two date inputs (customStart/customEnd), causing accessibility CI failure;
update the JSX in gallery-date-filter.tsx to either wrap the inputs in a
fieldset with a legend or add individual <label for="..."> paired with unique
ids on the date inputs (e.g., id="custom-start" and id="custom-end") and keep
the existing onChange handlers (setCustomStart/setCustomEnd) intact so screen
readers can correctly announce each control.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 00bfc8f7-9036-4eb5-a194-f9442398ca92
📒 Files selected for processing (2)
frontend/src/app/gallery/page.tsxfrontend/src/components/gallery-date-filter.tsx
Prevent race condition where onDateRangeChange and onCustomDateChange were called sequentially, causing two router.push calls. Now uses single onDateFilterChange callback to update all date params atomically.
ApprovabilityVerdict: Needs human review 1 blocking correctness issue found. This PR introduces a new date filtering and sorting feature with significant new frontend components, state management, and backend logic. Multiple open review comments identify potential bugs including a race condition on initial render and issues with clearing filters, which require human attention before merging. You can customize Macroscope's approvability policy. Learn more. |
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
frontend/src/components/gallery-date-filter.tsx (1)
73-80:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftAvoid emitting two parent updates for one date-filter action.
Line 75–76 and Line 172–176 still fire both callbacks per click. With current page wiring, that can trigger back-to-back
router.pushcalls and drop eitherdate_rangeor custom dates depending on timing/order.Suggested direction
- onDateRangeChange: (preset: DateRangePreset | null) => void; - onCustomDateChange: (start: string | null, end: string | null) => void; + onDateFilterChange: (payload: { + dateRange: DateRangePreset | null; + dateStart: string | null; + dateEnd: string | null; + }) => void;Then emit exactly one callback in preset/apply/clear handlers so URL updates are atomic.
Also applies to: 170-177
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/components/gallery-date-filter.tsx` around lines 73 - 80, The handler currently calls both onDateRangeChange and onCustomDateChange which causes duplicate parent updates; change handleCustomDateApply to emit a single callback (choose one: call only onCustomDateChange(customStart||null, customEnd||null) for apply and onCustomDateChange(null,null) for clear, or alternatively call only onDateRangeChange("custom")/null and remove the other) so only one parent update occurs; apply the same fix to the other preset/apply/clear handlers (the blocks around lines where preset handlers call both onDateRangeChange and onCustomDateChange) so each action invokes exactly one callback (use the same chosen callback consistently).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@backend/src/find_api/routers/gallery.py`:
- Around line 123-125: The date bounds logic must detect when both parsed dates
exist and start_date > end_date and normalize them (swap values) before
returning or building filters; update the branch that currently returns
start_date, end_date (the block using start_date/end_date derived from
date_start/date_end) to check "if start_date and end_date and start_date >
end_date" and swap the two variables (or raise a clear validation error) prior
to returning; apply the same normalization to the other identical block around
the code referenced at lines 181-189 so all date-range paths handle reversed
inputs consistently.
In `@frontend/src/app/gallery/page.tsx`:
- Around line 204-205: getDateRangeParam's signature and implementation must
accept nullable values to satisfy strictNullChecks: change the parameter type
from DateRangePreset | undefined to DateRangePreset | null | undefined and
ensure the function returns the string value or null (e.g., return dateRange ??
null) so callers like buildGalleryHref (and its nextState.dateRange /
nextDateRange) can pass null safely; update the function declaration for
getDateRangeParam and its return handling accordingly.
---
Duplicate comments:
In `@frontend/src/components/gallery-date-filter.tsx`:
- Around line 73-80: The handler currently calls both onDateRangeChange and
onCustomDateChange which causes duplicate parent updates; change
handleCustomDateApply to emit a single callback (choose one: call only
onCustomDateChange(customStart||null, customEnd||null) for apply and
onCustomDateChange(null,null) for clear, or alternatively call only
onDateRangeChange("custom")/null and remove the other) so only one parent update
occurs; apply the same fix to the other preset/apply/clear handlers (the blocks
around lines where preset handlers call both onDateRangeChange and
onCustomDateChange) so each action invokes exactly one callback (use the same
chosen callback consistently).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: dec92fae-752d-409f-9ece-c2ef2c5d6483
📒 Files selected for processing (9)
backend/src/find_api/models/person.pybackend/src/find_api/routers/gallery.pybackend/src/find_api/routers/vault.pybackend/tests/test_gallery.pybackend/tests/test_hybrid_embedding.pyfrontend/src/__tests__/gallery-url-state.test.tsxfrontend/src/app/gallery/page.tsxfrontend/src/components/gallery-date-filter.tsxfrontend/src/lib/api.ts
💤 Files with no reviewable changes (1)
- backend/tests/test_gallery.py
✅ Files skipped from review due to trivial changes (3)
- backend/tests/test_hybrid_embedding.py
- backend/src/find_api/routers/vault.py
- backend/src/find_api/models/person.py
- Fix strict-null type mismatch: getDateRangeParam now accepts null in addition to undefined/DateRangePreset - Handle reversed custom date bounds: normalize (swap) dates when start_date > end_date
When custom_start and custom_end are reversed and swapped, also correct the time components so the earlier date has 00:00:00 (start of day) and the later date has 23:59:59.999999 (end of day). Previously, time components were not adjusted after swap, causing filters to exclude records on boundary dates.
- Fix line wrapping for long function signatures and array literals - Reorganize imports to group values before types - Update test mock to use new onDateFilterChange callback signature - Wrap long attribute values and conditional expressions
|
@Prriiiyankaaa Please dont push anything I will fix it |
- Wrap nextDateRange, nextDateStart, nextDateEnd ternary expressions - Wrap dependency array across multiple lines for better readability
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/src/app/gallery/page.tsx (1)
277-286:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winGuard
dateStart/dateEndbehinddateRange === "custom"before callinggetGallery.Right now
dateStart/dateEndare forwarded even when the active range is not custom. That can leak malformed URL state into API calls and create inconsistent filtering behavior.Suggested fix
getGallery({ page: typeof pageParam === "number" ? pageParam : 1, limit: GALLERY_LIMIT, status: filter === "all" ? undefined : filter, liked: likedOnly ? true : undefined, sortOrder, dateRange, - dateStart: dateStart || undefined, - dateEnd: dateEnd || undefined, + dateStart: dateRange === "custom" ? (dateStart || undefined) : undefined, + dateEnd: dateRange === "custom" ? (dateEnd || undefined) : undefined, }),🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/app/gallery/page.tsx` around lines 277 - 286, The call to getGallery currently forwards dateStart and dateEnd regardless of dateRange, which can leak invalid/malformed URL state; update the getGallery invocation (the object passed into getGallery) to only include dateStart and dateEnd when dateRange === "custom" (otherwise pass undefined or omit them), i.e., gate both dateStart and dateEnd behind the condition dateRange === "custom" so they are not forwarded for non-custom ranges.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@frontend/src/app/gallery/page.tsx`:
- Around line 277-286: The call to getGallery currently forwards dateStart and
dateEnd regardless of dateRange, which can leak invalid/malformed URL state;
update the getGallery invocation (the object passed into getGallery) to only
include dateStart and dateEnd when dateRange === "custom" (otherwise pass
undefined or omit them), i.e., gate both dateStart and dateEnd behind the
condition dateRange === "custom" so they are not forwarded for non-custom
ranges.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: b6a021f5-5b14-4db4-8098-9c16b3098639
📒 Files selected for processing (5)
backend/src/find_api/routers/gallery.pyfrontend/src/__tests__/gallery-url-state.test.tsxfrontend/src/app/gallery/page.tsxfrontend/src/components/gallery-date-filter.tsxfrontend/src/lib/api.ts
|
@Abhash-Chakraborty alright. Let me know if anything required from my side. |
|
@macroscope-app review Please review this PR against its linked issue, local-first privacy rules, and the current Find repo instructions. |
|
Images are overlapping. |
| const searchParams = useSearchParams(); | ||
| const filter = getFilterFromStatusParam(searchParams.get("status")); | ||
| const likedOnly = searchParams.get("liked") === "true"; | ||
| const filter = galleryStore((state) => state.filter); |
There was a problem hiding this comment.
🟡 Medium gallery/page.tsx:251
On initial render, useInfiniteQuery fires with store defaults (filter: "all", sortOrder: "newest") before useEffect (lines 270-272) syncs URL params to the store. This causes an incorrect network request and caches wrong data under the wrong query key, then immediately refetches with correct params when the effect runs. The query should read directly from parsedGalleryFilters (derived from searchParams) on first render instead of the store, ensuring the initial request uses URL state.
🤖 Copy this AI Prompt to have your agent fix this:
In file frontend/src/app/gallery/page.tsx around line 251:
On initial render, `useInfiniteQuery` fires with store defaults (`filter: "all"`, `sortOrder: "newest"`) before `useEffect` (lines 270-272) syncs URL params to the store. This causes an incorrect network request and caches wrong data under the wrong query key, then immediately refetches with correct params when the effect runs. The query should read directly from `parsedGalleryFilters` (derived from `searchParams`) on first render instead of the store, ensuring the initial request uses URL state.
Evidence trail:
frontend/src/app/gallery/page.tsx lines 251-256 (store selectors), lines 258-268 (parsedGalleryFilters useMemo from URL), lines 270-272 (useEffect syncing URL to store — runs after render), lines 276-284 (query key from store), lines 293-305 (useInfiniteQuery using store values). frontend/src/store/galleryStore.ts lines 20-27 (defaults: filter: 'all', sortOrder: 'newest').


Summary
Closes #215
Adds upload-date sorting and date-range filtering to the gallery so larger local libraries are easier to browse when users remember roughly when an image was uploaded.
What changed
sort_ordersupport to/api/gallerywithnewestandoldestoptions.last_30_days,last_60_days,last_90_days, and customdate_start/date_endranges.Testing
uv run ruff check .uv run ruff format --check .uv run pytest tests/test_gallery.py -qpnpm exec vitest run src/__tests__/gallery-url-state.test.tsxNotes
The PR is scoped to gallery upload-date filtering and sorting. Unrelated backend formatting changes were removed.
Note
Add date-based filtering and sort order controls to the gallery
sort_order(newest/oldest) anddate_range(last_30_days,last_60_days,last_90_days,custom) query parameters to the/gallerybackend endpoint, filtering onMedia.created_at.parse_date_rangeutility in gallery.py that handles preset and custom date ranges, normalizing bounds and returning UTC-aware datetimes; invalid custom dates return HTTP 422.GalleryDateFilterUI component with sort and date range dropdowns, including custom start/end date inputs.getGalleryAPI calls in page.tsx and api.ts.date_start/date_endURL params are only set whendate_range=customand are cleared otherwise;sort_orderdefaults tonewestand is omitted from the URL when at its default value.Macroscope summarized b802d57.
Summary by CodeRabbit
Release Notes