-
Notifications
You must be signed in to change notification settings - Fork 350
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
✨ (llm) add entry points for reborn LP variant A (#8464)
- Loading branch information
1 parent
208d94a
commit 8cc1438
Showing
16 changed files
with
323 additions
and
51 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"live-mobile": minor | ||
--- | ||
|
||
Update entry points to reborn LP on read only mode |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
6 changes: 6 additions & 0 deletions
6
apps/ledger-live-mobile/src/newArch/features/Reborn/constants/analytics.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
const RebornAnalytics = { | ||
FALLBACK_REBORN: "Fallback_Reborn", | ||
REBORN_LP: "reborn_LP", | ||
} as const; | ||
|
||
export default RebornAnalytics; |
110 changes: 110 additions & 0 deletions
110
apps/ledger-live-mobile/src/newArch/features/Reborn/hooks/useRebornFlow.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import { useRef } from "react"; | ||
import { useFeature } from "@ledgerhq/live-common/featureFlags/index"; | ||
import { ABTestingVariants } from "@ledgerhq/types-live"; | ||
import { useNavigation } from "@react-navigation/native"; | ||
import { | ||
RootNavigationComposite, | ||
StackNavigatorNavigation, | ||
} from "~/components/RootNavigator/types/helpers"; | ||
import { BaseNavigatorStackParamList } from "~/components/RootNavigator/types/BaseNavigator"; | ||
import { NavigatorName, ScreenName } from "~/const"; | ||
import { CategoryContentCard, LandingPageUseCase } from "~/dynamicContent/types"; | ||
import { filterCategoriesByLocation, formatCategories } from "~/dynamicContent/utils"; | ||
import useDynamicContent from "~/dynamicContent/useDynamicContent"; | ||
import { ContentCard } from "@braze/react-native-sdk"; | ||
import { track } from "~/analytics"; | ||
import { useDynamicContentLogic } from "~/dynamicContent/useDynamicContentLogic"; | ||
import useFetchWithTimeout from "LLM/hooks/useFetchWithTimeout"; | ||
import RebornAnalytics from "../constants/analytics"; | ||
|
||
type NavigationProps = RootNavigationComposite< | ||
StackNavigatorNavigation<BaseNavigatorStackParamList> | ||
>; | ||
|
||
const FETCH_TIMEOUT = 3000; | ||
|
||
export function useRebornFlow(isFromOnboarding = false) { | ||
const { navigate } = useNavigation<NavigationProps>(); | ||
const rebornFeatureFlag = useFeature("llmRebornLP"); | ||
const featureFlagEnabled = rebornFeatureFlag?.enabled; | ||
const variant = getVariant(rebornFeatureFlag?.params?.variant); | ||
const { categoriesCards, mobileCards } = useDynamicContent(); | ||
const { fetchData, refreshDynamicContent } = useDynamicContentLogic(); | ||
const canDisplayLP = useRef(false); | ||
|
||
const fetchWithTimeout = useFetchWithTimeout(FETCH_TIMEOUT); | ||
|
||
const fetchAllData = async () => { | ||
refreshDynamicContent(); | ||
try { | ||
await fetchWithTimeout(fetchData); | ||
} catch (error) { | ||
canDisplayLP.current = false; | ||
} | ||
}; | ||
|
||
const checkIfCanDisplayLP = async (LP: LandingPageUseCase) => { | ||
const result = await hasContentCardToDisplay(LP, categoriesCards, mobileCards); | ||
canDisplayLP.current = result; | ||
}; | ||
|
||
const navigateToLandingPage = async (LP: LandingPageUseCase) => { | ||
await checkIfCanDisplayLP(LP); | ||
if (!canDisplayLP.current) { | ||
await fetchAllData(); | ||
await checkIfCanDisplayLP(LP); | ||
} | ||
|
||
if (canDisplayLP.current && !isFromOnboarding) { | ||
track(RebornAnalytics.REBORN_LP); | ||
navigate(NavigatorName.LandingPages, { | ||
screen: ScreenName.GenericLandingPage, | ||
params: { | ||
useCase: LP, | ||
}, | ||
}); | ||
} else { | ||
track(RebornAnalytics.FALLBACK_REBORN); | ||
navigate(NavigatorName.BuyDevice); | ||
} | ||
}; | ||
|
||
const navigateToRebornFlow = () => { | ||
if (!featureFlagEnabled) { | ||
navigate(NavigatorName.BuyDevice); | ||
return; | ||
} | ||
|
||
switch (variant) { | ||
case ABTestingVariants.variantA: | ||
navigateToLandingPage(LandingPageUseCase.LP_Reborn1); | ||
break; | ||
case ABTestingVariants.variantB: | ||
navigateToLandingPage(LandingPageUseCase.LP_Reborn2); | ||
break; | ||
default: | ||
navigate(NavigatorName.BuyDevice); | ||
break; | ||
} | ||
}; | ||
|
||
return { | ||
navigateToRebornFlow, | ||
rebornFeatureFlagEnabled: featureFlagEnabled, | ||
rebornVariant: variant, | ||
}; | ||
} | ||
|
||
const getVariant = (variant?: ABTestingVariants): ABTestingVariants => | ||
variant === ABTestingVariants.variantB ? ABTestingVariants.variantB : ABTestingVariants.variantA; | ||
|
||
const hasContentCardToDisplay = async ( | ||
lpLocation: LandingPageUseCase, | ||
categoriesCards: CategoryContentCard[], | ||
mobileCards: ContentCard[], | ||
) => { | ||
const categoriesToDisplay = filterCategoriesByLocation(categoriesCards, lpLocation); | ||
const categoriesFormatted = formatCategories(categoriesToDisplay, mobileCards); | ||
|
||
return categoriesFormatted.length > 0; | ||
}; |
44 changes: 44 additions & 0 deletions
44
apps/ledger-live-mobile/src/newArch/hooks/__tests__/useFetchWithTimeout.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { renderHook, act } from "@tests/test-renderer"; | ||
import useFetchWithTimeout from "../useFetchWithTimeout"; | ||
|
||
describe("useFetchWithTimeout", () => { | ||
it("should resolve the fetch function result within the timeout", async () => { | ||
const fetchFunction = jest.fn().mockResolvedValue("data"); | ||
const { result } = renderHook(() => useFetchWithTimeout(300)); | ||
|
||
await act(async () => { | ||
const data = await result.current(fetchFunction); | ||
expect(data).toBe("data"); | ||
}); | ||
|
||
expect(fetchFunction).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it("should reject if the fetch function takes longer than the timeout", async () => { | ||
jest.useFakeTimers(); | ||
const fetchFunction = jest | ||
.fn() | ||
.mockImplementation(() => new Promise(resolve => setTimeout(() => resolve("data"), 600))); | ||
const { result } = renderHook(() => useFetchWithTimeout(300)); | ||
|
||
act(() => { | ||
const fetchPromise = result.current(fetchFunction); | ||
jest.advanceTimersByTime(400); | ||
return expect(fetchPromise).rejects.toThrow("Fetch timed out"); | ||
}); | ||
|
||
jest.runAllTimers(); | ||
expect(fetchFunction).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it("should reject if the fetch function throws an error", async () => { | ||
const fetchFunction = jest.fn().mockRejectedValue(new Error("Fetch error")); | ||
const { result } = renderHook(() => useFetchWithTimeout(300)); | ||
|
||
await act(async () => { | ||
await expect(result.current(fetchFunction)).rejects.toThrow("Fetch error"); | ||
}); | ||
|
||
expect(fetchFunction).toHaveBeenCalledTimes(1); | ||
}); | ||
}); |
Oops, something went wrong.