Move explore tabs into home page sidebar#605
Conversation
📝 WalkthroughWalkthroughRefactors Explore UI: removes ExploreDrawer, introduces ExplorePageContainer and ExploreSearch in ExplorePane, updates ExploreBots/ExploreServers/ExploreThemes to use the new layout, adds optional Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Router
participant HomeDrawer
participant ExploreRoutes
participant ExplorePage
participant ExplorePane
User->>Router: navigate to /app/explore/servers
Router->>HomeDrawer: render (route context)
HomeDrawer->>ExploreRoutes: read routes (uses optional match)
ExploreRoutes-->>HomeDrawer: provide routes list
HomeDrawer->>User: render Explore header + sublist (if inExplore)
User->>HomeDrawer: click Servers subitem
HomeDrawer->>Router: navigate to /app/explore/servers
Router->>ExplorePage: mount page component
ExplorePage->>ExplorePane: render inside ExplorePageContainer with ExploreSearch
ExplorePane-->>User: show composed header + search UI
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
src/components/explore/ExplorePane.tsx (1)
48-58: KeepExploreSearchgeneric over the query IDs.This widens
sortandfilterto plainstring, while callers likeExploreBotskeep them as narrower unions. With strict function checks that can makesetQuery={setQuery}incompatible; otherwise it still erases the useful union typing.🛠️ Suggested direction
-interface Query { - sort: string; - filter: string; +interface Query<TSort extends string = string, TFilter extends string = string> { + sort: TSort; + filter: TFilter; search: string; } -export function ExploreSearch(props: { +export function ExploreSearch<TSort extends string, TFilter extends string>(props: { sortOpts: DropDownItem[]; filterOpts: DropDownItem[]; - query: () => Query, - setQuery: (q: Query) => void, + query: () => Query<TSort, TFilter>; + setQuery: (q: Query<TSort, TFilter>) => void; }) { @@ - onChange={(i) => - props.setQuery({ ...props.query(), sort: i.id }) - } + onChange={(i) => + props.setQuery({ ...props.query(), sort: i.id as TSort }) + } @@ - onChange={(i) => - props.setQuery({ ...props.query(), filter: i.id }) - } + onChange={(i) => + props.setQuery({ ...props.query(), filter: i.id as TFilter }) + }Run this to verify whether strict function checks are enabled and to inspect the narrowed call sites:
#!/bin/bash set -euo pipefail python - <<'PY' import json, pathlib for path in pathlib.Path('.').rglob('tsconfig*.json'): try: data = json.loads(path.read_text()) except Exception: continue co = data.get("compilerOptions", {}) print(f"\n-- {path} --") print({"strict": co.get("strict"), "strictFunctionTypes": co.get("strictFunctionTypes")}) PY echo rg -n -C2 'interface Query|export function ExploreSearch|createSignal<\{|<ExploreSearch|setQuery=\{setQuery\}|query=\{query\}' src/components/explore🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/explore/ExplorePane.tsx` around lines 48 - 58, The ExploreSearch component currently uses a concrete Query interface with sort/filter as plain strings which widens caller types and can break strictFunctionTypes; make ExploreSearch generic so it preserves callers' narrower unions. Change the local Query to a generic type parameter (e.g. ExploreSearch<S extends string, F extends string, Q extends { sort: S; filter: F; search: string }>) and update props to use those generics: sortOpts and filterOpts typed to the matching ID generics, and query: () => Q and setQuery: (q: Q) => void. Update the ExploreSearch declaration and usages (e.g. ExploreSearch<...>) so callers like ExploreBots keep their union types intact while ExploreSearch remains flexible.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/components/home-drawer/HomeDrawer.module.scss`:
- Around line 33-35: The CSS selector :global .itemContent is invalid for CSS
Modules and triggers stylelint; change the rule to use the CSS Modules global
function form by converting ":global .itemContent { gap: 6px; }" to
":global(.itemContent) { gap: 6px; }" (update the rule in HomeDrawer.module.scss
where the .itemContent global rule is defined).
- Around line 59-87: Rename the keyframe and its usage to kebab-case to satisfy
stylelint: change the animation name "slideDown" to "slide-down" in the rule
that sets animation/animation-duration/animation-fill-mode and rename the
corresponding `@keyframes` declaration from "@keyframes slideDown" to "@keyframes
slide-down"; also remove the extra blank line within the block (the one before
`@keyframes`) to clear the stylelint blank-line warning. Ensure all references
inside HomeDrawer.module.scss use the new "slide-down" name.
In `@src/components/home-drawer/HomeDrawer.tsx`:
- Around line 120-124: The explore Item generation in HomeDrawer.tsx wrongly
concatenates setting.path/setting.match causing double slashes and invalid
patterns; update the Item to build both href and match from setting.routePath
(e.g. href = "/app" + setting.routePath and match = setting.routePath ? "/app" +
setting.routePath : undefined) instead of using setting.path or setting.match,
and remove the old concatenation that references setting.path/setting.match so
nested explore routes match correctly.
In `@src/components/ui/Item.module.scss`:
- Around line 78-82: The shared .itemContent CSS removed left gutter causing
items with Item.Root's default handlePosition="left" (in
src/components/ui/Item.tsx) to overlap the handle; restore a base left padding
in .itemContent to reserve the left-handle space and move the padding-left: 0px
rule into the specific explore-list variant selector (or the class used by the
new list) so only that variant removes the left gutter.
---
Nitpick comments:
In `@src/components/explore/ExplorePane.tsx`:
- Around line 48-58: The ExploreSearch component currently uses a concrete Query
interface with sort/filter as plain strings which widens caller types and can
break strictFunctionTypes; make ExploreSearch generic so it preserves callers'
narrower unions. Change the local Query to a generic type parameter (e.g.
ExploreSearch<S extends string, F extends string, Q extends { sort: S; filter:
F; search: string }>) and update props to use those generics: sortOpts and
filterOpts typed to the matching ID generics, and query: () => Q and setQuery:
(q: Q) => void. Update the ExploreSearch declaration and usages (e.g.
ExploreSearch<...>) so callers like ExploreBots keep their union types intact
while ExploreSearch remains flexible.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: e612a3a9-678c-4400-8a0b-59210aa05f60
📒 Files selected for processing (11)
src/common/exploreRoutes.tssrc/components/explore/ExploreBots.tsxsrc/components/explore/ExploreDrawer.tsxsrc/components/explore/ExplorePane.tsxsrc/components/explore/ExploreServers.tsxsrc/components/explore/ExploreThemes.tsxsrc/components/home-drawer/HomeDrawer.module.scsssrc/components/home-drawer/HomeDrawer.tsxsrc/components/side-pane/SidePane.tsxsrc/components/ui/Item.module.scsssrc/index.tsx
💤 Files with no reviewable changes (1)
- src/components/explore/ExploreDrawer.tsx
1df318e to
6a7c1c7
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
src/components/home-drawer/HomeDrawer.tsx (1)
109-115: Drive the Explore header state frominExplore()only.The arrow icon and submenu visibility already depend on
inExplore(), so keeping selection on a separatematch="/app/explore"path adds avoidable state to keep in sync.♻️ Suggested cleanup
<Item label={t("explore.drawer.title")} icon={inExplore() ? "keyboard_arrow_down" : "explore"} href="/app/explore/servers" - match="/app/explore" + selected={!!inExplore()} class={style.exploreHeader} />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/home-drawer/HomeDrawer.tsx` around lines 109 - 115, The Item component's selection state should be driven solely by the inExplore() helper: remove the redundant match="/app/explore" prop from the Item instance (the one rendering the Explore header) and any code that relies on that prop for selection, and ensure any visual selection/submenu visibility logic only reads inExplore() (preserve label, icon, href and class={style.exploreHeader}); update any tests or usages expecting the match prop to be present for this Item.src/index.tsx (1)
311-314: Drop the redundantleftDraweroverride.The parent
/approute already providesleftDrawer: HomeDrawer, so this branch only needs to swapmainPane.♻️ Suggested cleanup
<Route path="/explore" - components={{ mainPane: ExplorePane, leftDrawer: HomeDrawer }} + components={{ mainPane: ExplorePane }} >🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/index.tsx` around lines 311 - 314, Remove the redundant leftDrawer override from the Route that renders ExplorePane: locate the Route with path="/explore" (the JSX Route element that currently sets components={{ mainPane: ExplorePane, leftDrawer: HomeDrawer }} ) and change its components prop to only provide mainPane (e.g., components={{ mainPane: ExplorePane }}), relying on the parent /app route to supply leftDrawer: HomeDrawer.src/components/home-drawer/HomeDrawer.module.scss (1)
52-85: Consider a reduced-motion escape hatch for the new nav animation.This runs on a primary navigation surface. Disabling it under
prefers-reduced-motionwould make the expanded Explore list friendlier for users who opt out of motion.♿ Suggested tweak
.exploreSublist { display: flex; flex-direction: column; position: relative; padding-left: 28px; gap: 2px; animation: slide-down 0.2s forwards; + + `@media` (prefers-reduced-motion: reduce) { + animation: none; + } &::before {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/home-drawer/HomeDrawer.module.scss` around lines 52 - 85, The nav animation on .exploreSublist uses `@keyframes` slide-down and should respect users' reduced-motion preference; add a prefers-reduced-motion media query that overrides the animation and ensures the element is rendered in its final visible state (e.g., set animation: none; opacity: 1; transform: none;) for .exploreSublist so the expand occurs instantly with no motion for users who opt out.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/components/explore/ExploreBots.tsx`:
- Around line 120-127: The filter IDs in defaultQuery and filterOpts
("online_bots"/"offline_bots") are not part of the PublicServerFilter type used
by query/setQuery/getExploreItems; update either the type or the filter options
so they align: either extend PublicServerFilter to include "online_bots" and
"offline_bots" (so query, setQuery and getExploreItems accept those values) or
change defaultQuery and filterOpts to use only the existing PublicServerFilter
values ("pinned" | "all" | "verified"); ensure ExploreSearch is passed a query
value that matches PublicServerFilter and update getExploreItems usage
accordingly.
---
Nitpick comments:
In `@src/components/home-drawer/HomeDrawer.module.scss`:
- Around line 52-85: The nav animation on .exploreSublist uses `@keyframes`
slide-down and should respect users' reduced-motion preference; add a
prefers-reduced-motion media query that overrides the animation and ensures the
element is rendered in its final visible state (e.g., set animation: none;
opacity: 1; transform: none;) for .exploreSublist so the expand occurs instantly
with no motion for users who opt out.
In `@src/components/home-drawer/HomeDrawer.tsx`:
- Around line 109-115: The Item component's selection state should be driven
solely by the inExplore() helper: remove the redundant match="/app/explore" prop
from the Item instance (the one rendering the Explore header) and any code that
relies on that prop for selection, and ensure any visual selection/submenu
visibility logic only reads inExplore() (preserve label, icon, href and
class={style.exploreHeader}); update any tests or usages expecting the match
prop to be present for this Item.
In `@src/index.tsx`:
- Around line 311-314: Remove the redundant leftDrawer override from the Route
that renders ExplorePane: locate the Route with path="/explore" (the JSX Route
element that currently sets components={{ mainPane: ExplorePane, leftDrawer:
HomeDrawer }} ) and change its components prop to only provide mainPane (e.g.,
components={{ mainPane: ExplorePane }}), relying on the parent /app route to
supply leftDrawer: HomeDrawer.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 85be2694-f36b-4395-8e14-a0e0c0b09d71
📒 Files selected for processing (12)
src/common/exploreRoutes.tssrc/components/explore/ExploreBots.tsxsrc/components/explore/ExploreDrawer.tsxsrc/components/explore/ExplorePane.tsxsrc/components/explore/ExploreServers.tsxsrc/components/explore/ExploreThemes.tsxsrc/components/home-drawer/HomeDrawer.module.scsssrc/components/home-drawer/HomeDrawer.tsxsrc/components/home-drawer/friend-item/HomeDrawerFriendItem.tsxsrc/components/side-pane/SidePane.tsxsrc/components/ui/Item.module.scsssrc/index.tsx
💤 Files with no reviewable changes (1)
- src/components/explore/ExploreDrawer.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
- src/components/explore/ExplorePane.tsx
- src/components/explore/ExploreThemes.tsx
- src/components/side-pane/SidePane.tsx
- src/common/exploreRoutes.ts
- src/components/ui/Item.module.scss
| <ExplorePageContainer> | ||
| <MetaTitle>{t("explore.bots.title")}</MetaTitle> | ||
| <ExploreSearch | ||
| sortOpts={sortOpts} | ||
| filterOpts={filterOpts} | ||
| query={query} | ||
| setQuery={setQuery} | ||
| /> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check how PublicServerSort and PublicServerFilter are defined
ast-grep --pattern 'type PublicServerSort = $_'
ast-grep --pattern 'type PublicServerFilter = $_'
rg -n 'PublicServerSort|PublicServerFilter' --type ts -A 2 -B 1Repository: Nerimity/nerimity-web
Length of output: 2907
🏁 Script executed:
fd -e tsx -e ts | xargs rg -l "ExploreSearch" | head -5Repository: Nerimity/nerimity-web
Length of output: 184
🏁 Script executed:
ast-grep --pattern 'function ExploreSearch($_) {
$$$
}'Repository: Nerimity/nerimity-web
Length of output: 2895
🏁 Script executed:
rg -n "interface.*ExploreSearch|type.*ExploreSearch" --type tsRepository: Nerimity/nerimity-web
Length of output: 47
🏁 Script executed:
rg -n "export.*ExploreSearch" --type tsx -A 10Repository: Nerimity/nerimity-web
Length of output: 92
🏁 Script executed:
rg -n "type Query|interface Query" src/components/explore/ --type tsRepository: Nerimity/nerimity-web
Length of output: 124
🏁 Script executed:
rg -n "type DropDownItem|interface DropDownItem" --type ts -A 3Repository: Nerimity/nerimity-web
Length of output: 332
🏁 Script executed:
sed -n '48,53p' src/components/explore/ExplorePane.tsxRepository: Nerimity/nerimity-web
Length of output: 137
🏁 Script executed:
sed -n '60,100p' src/components/explore/ExploreBots.tsxRepository: Nerimity/nerimity-web
Length of output: 1144
🏁 Script executed:
sed -n '100,130p' src/components/explore/ExploreBots.tsxRepository: Nerimity/nerimity-web
Length of output: 1097
🏁 Script executed:
sed -n '44,50p' src/chat-api/services/ExploreService.tsRepository: Nerimity/nerimity-web
Length of output: 236
🏁 Script executed:
sed -n '107,115p' src/components/explore/ExploreBots.tsxRepository: Nerimity/nerimity-web
Length of output: 453
🏁 Script executed:
rg -n "online_bots|offline_bots" src/components/explore/ --type tsRepository: Nerimity/nerimity-web
Length of output: 327
🏁 Script executed:
sed -n '40,52p' src/components/explore/ExploreBots.tsxRepository: Nerimity/nerimity-web
Length of output: 389
🏁 Script executed:
rg -n "getExploreItems" src/components/explore/ExploreBots.tsx -B 5 -A 5Repository: Nerimity/nerimity-web
Length of output: 940
Fix type mismatch: filter values don't match PublicServerFilter type definition.
The defaultQuery and filterOpts use filter IDs "online_bots" and "offline_bots", but the local query signal is typed with PublicServerFilter which only includes "pinned" | "all" | "verified". These invalid values will cause type errors when passed through setQuery and getExploreItems.
Either update the PublicServerFilter type to include the bot-specific filters, or align the filter options with the actual type definition.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/explore/ExploreBots.tsx` around lines 120 - 127, The filter
IDs in defaultQuery and filterOpts ("online_bots"/"offline_bots") are not part
of the PublicServerFilter type used by query/setQuery/getExploreItems; update
either the type or the filter options so they align: either extend
PublicServerFilter to include "online_bots" and "offline_bots" (so query,
setQuery and getExploreItems accept those values) or change defaultQuery and
filterOpts to use only the existing PublicServerFilter values ("pinned" | "all"
| "verified"); ensure ExploreSearch is passed a query value that matches
PublicServerFilter and update getExploreItems usage accordingly.
This PR moves the explore tabs from being a separate page with its own drawer into a sub-list in the home page drawer, and refactors the explore page header styles to be the same across all explore pages.
Refactors for the explore pages:
Screenshots
The animations for opening / interacting with the explore item list:
https://github.com/user-attachments/assets/761e626b-5cc7-42f8-8682-6c1c5140f632
Before:
After:
No mobile screenshots, but the changes are the same. The filter dropdown in for search is still wrapped to the next line on mobile.
Did you test your code?
Tested on Firefox on Desktop and Chrome on Android.
Checklist
Summary by CodeRabbit
New Features
Refactor
Style