Skip to content

Commit

Permalink
fix: device media padding cleanup (#265)
Browse files Browse the repository at this point in the history
Signed-off-by: Jan <[email protected]>
  • Loading branch information
janrtvld authored Jan 22, 2025
1 parent caef0ce commit 5b3d08d
Show file tree
Hide file tree
Showing 8 changed files with 38 additions and 35 deletions.
9 changes: 3 additions & 6 deletions apps/easypid/src/app/authenticate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { TypedArrayEncoder, WalletInvalidKeyError } from '@credo-ts/core'
import { initializeAppAgent, useSecureUnlock } from '@easypid/agent'
import { useBiometricsType } from '@easypid/hooks/useBiometricsType'
import { secureWalletKey } from '@package/secure-store/secureUnlock'
import { FlexPage, Heading, HeroIcons, IconContainer, YStack, useToastController } from '@package/ui'
import { FlexPage, Heading, HeroIcons, IconContainer, YStack, useDeviceMedia, useToastController } from '@package/ui'
import * as SplashScreen from 'expo-splash-screen'
import { PinDotsInput, type PinDotsInputRef } from 'packages/app/src'
import { useEffect, useRef, useState } from 'react'
Expand All @@ -22,6 +22,7 @@ export default function Authenticate() {
const secureUnlock = useSecureUnlock()
const biometricsType = useBiometricsType()
const pinInputRef = useRef<PinDotsInputRef>(null)
const { additionalPadding, noBottomSafeArea } = useDeviceMedia()
const [isInitializingAgent, setIsInitializingAgent] = useState(false)
const [isAllowedToUnlockWithFaceId, setIsAllowedToUnlockWithFaceId] = useState(false)
const isLoading =
Expand All @@ -35,10 +36,6 @@ export default function Authenticate() {
return () => clearTimeout(timer)
}, [])

// Make the pin pad fixed to the bottom of the screen on smaller devices
const { bottom } = useSafeAreaInsets()
const shouldStickToBottom = bottom < 16

// biome-ignore lint/correctness/useExhaustiveDependencies: canTryUnlockingUsingBiometrics not needed
useEffect(() => {
if (secureUnlock.state === 'locked' && secureUnlock.canTryUnlockingUsingBiometrics && isAllowedToUnlockWithFaceId) {
Expand Down Expand Up @@ -105,7 +102,7 @@ export default function Authenticate() {

return (
<FlexPage flex-1 safeArea="y" alignItems="center">
<YStack fg={1} gap="$6" mb={shouldStickToBottom ? -16 : undefined}>
<YStack fg={1} gap="$6" mb={noBottomSafeArea ? -additionalPadding : undefined}>
<YStack flex-1 alignItems="center" justifyContent="flex-end" gap="$4">
<IconContainer h="$4" w="$4" ai="center" jc="center" icon={<HeroIcons.LockClosedFilled />} />
<Heading variant="h2" fontWeight="$semiBold">
Expand Down
11 changes: 3 additions & 8 deletions apps/easypid/src/features/onboarding/screens/id-card-pin.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { IdCard, Paragraph, PinPad, PinValues, ScrollView, Stack, XStack, YStack, useMedia } from '@package/ui'
import { IdCard, Paragraph, PinPad, PinValues, ScrollView, Stack, XStack, YStack, useDeviceMedia } from '@package/ui'
import React, { forwardRef, useImperativeHandle, useRef, useState } from 'react'
import type { TextInput } from 'react-native'

import { useHaptics } from 'packages/app/src/hooks/useHaptics'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import germanIssuerImage from '../../../../assets/german-issuer-image.png'
import pidBackgroundImage from '../../../../assets/pid-background.png'

Expand All @@ -16,13 +15,9 @@ const pinLength = 6
export const OnboardingIdCardPinEnter = forwardRef(({ goToNextStep }: OnboardingIdCardPinEnterProps, ref) => {
const [pin, setPin] = useState('')
const inputRef = useRef<TextInput>(null)
const media = useMedia()
const { media, additionalPadding, noBottomSafeArea } = useDeviceMedia()
const { withHaptics } = useHaptics()

// Make the pin pad fixed to the bottom of the screen on smaller devices
const { bottom } = useSafeAreaInsets()
const shouldStickToBottom = bottom < 16

useImperativeHandle(ref, () => ({
focus: () => inputRef.current?.focus(),
clear: () => setPin(''),
Expand Down Expand Up @@ -66,7 +61,7 @@ export const OnboardingIdCardPinEnter = forwardRef(({ goToNextStep }: Onboarding
})

return (
<YStack fg={1} jc="space-between" mb={shouldStickToBottom ? -16 : undefined}>
<YStack fg={1} jc="space-between" mb={noBottomSafeArea ? -additionalPadding : undefined}>
{/* Overflow issue only present on smaller devices, so set to max height */}
<ScrollView flex={1} maxHeight={media.short ? 150 : undefined}>
<Stack jc="center">
Expand Down
9 changes: 3 additions & 6 deletions apps/easypid/src/features/onboarding/screens/pin.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { YStack } from '@package/ui'
import { YStack, useDeviceMedia } from '@package/ui'
import { PinDotsInput } from 'packages/app/src'
import type { PinDotsInputRef } from 'packages/app/src'
import React, { useRef, useState } from 'react'
Expand All @@ -11,10 +11,7 @@ export interface OnboardingPinEnterProps {
export default function OnboardingPinEnter({ goToNextStep }: OnboardingPinEnterProps) {
const [isLoading, setIsLoading] = useState(false)
const pinRef = useRef<PinDotsInputRef>(null)

// Make the pin pad fixed to the bottom of the screen on smaller devices
const { bottom } = useSafeAreaInsets()
const shouldStickToBottom = bottom < 16
const { additionalPadding, noBottomSafeArea } = useDeviceMedia()

const onPinComplete = (pin: string) => {
setIsLoading(true)
Expand All @@ -28,7 +25,7 @@ export default function OnboardingPinEnter({ goToNextStep }: OnboardingPinEnterP
}

return (
<YStack mt="$10" fg={1} mb={shouldStickToBottom ? -16 : undefined}>
<YStack mt="$10" fg={1} mb={noBottomSafeArea ? -additionalPadding : undefined}>
<PinDotsInput
onPinComplete={onPinComplete}
isLoading={isLoading}
Expand Down
11 changes: 3 additions & 8 deletions apps/easypid/src/features/pid/PidWalletPinSlide.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Heading, Paragraph, YStack } from '@package/ui'
import { Heading, Paragraph, YStack, useDeviceMedia } from '@package/ui'
import { PinDotsInput, type PinDotsInputRef, useWizard } from 'packages/app/src'
import { useRef, useState } from 'react'
import { useSafeAreaInsets } from 'react-native-safe-area-context'

interface PidWalletPinSlideProps {
title: string
Expand All @@ -13,11 +12,7 @@ export function PidWalletPinSlide({ title, subtitle, onEnterPin }: PidWalletPinS
const { onNext } = useWizard()
const [isLoading, setIsLoading] = useState(false)
const ref = useRef<PinDotsInputRef>(null)

// Make the pin pad fixed to the bottom of the screen on smaller devices
const { bottom } = useSafeAreaInsets()
const shouldStickToBottom = bottom < 16

const { additionalPadding, noBottomSafeArea } = useDeviceMedia()
const onSubmitPin = async (pin: string) => {
if (isLoading) return
setIsLoading(true)
Expand All @@ -35,7 +30,7 @@ export function PidWalletPinSlide({ title, subtitle, onEnterPin }: PidWalletPinS
}

return (
<YStack fg={1} jc="space-between" mb={shouldStickToBottom ? -16 : undefined}>
<YStack fg={1} jc="space-between" mb={noBottomSafeArea ? -additionalPadding : undefined}>
<YStack gap="$6">
<YStack gap="$3">
<Heading variant="h1">{title}</Heading>
Expand Down
16 changes: 10 additions & 6 deletions packages/ui/src/base/Page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { styled } from 'tamagui'

import { useSafeAreaInsets } from 'react-native-safe-area-context'
import { useDeviceMedia } from '../hooks/useDeviceMedia'
import { Stack } from './Stacks'

export const Page = styled(Stack, {
Expand All @@ -25,19 +26,22 @@ const FlexPageBase = styled(Stack, {
export const FlexPage = FlexPageBase.styleable<{ safeArea?: boolean | 'x' | 'y' | 'l' | 'b' | 't' | 'r' }>(
(props, ref) => {
const safeAreaInsets = useSafeAreaInsets()
const { additionalPadding } = useDeviceMedia()

// Not defined means true, not sure why defaultVariant doesn't work
const safeArea = props.safeArea ?? true

// Some devices have no bottom safe area, so we add a default of 16px so the content is not against the edge
// Some devices have no bottom safe area, so we add a default padding so the content is not against the edge
const bottom =
safeArea === true || safeArea === 'y' || safeArea === 'b' ? Math.max(safeAreaInsets.bottom, 16) : undefined

// We add an extra 16px to the top margin to give the header some more space on smaller devices
const additionalTop = safeAreaInsets.top <= 24 ? 16 : 0
safeArea === true || safeArea === 'y' || safeArea === 'b'
? Math.max(safeAreaInsets.bottom, additionalPadding)
: undefined

// Some devices have little to no top safe area, so we add a default padding so the content is not against the edge
const top =
safeArea === true || safeArea === 'y' || safeArea === 't' ? safeAreaInsets.top + additionalTop : undefined
safeArea === true || safeArea === 'y' || safeArea === 't'
? Math.max(safeAreaInsets.top, additionalPadding)
: undefined
const left = safeArea === true || safeArea === 'x' || safeArea === 'l' ? safeAreaInsets.left : undefined
const right = safeArea === true || safeArea === 'x' || safeArea === 'r' ? safeAreaInsets.right : undefined

Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/content/IconContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { cloneElement } from 'react'
import { Circle, type StackProps } from 'tamagui'
import type { StackProps } from 'tamagui'
import { AnimatedStack } from '../base'
import { useScaleAnimation } from '../hooks'

Expand Down
1 change: 1 addition & 0 deletions packages/ui/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export { useScaleAnimation } from './useScaleAnimation'
export { useInitialRender } from './useInitialRender'
export { useMinimumLoadingTime } from './useMinimumLoadingTime'
export { useScrollToggle } from './useScrollToggle'
export { useDeviceMedia } from './useDeviceMedia'
14 changes: 14 additions & 0 deletions packages/ui/src/hooks/useDeviceMedia.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { useMedia } from '@tamagui/web'
import { useSafeAreaInsets } from 'react-native-safe-area-context'

const SHORT_DEVICE_PADDING = 16
const LONG_DEVICE_PADDING = 24

export const useDeviceMedia = () => {
const media = useMedia()
const additionalPadding = media.short ? SHORT_DEVICE_PADDING : LONG_DEVICE_PADDING
const { bottom } = useSafeAreaInsets()
const noBottomSafeArea = bottom === 0

return { media, additionalPadding, noBottomSafeArea }
}

0 comments on commit 5b3d08d

Please sign in to comment.