Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
58842e3
feat: home screen transaction filter
JMoises-XCode Aug 25, 2025
4203a30
feat: transaction filter by route param test
JMoises-XCode Aug 26, 2025
d3c567e
feat: extend pressable area to transaction filters
JMoises-XCode Sep 2, 2025
73092ae
fix: never used vars
JMoises-XCode Sep 2, 2025
e04b38a
feat: transactio filter button sizes
JMoises-XCode Sep 8, 2025
b558b28
feat: transaction last seen query and schema
esaugomez31 Sep 25, 2025
c56ca53
feat: useUnseenTransactions hooks to get last transactions
esaugomez31 Sep 25, 2025
87c6ca8
feat: notification-badge component
esaugomez31 Sep 25, 2025
a5f3abf
feat: implement notification-badge transaction in home-screen
esaugomez31 Sep 25, 2025
8802955
chore: notification-badge size changed
esaugomez31 Sep 30, 2025
0e10252
feat: bounce-in component
esaugomez31 Sep 30, 2025
3a928c2
feat: using bounce-in animation to show notification badge
esaugomez31 Sep 30, 2025
7f5bf94
fix: replacing @rneui/themed by @rn-vui/themed
esaugomez31 Oct 1, 2025
5758454
fix: Show transaction notification only for received transactions
esaugomez31 Oct 1, 2025
f4b4f93
chore: chore: hide question icon when USD notification is visible
esaugomez31 Oct 1, 2025
df383c7
feat: highlight latest unseen transaction and mark it as seen
esaugomez31 Oct 2, 2025
67094b3
feat: separate scan button with left line
esaugomez31 Oct 2, 2025
e9af519
fix(reanimated): add deps array to useAnimatedStyle to prevent Jest c…
esaugomez31 Oct 16, 2025
4bc1a2c
fix(test): filters only BTC by route param
esaugomez31 Oct 16, 2025
1d094ba
chore(home-screen): QR scan button separator
esaugomez31 Oct 20, 2025
9616303
feat: new qr-code icon
esaugomez31 Oct 20, 2025
c586ea2
feat(transaction-history): mark newest tx as seen on first appearance
esaugomez31 Oct 20, 2025
9a3b23b
refactor: animation separated into components
esaugomez31 Oct 20, 2025
a3dc433
feat: drop-in animation
esaugomez31 Oct 21, 2025
94b32ec
feat(home-screen): incoming badge
esaugomez31 Oct 21, 2025
3140851
feat: slide up component implmentation
esaugomez31 Oct 21, 2025
cbec7d3
feat: outgoing badge style
esaugomez31 Oct 21, 2025
f203eeb
fix(tx-history): reset sticky highlight on focus
esaugomez31 Oct 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 23 additions & 5 deletions __tests__/screens/transaction-history-screen.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import { TransactionHistoryScreen } from "@app/screens/transaction-history"

import { ContextForScreen } from "./helper"

const mockRoute: RouteProp<RootStackParamList, "transactionHistory"> = {
key: "transactionHistory-test",
const mockRouteWithCurrencyFilter = (
currency?: "BTC" | "USD",
): RouteProp<RootStackParamList, "transactionHistory"> => ({
key: `transactionHistory-test`,
name: "transactionHistory",
params: {
wallets: [
Expand All @@ -21,16 +23,17 @@ const mockRoute: RouteProp<RootStackParamList, "transactionHistory"> = {
walletCurrency: "USD",
},
],
...(currency ? { currencyFilter: currency } : {}),
},
}
})

describe("TransactionHistoryScreen", () => {
afterEach(cleanup)

it("shows all transactions by default", async () => {
const { findByTestId } = render(
<ContextForScreen>
<TransactionHistoryScreen route={mockRoute} />
<TransactionHistoryScreen route={mockRouteWithCurrencyFilter()} />
</ContextForScreen>,
)

Expand All @@ -40,7 +43,7 @@ describe("TransactionHistoryScreen", () => {
it("filters only BTC transactions", async () => {
const screen = render(
<ContextForScreen>
<TransactionHistoryScreen route={mockRoute} />
<TransactionHistoryScreen route={mockRouteWithCurrencyFilter()} />
</ContextForScreen>,
)

Expand All @@ -60,4 +63,19 @@ describe("TransactionHistoryScreen", () => {
expect(screen.queryByText("user_usd")).toBeNull()
})
})

it("filters only BTC by route param", async () => {
const screen = render(
<ContextForScreen>
<TransactionHistoryScreen route={mockRouteWithCurrencyFilter("BTC")} />
</ContextForScreen>,
)

await waitFor(() => {
expect(screen.getByTestId("transaction-by-index-0")).toBeTruthy()
})

expect(screen.queryByText("user_btc")).toBeTruthy()
expect(screen.queryByText("user_usd")).toBeNull()
})
})
22 changes: 13 additions & 9 deletions app/assets/icons-redesign/qr-code.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
75 changes: 75 additions & 0 deletions app/components/animations/bounce-in-animation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { useEffect, useRef, useState } from "react"
import {
withSequence,
withTiming,
withSpring,
Easing,
type SharedValue,
} from "react-native-reanimated"

export const bounceInAnimation = ({
scale,
duration,
}: {
scale: SharedValue<number>
duration: number
}) => {
scale.value = 0.88
scale.value = withSequence(
withTiming(1.22, { duration, easing: Easing.out(Easing.quad) }),
withSpring(1, { damping: 12, stiffness: 200 }),
)
}

export const useBounceInAnimation = ({
isFocused,
visible,
scale,
delay,
duration,
}: {
isFocused: boolean
visible: boolean
scale: SharedValue<number>
delay: number
duration: number
}) => {
const [rendered, setRendered] = useState(false)
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
const prevFocused = useRef(false)
const prevVisible = useRef(false)

useEffect(() => {
const screenJustFocused = !prevFocused.current && isFocused
const visibilityJustEnabled = !prevVisible.current && visible
const shouldStartBounce =
isFocused && visible && (screenJustFocused || visibilityJustEnabled)

if (timerRef.current) {
clearTimeout(timerRef.current)
timerRef.current = null
}

if (!isFocused || !visible) {
setRendered(false)
scale.value = 1
}

if (shouldStartBounce) {
setRendered(false)
timerRef.current = setTimeout(() => {
setRendered(true)
bounceInAnimation({ scale, duration })
}, delay)
}

prevFocused.current = isFocused
prevVisible.current = visible

return () => {
if (timerRef.current) clearTimeout(timerRef.current)
}
}, [delay, duration, isFocused, scale, visible])

return rendered
}
84 changes: 84 additions & 0 deletions app/components/animations/drop-in-animation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { useEffect, useRef } from "react"
import { Animated, Easing } from "react-native"

type DropInAnimationParams = {
visible?: boolean
delay?: number
distance?: number
durationIn?: number
overshoot?: number
springStiffness?: number
springDamping?: number
springVelocity?: number
}

export const useDropInAnimation = ({
visible = true,
delay = 0,
distance = 56,
durationIn = 180,
overshoot = 5,
springStiffness = 200,
springDamping = 18,
springVelocity = 0.4,
}: DropInAnimationParams = {}) => {
const opacity = useRef(new Animated.Value(0)).current
const translateY = useRef(new Animated.Value(-distance)).current

useEffect(() => {
opacity.stopAnimation()
translateY.stopAnimation()

if (!visible) {
opacity.setValue(0)
translateY.setValue(-distance)
return
}

opacity.setValue(0)
translateY.setValue(-distance)

const anim = Animated.parallel([
Animated.timing(opacity, {
toValue: 1,
duration: Math.round(durationIn * 0.8),
delay,
easing: Easing.out(Easing.cubic),
useNativeDriver: true,
}),
Animated.sequence([
Animated.timing(translateY, {
toValue: overshoot,
duration: durationIn,
delay,
easing: Easing.in(Easing.quad),
useNativeDriver: true,
}),
Animated.spring(translateY, {
toValue: 0,
stiffness: springStiffness,
damping: springDamping,
mass: 0.6,
velocity: springVelocity,
useNativeDriver: true,
}),
]),
])

anim.start()
return () => anim.stop()
}, [
visible,
delay,
distance,
durationIn,
overshoot,
springStiffness,
springDamping,
springVelocity,
opacity,
translateY,
])

return { opacity, translateY }
}
2 changes: 2 additions & 0 deletions app/components/animations/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./bounce-in-animation"
export * from "./drop-in-animation"
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from "react"

import { WalletCurrency } from "@app/graphql/generated"

import { GaloyCurrencyBubbleText } from "./galoy-currency-bubble-text"
import { Story, UseCase } from "../../../../.storybook/views"

const UseCaseWrapper = ({ children, text, style }) => (
<UseCase style={style} text={text}>
{children}
</UseCase>
)

const styles = {
wrapper: { flexDirection: "row", gap: 12 },
}

export default {
title: "Galoy Currency Bubble",
component: GaloyCurrencyBubbleText,
}

export const Default = () => (
<Story>
<UseCaseWrapper style={styles.wrapper} text="Text Size: p2 (medium)">
<GaloyCurrencyBubbleText textSize="p2" currency={WalletCurrency.Btc} />
<GaloyCurrencyBubbleText textSize="p2" currency={WalletCurrency.Usd} />
</UseCaseWrapper>
<UseCaseWrapper style={styles.wrapper} text="Text Size: p1 (large)">
<GaloyCurrencyBubbleText textSize="p1" currency={WalletCurrency.Btc} />
<GaloyCurrencyBubbleText textSize="p1" currency={WalletCurrency.Usd} />
</UseCaseWrapper>
</Story>
)
Loading
Loading