Skip to content

Commit 5d9e046

Browse files
Merge pull request #14748 from LedgerHQ/feat/earn-background-scroll-fade
feat(mobile): add scroll-based fade animation to earn background
2 parents 2dbdf28 + ebe9573 commit 5d9e046

File tree

4 files changed

+49
-8
lines changed

4 files changed

+49
-8
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"live-mobile": minor
3+
---
4+
5+
Add scroll-based fade animation to Earn background image

apps/ledger-live-mobile/src/screens/PTX/Earn/EarnBackground.tsx

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
import React, { memo, useMemo } from "react";
2-
import { View, ImageBackground } from "react-native";
2+
import { Animated, ImageBackground, View } from "react-native";
33
import { useTheme, useStyleSheet } from "@ledgerhq/lumen-ui-rnative/styles";
44

5-
function EarnBackgroundComponent() {
5+
const FADE_DISTANCE = 150;
6+
7+
type Props = {
8+
scrollY?: Animated.Value;
9+
fadeDistance?: number;
10+
};
11+
12+
function EarnBackgroundComponent({ scrollY, fadeDistance }: Props) {
613
const { colorScheme } = useTheme();
714
const styles = useStyleSheet(
815
theme => ({
@@ -25,11 +32,27 @@ function EarnBackgroundComponent() {
2532
return colorScheme === "dark";
2633
}, [colorScheme]);
2734

35+
const opacity = useMemo(
36+
() =>
37+
scrollY
38+
? scrollY.interpolate({
39+
inputRange: [0, fadeDistance ?? FADE_DISTANCE],
40+
outputRange: [1, 0],
41+
extrapolate: "clamp",
42+
})
43+
: 1,
44+
[scrollY, fadeDistance],
45+
);
46+
2847
const source = require("~/images/liveApps/earn/background-dark.webp");
2948

3049
return (
3150
<View style={styles.container} pointerEvents="none">
32-
{isDark && <ImageBackground source={source} style={styles.imageContainer} />}
51+
{isDark && (
52+
<Animated.View style={{ opacity }}>
53+
<ImageBackground source={source} style={styles.imageContainer} />
54+
</Animated.View>
55+
)}
3356
</View>
3457
);
3558
}

apps/ledger-live-mobile/src/screens/PTX/Earn/EarnV2Webview/index.tsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ import useFeature from "@ledgerhq/live-common/featureFlags/useFeature";
22
import { useRemoteLiveAppContext } from "@ledgerhq/live-common/platform/providers/RemoteLiveAppProvider/index";
33
import { LiveAppManifest } from "@ledgerhq/live-common/platform/types";
44
import { Flex } from "@ledgerhq/native-ui";
5-
import React, { Fragment } from "react";
6-
import { View } from "react-native";
5+
import React, { ComponentProps, Fragment, useRef, useCallback } from "react";
6+
import { Animated, View } from "react-native";
7+
import type WebView from "react-native-webview";
78
import { useSafeAreaInsets } from "react-native-safe-area-context";
89
import { TrackScreen } from "~/analytics";
910
import GenericErrorView from "~/components/GenericErrorView";
@@ -33,6 +34,14 @@ export const EarnV2Webview = ({
3334
const earnUiVersion = earnUiFlag?.params?.value ?? "v1";
3435
const isPtxUiV2 = earnUiVersion === "v2" || earnUiVersion === "2";
3536

37+
const scrollY = useRef(new Animated.Value(0)).current;
38+
const handleScroll = useCallback<NonNullable<ComponentProps<typeof WebView>["onScroll"]>>(
39+
event => {
40+
scrollY.setValue(event.nativeEvent.contentOffset.y);
41+
},
42+
[scrollY],
43+
);
44+
3645
const webviewInputs = {
3746
...inputs,
3847
safeAreaTop: insets.top.toString(),
@@ -47,7 +56,7 @@ export const EarnV2Webview = ({
4756

4857
return (
4958
<View style={{ flex: 1, overflow: "visible" }}>
50-
{isPtxUiV2 && !hideMainNavigator && <EarnBackground />}
59+
{isPtxUiV2 && !hideMainNavigator && <EarnBackground scrollY={scrollY} />}
5160
<View style={{ flex: 1, zIndex: 1 }} pointerEvents="box-none">
5261
{manifest ? (
5362
<Fragment>
@@ -56,6 +65,7 @@ export const EarnV2Webview = ({
5665
manifest={manifest}
5766
inputs={webviewInputs}
5867
isLwm40Enabled={isLwm40Enabled}
68+
onScroll={isPtxUiV2 && !hideMainNavigator ? handleScroll : undefined}
5969
/>
6070
</Fragment>
6171
) : (

apps/ledger-live-mobile/src/screens/PTX/Earn/EarnWebview/index.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
1+
import React, { ComponentProps, useCallback, useEffect, useMemo, useRef, useState } from "react";
22
import { View, StyleSheet, SafeAreaView, BackHandler, Platform } from "react-native";
33
import { useSelector } from "~/context/hooks";
44

55
import { LiveAppManifest } from "@ledgerhq/live-common/platform/types";
66
import { safeGetRefValue } from "@ledgerhq/live-common/wallet-api/react";
77
import { safeUrl } from "@ledgerhq/live-common/wallet-api/helpers";
8+
import WebView from "react-native-webview";
89

910
import { useNavigation, useFocusEffect } from "@react-navigation/native";
1011

@@ -37,9 +38,10 @@ type Props = {
3738
manifest: LiveAppManifest;
3839
inputs?: Record<string, string | undefined>;
3940
isLwm40Enabled?: boolean;
41+
onScroll?: ComponentProps<typeof WebView>["onScroll"];
4042
};
4143
/** Subset of WebPTXPlayer functionality required for Earn live app. */
42-
export const EarnWebview = ({ manifest, inputs, isLwm40Enabled }: Props) => {
44+
export const EarnWebview = ({ manifest, inputs, isLwm40Enabled, onScroll }: Props) => {
4345
const webviewAPIRef = useRef<WebviewAPI>(null);
4446
const [webviewState, setWebviewState] = useState<WebviewState>(initialWebviewState);
4547

@@ -149,6 +151,7 @@ export const EarnWebview = ({ manifest, inputs, isLwm40Enabled }: Props) => {
149151
inputs={inputs}
150152
onStateChange={setWebviewState}
151153
customHandlers={customHandlers}
154+
onScroll={onScroll}
152155
Loader={() => <Loading backgroundColor="transparent" />}
153156
/>
154157
</Container>

0 commit comments

Comments
 (0)