Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve search params #1498

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@
"@sentry/vite-plugin": "^2.21.1",
"@tanstack/react-query": "^5.40.1",
"@tanstack/react-query-devtools": "^5.40.1",
"@tanstack/react-router": "^1.4.9",
"@tanstack/react-router": "1.57.18",
"@tanstack/react-table": "^8.9.3",
"@tanstack/react-virtual": "^3.0.0-beta.30",
"@tanstack/router-devtools": "^1.4.9",
"@tanstack/router-valibot-adapter": "^1.57.18",
"@testing-library/dom": "^10.2.0",
"@testing-library/jest-dom": "^6.4.6",
"@testing-library/react": "^16.0.0",
Expand Down Expand Up @@ -132,4 +133,4 @@
"@types/d3": "^7.4.3"
},
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}
}
23 changes: 9 additions & 14 deletions src/components/activity/ActivityProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ interface ActivityContextType {
status: 'error' | 'pending' | 'success';
fetchStatus: FetchStatus;
queryParams: QueryActivityParams;
searchParams: ActivitySearchParams;
searchParams: Partial<ActivitySearchParams>;
setSearchParams: (searchParams: Partial<ActivitySearchParams>) => any;
}

Expand Down Expand Up @@ -51,13 +51,18 @@ const getQueryParams = (
return params;
};

type ActivityUrls =
| '/strategies/activity'
| '/explore/$type/$slug/activity'
| '/strategy/$id';
interface Props {
url: ActivityUrls;
params: QueryActivityParams;
children: ReactNode;
}
type ParamsKey = Extract<keyof QueryActivityParams, string>;
export const ActivityProvider: FC<Props> = ({ children, params }) => {
const nav = useNavigate();
export const ActivityProvider: FC<Props> = ({ children, params, url }) => {
const nav = useNavigate({ from: url });
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, we should probably add from: url to more uses of useNavigate as it improves speed.

https://tanstack.com/router/latest/docs/framework/react/guide/type-safety#narrow-to-relevant-routes-as-much-as-you-possibly-can

const searchParams: ActivitySearchParams = useSearch({ strict: false });
const limit = searchParams.limit;
const offset = searchParams.offset;
Expand All @@ -78,17 +83,7 @@ export const ActivityProvider: FC<Props> = ({ children, params }) => {
replace: true,
resetScroll: false,
params: (params) => params,
search: (currentSearch) => {
const updates = structuredClone(changes);
const search = structuredClone(currentSearch);
for (const [key, value] of Object.entries(changes)) {
if (isEmpty(value)) {
delete (updates as any)[key];
if (key in search) delete (search as any)[key];
}
}
return { ...search, ...updates };
},
search: (current) => ({ ...current, ...changes }),
});
},
[nav]
Expand Down
52 changes: 14 additions & 38 deletions src/components/activity/utils.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { PaginationParams } from 'hooks/useList';
import { Activity, ActivityAction } from 'libs/queries/extApi/activity';
import {
SearchParamsValidator,
InferSearch,
searchValidator,
validArrayOf,
validLiteral,
validNumber,
validNumberType,
validString,
validateSearchParams,
} from 'libs/routing/utils';
import { SafeDecimal } from 'libs/safedecimal';
import {
Expand All @@ -16,18 +14,8 @@ import {
tokenRange,
} from 'utils/helpers';
import { exist } from 'utils/helpers/operators';
import * as v from 'valibot';

export interface ActivitySearchParams extends Partial<PaginationParams> {
pairs?: string[];
ids?: string[];
actions?: ActivityAction[];
start?: Date;
end?: Date;
// This is only for StrategyPageParams, but if I don't implement it here the build breaks
priceStart?: string;
priceEnd?: string;
hideIndicators?: boolean;
}
export const activityActionName: Record<ActivityAction, string> = {
create: 'Create',
edit: 'Edit Price',
Expand All @@ -43,29 +31,17 @@ export const activityActions = Object.keys(
activityActionName
) as ActivityAction[];

export const activityValidators: SearchParamsValidator<ActivitySearchParams> = {
actions: validArrayOf(validLiteral(activityActions)),
ids: validArrayOf(validString),
pairs: validArrayOf(validString),
start: validString,
end: validString,
limit: validNumber,
offset: validNumber,
};
export const validateActivityParams = (
activityValidators: SearchParamsValidator<ActivitySearchParams>
) => {
return (search: Record<string, string>): ActivitySearchParams => {
const rawSearch = validateSearchParams(activityValidators)(search);
const limit = Number(rawSearch.limit ?? 10);
const offset = Number(rawSearch.offset ?? 0);
return {
...rawSearch,
limit,
offset,
};
};
export type ActivitySearchParams = InferSearch<typeof activityValidators>;
export const activityValidators = {
actions: v.optional(validArrayOf(v.picklist(activityActions))),
ids: v.optional(validArrayOf(validString)),
pairs: v.optional(validArrayOf(validString)),
start: v.optional(validString),
end: v.optional(validString),
limit: v.optional(validNumberType, 10),
offset: v.optional(validNumberType, 0),
};
export const validateActivityParams = searchValidator(activityValidators);

export const activityHasPairs = (activity: Activity, pairs: string[] = []) => {
if (pairs.length === 0) return true;
Expand Down
2 changes: 1 addition & 1 deletion src/components/explorer/ExplorerSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { useDebouncedValue } from 'hooks/useDebouncedValue';
export const _ExplorerSearch: FC = () => {
const navigate = useNavigate();
const pairs = usePairs();
const { type, slug } = useExplorerParams();
const { type, slug } = useExplorerParams('/explore/$type');
const [search, setSearch] = useState(slug ?? '');
const [debouncedSearch] = useDebouncedValue<string>(search, 300); // Debounce search input for ens query

Expand Down
2 changes: 1 addition & 1 deletion src/components/explorer/ExplorerSearchDropdownButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const ExplorerSearchDropdownButton = forwardRef<
HTMLButtonElement,
MenuButtonProps
>(function ExplorerSearchDropdownButton(props, ref) {
const { type } = useExplorerParams();
const { type } = useExplorerParams('/explore/$type');
return (
<button
ref={ref}
Expand Down
2 changes: 1 addition & 1 deletion src/components/explorer/ExplorerSearchDropdownItems.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ interface Props {
}

export const ExplorerSearchDropdownItems: FC<Props> = ({ setSearch }) => {
const { type: currentType } = useExplorerParams();
const { type: currentType } = useExplorerParams('/explore/$type');
const items = [
{
type: 'wallet' as const,
Expand Down
2 changes: 1 addition & 1 deletion src/components/explorer/ExplorerTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { useStrategyCtx } from 'hooks/useStrategies';

export const ExplorerTabs = () => {
const { filteredStrategies } = useStrategyCtx();
const { slug, type } = useExplorerParams();
const { slug, type } = useExplorerParams('/explore/$type/$slug');

// To support emojis in ens domains
const { location } = useRouterState();
Expand Down
1 change: 0 additions & 1 deletion src/components/explorer/suggestion/SuggestionList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ export const SuggestionList: FC<Props> = (props) => {
className="px-30 flex cursor-pointer items-center space-x-10 py-10 hover:bg-white/20 aria-selected:bg-white/10"
to="/explore/$type/$slug"
params={params}
search={{}}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, the empty objects on search and params can now be removed from these files too:

  • src/components/core/menu/mainMenu/MainMenuLeft.tsx
  • src/components/core/menu/mobileMenu/MobileMenu.tsx
  • src/components/strategies/StrategyPageTabs.tsx
  • src/components/trade/TradeExplorerTabs.tsx

>
<PairLogoName pair={pair} />
</Link>
Expand Down
4 changes: 2 additions & 2 deletions src/components/explorer/useExplorer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import { usePairs } from 'hooks/usePairs';
import { useMemo } from 'react';

export const useExplorer = () => {
const { slug, type } = useExplorerParams();
const { slug, type } = useExplorerParams('/explore/$type');
const pairs = usePairs();

// PAIR
const exactMatch = useMemo(() => pairs.map.get(slug), [pairs.map, slug]);
const exactMatch = useMemo(() => pairs.map.get(slug!), [pairs.map, slug]);
const pairQuery = useGetPairStrategies({
token0: exactMatch?.baseToken.address,
token1: exactMatch?.quoteToken.address,
Expand Down
22 changes: 14 additions & 8 deletions src/components/explorer/useExplorerParams.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import { useParams } from 'libs/routing';

export const useExplorerParams = () => {
const params = useParams({
from: '/explore/$type/$slug/portfolio/token/$address',
});
type ExplorerUrl =
| '/explore/$type'
| '/explore/$type/$slug'
| '/explore/$type/$slug/activity'
| '/explore/$type/$slug/portfolio'
| '/explore/$type/$slug/portfolio/token/$address';

// To support emojis in ens domains
const decodedSlug = params.slug && decodeURIComponent(params.slug);

return { ...params, slug: decodedSlug };
export const useExplorerParams = <T extends ExplorerUrl>(url: T) => {
const params = useParams({ from: url });
if ('slug' in params) {
// To support emojis in ens domains
return { ...params, slug: decodeURIComponent(params.slug) };
} else {
return { ...params, slug: undefined };
}
};
93 changes: 48 additions & 45 deletions src/components/simulator/input/SimInputStrategyType.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,51 +45,54 @@ export const SimInputStrategyType: FC<Props> = ({ baseToken, quoteToken }) => {
<h2 className="text-18 font-weight-500 m-0">Trading Strategy</h2>
</header>
<article role="tablist" className="grid grid-cols-2 gap-8">
{items.map(({ title, label, svg, tooltipText }) => (
<Link
role="tab"
id={'tab-' + label}
aria-controls={'panel-' + label}
key={label}
to={`/simulate/${label}`}
search={{ baseToken, quoteToken }}
className={cn(
'rounded-10 text-14 font-weight-500 group flex size-full flex-row items-center justify-center gap-8 bg-black px-8 py-16 outline-white',
'md:px-12',
'focus-visible:outline focus-visible:outline-1'
)}
inactiveProps={{
className:
'hover:outline hover:outline-1 hover:outline-background-400',
}}
activeProps={{ className: 'outline outline-1 outline-white' }}
replace={true}
resetScroll={false}
params={{ simulationType: label }}
data-testid={`select-type-${label}`}
>
{({ isActive }) => {
return (
<>
{svg}
<span
className={`capitalize ${
isActive
? 'text-white'
: 'text-white/40 group-hover:text-white/80'
}`}
>
{title}
</span>
<Tooltip
element={<div>{tooltipText}</div>}
iconClassName="size-12 text-white/60"
/>
</>
);
}}
</Link>
))}
{items.map(({ title, label, svg, tooltipText }) => {
const to = `/simulate/${label}` as const;
return (
<Link
role="tab"
id={'tab-' + label}
aria-controls={'panel-' + label}
key={label}
to={to}
search={{ baseToken, quoteToken }}
className={cn(
'rounded-10 text-14 font-weight-500 group flex size-full flex-row items-center justify-center gap-8 bg-black px-8 py-16 outline-white',
'md:px-12',
'focus-visible:outline focus-visible:outline-1'
)}
inactiveProps={{
className:
'hover:outline hover:outline-1 hover:outline-background-400',
}}
activeProps={{ className: 'outline outline-1 outline-white' }}
replace={true}
resetScroll={false}
params={{ simulationType: label }}
data-testid={`select-type-${label}`}
>
{({ isActive }) => {
return (
<>
{svg}
<span
className={`capitalize ${
isActive
? 'text-white'
: 'text-white/40 group-hover:text-white/80'
}`}
>
{title}
</span>
<Tooltip
element={<div>{tooltipText}</div>}
iconClassName="size-12 text-white/60"
/>
</>
);
}}
</Link>
);
})}
</article>
</section>
);
Expand Down
2 changes: 1 addition & 1 deletion src/components/simulator/input/SimInputTokenSelection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const SimInputTokenSelection: FC<Props> = ({
quoteToken,
noPriceHistory,
}) => {
const navigate = useNavigate();
const navigate = useNavigate({ from: '/simulate' });
const { openModal } = useModal();
const { getTokenById } = useTokens();
const base = getTokenById(baseToken);
Expand Down
10 changes: 8 additions & 2 deletions src/components/strategies/common/useSetOrder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { StrategyDirection, useNavigate } from 'libs/routing';
import { OrderBlock } from './types';
import { useCallback } from 'react';

export const useSetDisposableOrder = (url: string) => {
type DisposableUrl = '/strategies/edit/$strategyId/prices/disposable';
export const useSetDisposableOrder = (url: DisposableUrl) => {
const navigate = useNavigate({ from: url });
const setOrder = useCallback(
(order: Partial<OrderBlock>) => {
Expand Down Expand Up @@ -32,7 +33,12 @@ export const toOrderSearch = <T>(
return search;
};

export const useSetRecurringOrder = <T>(url: string) => {
type RecurringUrl =
| '/strategies/edit/$strategyId/budget/disposable'
| '/strategies/edit/$strategyId/budget/recurring'
| '/strategies/edit/$strategyId/prices/recurring'
| '/trade/recurring';
export const useSetRecurringOrder = <T>(url: RecurringUrl) => {
const navigate = useNavigate({ from: url });
const setOrder = useCallback(
(order: Partial<OrderBlock>, direction: StrategyDirection) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const StrategyBlockManage: FC<Props> = (props) => {
const { filteredStrategies, sort, filter } = useStrategyCtx();
const { openModal } = useModal();
const navigate = useNavigate();
const { type, slug } = useParams({ from: '/explore/$type/$slug' });
const { type, slug } = useParams({ strict: false });

const isOwn = useIsStrategyOwner(strategy.id);

Expand Down
Loading