Skip to content

chore: update Market Insights entry card UI#28029

Merged
zone-live merged 23 commits into
mainfrom
TSA-324-mrkt-insights-card-ui-update
Mar 30, 2026
Merged

chore: update Market Insights entry card UI#28029
zone-live merged 23 commits into
mainfrom
TSA-324-mrkt-insights-card-ui-update

Conversation

@zone-live

@zone-live zone-live commented Mar 27, 2026

Copy link
Copy Markdown
Contributor

Description

UI update for the Market Insights entry card, and adds the skeleton piece as well.

Screenshot 2026-03-27 at 11 19 11
Simulator.Screen.Recording.-.iPhone.17.Pro.-.2026-03-27.at.10.39.06.mov

Changelog

CHANGELOG entry: null

Related issues

Fixes:

Manual testing steps

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

Before

After

Pre-merge author checklist

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.

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.

Written by Cursor Bugbot for commit 6e5a52c. This will update automatically on new commits. Configure here.

@zone-live zone-live requested a review from a team as a code owner March 27, 2026 11:20
@github-actions

Copy link
Copy Markdown
Contributor

CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes.

@metamaskbot metamaskbot added the team-social-ai Social & AI team label Mar 27, 2026
Comment thread app/components/UI/TokenDetails/components/AssetOverviewContent.tsx Outdated
@github-actions github-actions Bot added the risk-low Low testing needed · Low bug introduction risk label Mar 27, 2026
@github-actions github-actions Bot added risk-low Low testing needed · Low bug introduction risk and removed risk-low Low testing needed · Low bug introduction risk labels Mar 27, 2026
@github-actions github-actions Bot added risk-low Low testing needed · Low bug introduction risk and removed risk-low Low testing needed · Low bug introduction risk labels Mar 27, 2026
zone-live and others added 12 commits March 27, 2026 14:46
- Add onSlideStart to SlidingTextCarousel; wire entry card border key bump
- Extend border trail opacity fade-in end fraction
- Cover onSlideStart in unit tests

Made-with: Cursor
- Use smoothstep for trail opacity fade-in
- Tune fade-in/fade-out timing (plateau before fade-out)

Made-with: Cursor
…ntry card (#28047)

<!--
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**

<!--
Write a short description of the changes included in this pull request,
also include relevant motivation and context. Have in mind the following
questions:
1. What is the reason for the change?
2. What is the improvement/solution?
-->

Adds an animated gradient border to the Market Insights entry card on
Asset Overview: a dashed stroke that sweeps around the card with a soft
trailing gradient aligned to the path. The carousel that cycles trend
descriptions now exposes `onSlideStart` so the border animation
retriggers when each slide begins (not only when it finishes). Constants
and easing were tuned for a smoother trail (fade-in/fade-out fractions,
sweep path end). Includes `SlidingTextCarousel`, polyline helpers for
the rounded rect, skeleton/test updates, and wiring from
`AssetOverviewContent`.

## **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: Improved the Market Insights card on the asset overview
with an animated gradient border that highlights the card as trend
descriptions rotate.

## **Related issues**

Fixes:

## **Manual testing steps**

```gherkin
Feature: Market Insights entry card animated border

  Scenario: User views asset overview with Market Insights
    Given the user is on the asset overview for a token that shows Market Insights
    When the screen loads and trend descriptions rotate in the entry card
    Then the card border shows a sweeping gradient animation aligned with slide changes
```

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

N/A

### **After**

N/A

## **Pre-merge author checklist**

- [x] 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).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] 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.

<!-- Generated with the help of the pr-description AI skill -->

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> UI-only changes but with non-trivial `react-native-reanimated`/SVG
worklet math and new polyline sampling that could impact animation
correctness/performance across devices.
> 
> **Overview**
> Adds a new sweeping dashed border animation to the Market Insights
entry card, using a **trail-aligned SVG gradient** (tail→head) and tuned
sweep/opacity behavior (elastic trail length, eased timing, configurable
fade-in/hold/fade-out).
> 
> Refactors the border implementation to compute a rounded-rect
**polyline + arc-length lookup** (`roundedRectBorderPolyline.ts`) so the
gradient endpoints track the moving dash segment, and centralizes shared
chrome gradient tokens in `constants.ts` for the border, title, sparkle,
and arrow.
> 
> Updates the carousel/card wiring so the border re-triggers when each
description slide *starts* (`onSlideStart`), switches the card wrapper
to `TouchableOpacity` + inner `View` for viewport tracking, hardens
analytics to only include `digest_id` when present, and adjusts/adds
tests for the new layout/slide-start behavior.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
ba52e22. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
@github-actions github-actions Bot added size-XL risk-medium Moderate testing recommended · Possible bug introduction risk and removed size-L risk-low Low testing needed · Low bug introduction risk labels Mar 27, 2026
@zone-live zone-live added the skip-sonar-cloud Only used for bypassing sonar cloud when failures are not relevant to the changes. label Mar 27, 2026
@github-actions github-actions Bot added risk-medium Moderate testing recommended · Possible bug introduction risk and removed risk-low Low testing needed · Low bug introduction risk labels Mar 27, 2026
@MetaMask MetaMask deleted a comment from sonarqubecloud Bot Mar 27, 2026
Comment thread app/components/UI/TokenDetails/components/AssetOverviewContent.tsx Outdated
…aMask/metamask-mobile into TSA-324-mrkt-insights-card-ui-update
@github-actions github-actions Bot added risk-medium Moderate testing recommended · Possible bug introduction risk and removed risk-medium Moderate testing recommended · Possible bug introduction risk labels Mar 27, 2026
@aganglada

aganglada commented Mar 27, 2026

Copy link
Copy Markdown
Contributor

Performance Review: TSA-324 — Market Insights Card UI Update

PR: MetaMask/metamask-mobile#28029
Branch: TSA-324-mrkt-insights-card-ui-update


Overall the PR is well-structured and clearly authored by someone aware of RN performance constraints. Most of the animation code is genuinely solid. But there are a handful of real issues worth raising.


🔴 Critical / High Impact

1. pointAtLengthOnBorderPolyline runs a linear scan every frame on the UI thread

File: roundedRectBorderPolyline.ts

The pointAtLengthOnBorderPolyline worklet does this:

for (let i = 0; i < n - 1; i++) {
  if (u <= cum[i + 1]) { ... }
}

This is an O(n) linear scan called twice per frame (once for tailPt, once for headPt) inside useDerivedValue, which runs at 60fps on the UI thread. With ARC_STEPS = 14 and 4 straight edges sampled at MAX_STRAIGHT_STEP = 5px, a typical ~350px wide card generates ~100–120 polyline points. That means ~240 iterations per frame just for point lookup.

Fix: Replace the linear scan with a binary search — the cum array is monotonically increasing, so this is O(log n) with no structural change needed:

let lo = 0, hi = n - 2;
while (lo < hi) {
  const mid = (lo + hi) >> 1;
  if (cum[mid + 1] < u) lo = mid + 1;
  else hi = mid;
}

2. useDerivedValue → two useAnimatedProps → object-valued shared value overhead

File: AnimatedGradientBorder.tsx

The chain is: progressuseDerivedValue (computes frame) → pathAnimatedProps reads frame.valuegradientAnimatedProps reads frame.value again.

The frame shared value holds a plain JS object { strokeDasharray, strokeDashoffset, strokeOpacity, x1, y1, x2, y2 }. Shared values holding objects have higher serialization overhead than primitives. Since both animated props read the same object, consider either:

  • Flattening into 7 individual useSharedValue primitives, or
  • Computing both prop sets inside a single useAnimatedProps to avoid two worklet activations per frame

🟡 Medium Impact

3. handleContainerLayout re-creates on every containerWidth change

File: SlidingTextCarousel.tsx

const handleContainerLayout = useCallback(
  (e) => {
    const { width } = e.nativeEvent.layout;
    if (width !== containerWidth) {
      setContainerWidth(width);
    }
  },
  [containerWidth], // ← re-creates whenever width changes
);

Because containerWidth is in the dependency array, this callback is a new reference every time the container resizes. This causes onLayout to be a new prop on every resize — which can subtly retrigger layout passes in some RN versions. Use a useRef to track the current width instead:

const containerWidthRef = useRef(0);
const handleContainerLayout = useCallback((e) => {
  const { width } = e.nativeEvent.layout;
  if (width !== containerWidthRef.current) {
    containerWidthRef.current = width;
    setContainerWidth(width);
  }
}, []); // stable reference

4. advanceSlide interval resets on layout measurement

File: SlidingTextCarousel.tsx

const advanceSlide = useCallback(() => {
  if (texts.length <= 1 || isAnimating.current || containerWidth === 0) { ... }
}, [texts.length, containerWidth, slotAX, slotBX, onSlideEnd, onSlideStart]);

Every time containerWidth changes (including initial measurement), advanceSlide gets a new reference, which clears and re-creates the 5-second setInterval. On first render this means the interval clock resets after the layout event fires — texts could show for up to 5s + measurement_time before the first slide. Use a ref for containerWidth (same fix as issue #3) to keep advanceSlide stable.


5. Weak useMemo deps on report.trends

File: MarketInsightsEntryCard.tsx

const displayTexts = useMemo(() => {
  const descriptions = report.trends.map((t) => t.description).filter(Boolean);
  return descriptions.length > 0 ? descriptions : [report.summary];
}, [report.trends, report.summary]);

report.trends is an array reference. If the parent re-renders and passes a new report object with the same trends content (common in Redux-connected components), this memo will bust and re-run the map + filter. For a card component this is low-cost, but given the surrounding animation complexity, it's worth being aware that this provides weaker memoization than it appears.


✅ Things Done Well

  • useMemo on pathSpec in AnimatedGradientBorder — computing the polyline only when dimensions changes is correct and important.
  • cancelAnimation before re-triggering — properly cancels the previous sweep before starting a new one, no animation leaks.
  • isAnimating ref guard in SlidingTextCarousel — prevents overlapping transitions cleanly.
  • runOnJS(onSlideEnd) — correctly hands ref mutations back to the JS thread rather than trying to mutate from a worklet.
  • Double-buffer (slot A/B) carousel — avoids any flash or layout jank during text rotation.
  • PressableTouchableOpacity swap — more predictable performance profile in this context.
  • withTiming + Easing.bezier instead of withRepeat — fires once per animationKey increment rather than looping uncontrolled. More controllable.

Summary Table

# Issue File Severity
1 Linear scan in worklet (O(n) per frame, called 2x at 60fps) roundedRectBorderPolyline.ts 🔴 High
2 Object-valued shared value + 2 animated prop reads per frame AnimatedGradientBorder.tsx 🔴 High
3 handleContainerLayout re-creates on every resize SlidingTextCarousel.tsx 🟡 Medium
4 advanceSlide interval resets after layout measurement SlidingTextCarousel.tsx 🟡 Medium
5 Weak useMemo deps on report.trends array reference MarketInsightsEntryCard.tsx 🟡 Low-Medium

The binary search fix in the worklet (#1) is the most impactful change — everything else is polish.

@github-actions github-actions Bot added risk-medium Moderate testing recommended · Possible bug introduction risk and removed risk-medium Moderate testing recommended · Possible bug introduction risk labels Mar 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🔍 Smart E2E Test Selection

  • Selected E2E tags: SmokePerps, SmokeWalletPlatform, SmokeConfirmations
  • Selected Performance tags: @PerformanceAssetLoading, @PerformancePreps
  • Risk Level: medium
  • AI Confidence: 78%
click to see 🤖 AI reasoning details

E2E Test Selection:
The changes in this PR are focused on UI/visual improvements to the MarketInsights entry card and its integration into two views:

  1. MarketInsightsEntryCard (major redesign): Replaced Pressable with TouchableOpacity, added gradient text/icons using MaskedView + LinearGradient, added new SlidingTextCarousel component for rotating trend descriptions, changed animation trigger from boolean to counter.

  2. AnimatedGradientBorder (significant animation overhaul): New trail-aligned gradient using AnimatedLinearGradient, elastic trail length animation, new opacity envelope system, changed border radius/stroke width.

  3. SlidingTextCarousel (new component): Cycles through trend descriptions with slide animations.

  4. PerpsMarketDetailsView: Added MarketInsightsEntryCardSkeleton for loading state - this is in the Perps market details view.

  5. AssetOverviewContent: Added MarketInsightsEntryCardSkeleton for loading state - this is in the token details page.

Tag Selection Reasoning:

  • SmokePerps: PerpsMarketDetailsView was modified to add skeleton loading state for market insights. The MarketInsightsEntryCard is shown in the Perps market details view when the feature flag is enabled.
  • SmokeWalletPlatform: AssetOverviewContent (token details page) was modified. Per SmokePerps tag description, changes to Perps views affect Trending (SmokeWalletPlatform). Also, the token details page is part of the wallet platform.
  • SmokeConfirmations: Required by SmokePerps tag description - 'When selecting SmokePerps, also select SmokeWalletPlatform (Trending section) and SmokeConfirmations (Add Funds deposits are on-chain transactions).'

Tags NOT selected:

  • SmokeAccounts, SmokeIdentity, SmokeNetworkAbstractions, SmokeNetworkExpansion, SmokeTrade, SmokeCard, SmokeRamps, SmokeMultiChainAPI, SmokePredictions, FlaskBuildTests: These are not affected by the UI changes to MarketInsights entry card components.

The risk is medium because these are UI component changes with animations that could affect rendering performance and visual correctness, but they don't touch core business logic, navigation, or controllers.

Performance Test Selection:
The changes introduce significant new animation work: a new SlidingTextCarousel component with Reanimated animations cycling through text, and a redesigned AnimatedGradientBorder with AnimatedLinearGradient (trail-aligned gradient). These animated components are rendered in the token details page (AssetOverviewContent) and the Perps market details view (PerpsMarketDetailsView). @PerformanceAssetLoading covers token list/details rendering performance which is directly impacted by the new animated MarketInsightsEntryCard in AssetOverviewContent. @PerformancePreps covers the Perps market details view which now includes the animated entry card with skeleton loading state.

View GitHub Actions results

@cursor cursor Bot 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

@zone-live

Copy link
Copy Markdown
Contributor Author

Performance Review points were addressed 👍🏼

@github-actions

Copy link
Copy Markdown
Contributor

E2E Fixture Validation — Schema is up to date
16 value mismatches detected (expected — fixture represents an existing user).
View details

@Prithpal-Sooriya Prithpal-Sooriya 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.

Assets CO LGTM

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
53.9% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@zone-live zone-live requested a review from aganglada March 27, 2026 20:16
@zone-live zone-live enabled auto-merge March 27, 2026 20:16
@zone-live zone-live added this pull request to the merge queue Mar 30, 2026
Merged via the queue into main with commit 868aa7a Mar 30, 2026
195 of 199 checks passed
@zone-live zone-live deleted the TSA-324-mrkt-insights-card-ui-update branch March 30, 2026 08:56
@github-actions github-actions Bot locked and limited conversation to collaborators Mar 30, 2026
@metamaskbot metamaskbot added the release-7.73.0 Issue or pull request that will be included in release 7.73.0 label Mar 30, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

release-7.73.0 Issue or pull request that will be included in release 7.73.0 risk-medium Moderate testing recommended · Possible bug introduction risk size-XL skip-sonar-cloud Only used for bypassing sonar cloud when failures are not relevant to the changes. team-social-ai Social & AI team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants