Skip to content

feat(perps): persist Pro positions sort and side filter prefs - #9838

Merged
michalconsensys merged 3 commits into
mainfrom
feat/core-perps-positions-sort-filter-prefs
Aug 12, 2026
Merged

feat(perps): persist Pro positions sort and side filter prefs#9838
michalconsensys merged 3 commits into
mainfrom
feat/core-perps-positions-sort-filter-prefs

Conversation

@michalconsensys

@michalconsensysmichalconsensys commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Explanation

In Pro mode, mobile needs Positions/Orders panel sort and side-filter preferences to persist across markets and app restarts. Today those values live in local React state and reset on remount (e.g. market navigation via stack push).

This extends the existing network-independent flat proLayoutPreferences bag (already used for chartExpanded) with:

  • positionsSideFilter — default 'all'
  • positionsSortField — default 'positionValue'
  • positionsSortDirection — default 'desc'

Sort is stored as two top-level scalars (same shape as marketFilterPreferences), so getter/setter/selector stay on shallow spread with no custom merge helper. Callers continue to use getProLayoutPreferences() / setProLayoutPreferences(patch).

TypeScript breaking: consumers that construct a full ProLayoutPreferences object must include the new required fields. Runtime persisted state is forward-compatible via merge-over-defaults.

Mobile consumer PR: MetaMask/metamask-mobile#34667 (TAT-3644).

References

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Note

Low Risk
UI preference persistence only; runtime remains forward-compatible via merge-over-defaults, with a TypeScript-only break for full-object constructors.

Overview
Extends the flat ProLayoutPreferences bag so Pro Positions/Orders side filter and sort survive market navigation and app restarts via the existing getProLayoutPreferences() / setProLayoutPreferences(patch) API.

Adds three required fields with defaults: positionsSideFilter ('all'), positionsSortField ('positionValue'), and positionsSortDirection ('desc'), plus exported types ProPositionsSideFilter, ProPositionsSortField, and ProPositionsSortDirection.

Breaking for TypeScript: callers that construct a full ProLayoutPreferences object must include the new fields. Older persisted state stays valid at runtime because the getter and selectProLayoutPreferences still merge over DEFAULT_PRO_LAYOUT_PREFERENCES.

Reviewed by Cursor Bugbot for commit 6ec9a8f. Bugbot is set up for automated code reviews on this repo. Configure here.

Add positionsSideFilter and positionsSortConfig to proLayoutPreferences so mobile can persist Positions/Orders panel preferences across markets and restarts.
Satisfy CI changelog link requirements and keep PerpsController method action docs in sync after the setProLayoutPreferences signature/docs change.
@michalconsensys
michalconsensys marked this pull request as ready for review August 12, 2026 11:35
@michalconsensys
michalconsensys requested review from a team as code ownersAugust 12, 2026 11:35

@agangladaaganglada left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Focused on the merge helper and whether existing perps patterns apply. No approval/block — inline notes on specific lines.

orderBookPosition: 'left' | 'right';
orderFormPosition: 'left' | 'right';
positionsSideFilter: ProPositionsSideFilter;
positionsSortConfig: ProPositionsSortConfig;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nesting positionsSortConfig is the only reason we need a custom merge helper — the original proLayoutPreferences bag from #9550 was intentionally flat.

Closest in-package precedent is marketFilterPreferences, which stores sort as two top-level scalars (optionId + direction) with a simple pref ?? defaults getter and an atomic setter. Consider flattening to positionsSortField + positionsSortDirection here so getter/setter/selector can stay on shallow spread and you can drop mergeProLayoutPreferences entirely.

If Mobile's UI state is nested, mapping at the boundary is a small cost for a much simpler persisted shape.

* @param prefs - Partial preferences from persisted state or a setter patch.
* @returns A fully-populated `ProLayoutPreferences` object.
*/
export const mergeProLayoutPreferences = (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If you keep the nested object, this helper only deep-merges positionsSortConfig over defaults, not over successive layers (persisted state → patch). That forces the setter to re-implement nested merge before calling in.

Consider a variadic signature that folds layers in one place:

exportconstmergeProLayoutPreferences=(
...layers: Array<ProLayoutPreferencesPatch|null|undefined>): ProLayoutPreferences=>{/* merge top-level + accumulate positionsSortConfig across layers */};

Then:

  • getter: mergeProLayoutPreferences(this.state.proLayoutPreferences)
  • setter: mergeProLayoutPreferences(state.proLayoutPreferences, patch)

No need for deepmerge (devDependency only here) — a ~15-line loop is enough for one nested key.

...state.proLayoutPreferences,
...patch,
};
...(patch.positionsSortConfig

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This manual positionsSortConfig spread exists because { ...state, ...patch } clobbers the nested object when the patch only supplies field or direction. The logic belongs inside mergeProLayoutPreferences (see comment on the helper) so the setter can be:

state.proLayoutPreferences=mergeProLayoutPreferences(state.proLayoutPreferences,patch,);

Alternative if you want to avoid deep merge altogether: keep nested shape but add an atomic setPositionsSortConfig(config) (like saveMarketFilterPreferences(optionId, direction)) and restrict setProLayoutPreferences to shallow top-level patches.

PerpsMode,
DEFAULT_PERPS_MODE,
DEFAULT_PRO_LAYOUT_PREFERENCES,
mergeProLayoutPreferences,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do consumers need mergeProLayoutPreferences on the public surface? Getter/setter/selector already wrap it. Exporting adds API surface for an internal persistence detail — I'd keep it module-private unless Mobile has a concrete out-of-controller hydration path that needs it.

Store positionsSortField and positionsSortDirection as top-level scalars like marketFilterPreferences, dropping the nested sort config and merge helper.
@michalconsensys
michalconsensys added this pull request to the merge queueAug 12, 2026
Merged via the queue into main with commit 32b6264Aug 12, 2026
46 checks passed
@michalconsensys
michalconsensys deleted the feat/core-perps-positions-sort-filter-prefs branch August 12, 2026 14:02
pullBot pushed a commit to dmrazzy/core that referenced this pull request Aug 13, 2026
## Explanation
In Pro mode, mobile's Positions/Orders panel has **independent** sort
configs, and the same should be true for side filter: a user can keep
Long on Positions and Short on Orders.
[MetaMask#9838](MetaMask#9838) persisted the
Positions sort and side filter on `proLayoutPreferences`, but Orders
sort/filter still lived in local React state and reset on remount (e.g.
market navigation via stack push).
This extends the existing network-independent flat
`proLayoutPreferences` bag with:
- `ordersSideFilter` — default `'all'`
- `ordersSortField` — default `'time'`
- `ordersSortDirection` — default `'desc'`
Field unions match mobile's Orders panel: side filter `'all' | 'long' |
'short'`, sort `'orderValue' | 'size' | 'price' | 'time'`. Values are
stored as top-level scalars (same shape as the positions fields), so
getter/setter/selector stay on shallow spread with no custom merge
helper. Callers continue to use `getProLayoutPreferences()` /
`setProLayoutPreferences(patch)`.
`ordersSideFilter` is independent of `positionsSideFilter`.
**TypeScript breaking:** consumers that construct a full
`ProLayoutPreferences` object must include the new required fields.
Runtime persisted state is forward-compatible via merge-over-defaults.
## References
* Fixes https://consensyssoftware.atlassian.net/browse/TAT-3644
* Related: MetaMask#9838
* Related: MetaMask/metamask-mobile#34667
## Checklist
- [ ] I've updated the test suite for new or updated code as appropriate
- [ ] I've updated documentation (JSDoc, Markdown, etc.) for new or
updated code as appropriate
- [ ] I've communicated my changes to consumers by [updating changelogs
for packages I've
changed](https://github.com/MetaMask/core/tree/main/docs/processes/updating-changelogs.md)
- [ ] I've introduced [breaking
changes](https://github.com/MetaMask/core/tree/main/docs/processes/breaking-changes.md)
in this PR and have prepared draft pull requests for clients and
consumer packages to resolve them
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Low Risk**
> Preference schema extension with defaults merge; breaking only for
code that manually constructs a full `ProLayoutPreferences` object
without the new fields.
> > **Overview**
> **BREAKING:** Extends flat `ProLayoutPreferences` with
**`ordersSideFilter`**, **`ordersSortField`**, and
**`ordersSortDirection`** (defaults `'all'`, `'time'`, `'desc'`) so Pro
**Orders** panel filter/sort persist separately from Positions across
markets and restarts, using the same `getProLayoutPreferences()` /
`setProLayoutPreferences(patch)` flow.
> > Adds exported types `ProOrdersSideFilter`, `ProOrdersSortField`, and
`ProOrdersSortDirection`; updates `DEFAULT_PRO_LAYOUT_PREFERENCES`,
package exports, changelog, and tests (including independence from
positions prefs and merge-over-defaults for older persisted state).
> > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
ceb550e. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@michalconsensys@aganglada