Skip to content

Commit 868aa7a

Browse files
chore: update Market Insights entry card UI (#28029)
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** UI update for the Market Insights entry card, and adds the skeleton piece as well. <img width="398" height="148" alt="Screenshot 2026-03-27 at 11 19 11" src="https://github.com/user-attachments/assets/ba8a7c7a-234b-453a-8baf-b4f9304f10ba" /> https://github.com/user-attachments/assets/5ded82fe-65cd-4f5a-837f-cacdb98e5a78 ## **Changelog** <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: null ## **Related issues** Fixes: ## **Manual testing steps** ```gherkin Feature: my feature name Scenario: user [verb for user action] Given [describe expected initial app state] When user [verb for user action] Then [describe expected outcome] ``` ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I've included tests if applicable - [ ] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Moderate UI/animation refactor (Reanimated/SVG worklets, timed carousel) that could introduce visual regressions or extra render/animation cost, plus conditional rendering changes for loading states. > > **Overview** > Updates the Market Insights entry card presentation: adds gradient “chrome” styling (title, sparkle, arrow), replaces the static body copy with a rotating `SlidingTextCarousel` of trend descriptions (falls back to summary), and tweaks layout/interaction from `Pressable` to `TouchableOpacity`. > > Refactors `AnimatedGradientBorder` to use a trail-aligned animated SVG gradient and a new `animationKey` trigger (re-fires on visibility and on carousel slide starts), with new polyline-based border sampling to align dash offset with gradient head/tail. > > Introduces `MarketInsightsEntryCardSkeleton` (and new `ENTRY_CARD_SKELETON` test id) and updates Token Details + Perps Market Details to show the skeleton while insights are loading, otherwise hide the section when not loading and no report. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 6e5a52c. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Xavier Brochard <xavier.brochard@consensys.net>
1 parent 4e8f960 commit 868aa7a

16 files changed

Lines changed: 1253 additions & 225 deletions

app/components/UI/MarketInsights/MarketInsights.testIds.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export enum MarketInsightsSelectorsIDs {
22
ENTRY_CARD = 'market-insights-entry-card',
33
ENTRY_CARD_HEADLINE = 'market-insights-entry-card-headline',
44
VIEW_CONTAINER = 'market-insights-view-container',
5+
ENTRY_CARD_SKELETON = 'market-insights-entry-card-skeleton',
56
VIEW_SKELETON = 'market-insights-view-skeleton',
67
VIEW_HEADER = 'market-insights-view-header',
78
TREND_ITEM = 'market-insights-trend-item',

app/components/UI/MarketInsights/components/MarketInsightsEntryCard/AnimatedGradientBorder.constants.ts

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,54 @@ import { brandColor } from '@metamask/design-tokens';
33
/** Duration of the sweep traveling around the border in ms */
44
export const BORDER_SWEEP_DURATION_MS = 1000;
55

6-
/** Border radius matching Tailwind's rounded-2xl (16px) */
7-
export const BORDER_RADIUS = 16;
6+
/** Border radius matching Tailwind's rounded-xl (12px) */
7+
export const BORDER_RADIUS = 12;
88

99
/** Stroke width of the sharp main border line */
10-
export const BORDER_STROKE_WIDTH = 3;
10+
export const BORDER_STROKE_WIDTH = 2.5;
1111

1212
/** Fraction of the perimeter visible as the sweep segment
1313
* How long the trail is
1414
*/
15-
export const BORDER_TRAIL_FRACTION = 0.4;
15+
export const BORDER_TRAIL_FRACTION = 0.5;
1616

17-
/** Fraction of progress used for the fade-in at the start of the sweep
18-
* How long the fade-in is
17+
/**
18+
* Elastic trail: at sweep start/end the trail length is this fraction of
19+
* the peak length (`BORDER_TRAIL_FRACTION * perimeter`). Middle of sweep uses
20+
* the full peak length. 1 disables compression at the ends.
1921
*/
20-
export const BORDER_FADE_IN_FRACTION = 0.8;
22+
export const BORDER_TRAIL_ELASTIC_END_RATIO = 0.5;
2123

22-
/** Fraction of progress used for the fade-out at the end of the sweep
23-
* How long the fade-out is
24+
/**
25+
* Constant phase added to `strokeDashoffset` (`perimeter * this`), independent
26+
* of animation progress (rotates where the dash sits on the path).
2427
*/
25-
export const BORDER_FADE_OUT_FRACTION = 0.8;
28+
export const BORDER_DASH_START_SHIFT_FRACTION = 0.4;
2629

27-
/** Gradient stop colors: pink/purple → orange */
28-
export const BORDER_GRADIENT_COLORS = [
29-
brandColor.purple300,
30-
brandColor.orange400,
31-
] as const;
30+
/**
31+
* Normalized sweep at `p === 0`. See `AnimatedGradientBorder` worklet:
32+
* `sweepT = start + p * (end - start)`, `pathPhase = 1 - sweepT`.
33+
*/
34+
export const BORDER_SWEEP_PATH_START_FRACTION = 0;
35+
36+
/** Normalized sweep position when the animation completes (`p === 1`). */
37+
export const BORDER_SWEEP_PATH_END_FRACTION = 1;
38+
39+
/**
40+
* Opacity fade-in completes at this animation progress (`p`). Lower = trail
41+
* reaches full opacity sooner. Fade-in uses smoothstep `u²(3-2u)` in the worklet.
42+
* If `BORDER_TRAIL_OPACITY_FADE_OUT_START_FRACTION` is greater than this value,
43+
* opacity stays at 1 until fade-out starts; if equal, fade-out begins as soon
44+
* as fade-in completes (no hold).
45+
*/
46+
export const BORDER_TRAIL_OPACITY_FADE_IN_END_FRACTION = 0.7;
47+
48+
/**
49+
* Opacity fade-out begins at this `p` (cubic ease-out to 0 at `p === 1`).
50+
* Use a value strictly greater than `BORDER_TRAIL_OPACITY_FADE_IN_END_FRACTION`
51+
* for a full-opacity plateau between the two phases.
52+
*/
53+
export const BORDER_TRAIL_OPACITY_FADE_OUT_START_FRACTION = 0.8;
3254

3355
/** Fraction of the card that must be visible on-screen to trigger the animation (1 = fully visible) */
3456
export const VISIBILITY_THRESHOLD = 1;

0 commit comments

Comments
 (0)