Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lazy load ViewShot #5517

Merged
merged 5 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
17 changes: 11 additions & 6 deletions src/components/StarterPack/QrCode.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
import React from 'react'
import {View} from 'react-native'
import QRCode from 'react-native-qrcode-styled'
import ViewShot from 'react-native-view-shot'
import type ViewShot from 'react-native-view-shot'
import {AppBskyGraphDefs, AppBskyGraphStarterpack} from '@atproto/api'
import {Trans} from '@lingui/macro'

import {isWeb} from 'platform/detection'
import {Logo} from 'view/icons/Logo'
import {Logotype} from 'view/icons/Logotype'
import {isWeb} from '#/platform/detection'
import {Logo} from '#/view/icons/Logo'
import {Logotype} from '#/view/icons/Logotype'
import {useTheme} from '#/alf'
import {atoms as a} from '#/alf'
import {LinearGradientBackground} from '#/components/LinearGradientBackground'
import {Text} from '#/components/Typography'

const LazyViewShot = React.lazy(
// @ts-expect-error dynamic import
() => import('react-native-view-shot/src/index'),
)

interface Props {
starterPack: AppBskyGraphDefs.StarterPackView
link: string
Expand All @@ -29,7 +34,7 @@ export const QrCode = React.forwardRef<ViewShot, Props>(function QrCode(
}

return (
<ViewShot ref={ref}>
<LazyViewShot ref={ref}>
<LinearGradientBackground
style={[
{width: 300, minHeight: 390},
Expand Down Expand Up @@ -79,7 +84,7 @@ export const QrCode = React.forwardRef<ViewShot, Props>(function QrCode(
</Text>
</View>
</LinearGradientBackground>
</ViewShot>
</LazyViewShot>
)
})

Expand Down
90 changes: 49 additions & 41 deletions src/components/StarterPack/QrCodeDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import React from 'react'
import {View} from 'react-native'
import ViewShot from 'react-native-view-shot'
import type ViewShot from 'react-native-view-shot'
import {requestMediaLibraryPermissionsAsync} from 'expo-image-picker'
import {createAssetAsync} from 'expo-media-library'
import * as Sharing from 'expo-sharing'
import {AppBskyGraphDefs, AppBskyGraphStarterpack} from '@atproto/api'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'

import {logEvent} from '#/lib/statsig/statsig'
import {logger} from '#/logger'
import {logEvent} from 'lib/statsig/statsig'
import {isNative, isWeb} from 'platform/detection'
import {isNative, isWeb} from '#/platform/detection'
import * as Toast from '#/view/com/util/Toast'
import {atoms as a} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
Expand Down Expand Up @@ -153,46 +153,54 @@ export function QrCodeDialog({
<Dialog.ScrollableInner
label={_(msg`Create a QR code for a starter pack`)}>
<View style={[a.flex_1, a.align_center, a.gap_5xl]}>
{!link ? (
<View style={[a.align_center, a.p_xl]}>
<Loader size="xl" />
</View>
) : (
<>
<QrCode starterPack={starterPack} link={link} ref={ref} />
{isProcessing ? (
<View>
<Loader size="xl" />
</View>
) : (
<View
style={[a.w_full, a.gap_md, isWeb && [a.flex_row_reverse]]}>
<Button
label={_(msg`Copy QR code`)}
variant="solid"
color="secondary"
size="small"
onPress={isWeb ? onCopyPress : onSharePress}>
<ButtonText>
{isWeb ? <Trans>Copy</Trans> : <Trans>Share</Trans>}
</ButtonText>
</Button>
<Button
label={_(msg`Save QR code`)}
variant="solid"
color="secondary"
size="small"
onPress={onSavePress}>
<ButtonText>
<Trans>Save</Trans>
</ButtonText>
</Button>
</View>
)}
</>
)}
<React.Suspense fallback={<Loading />}>
{!link ? (
<Loading />
) : (
<>
<QrCode starterPack={starterPack} link={link} ref={ref} />
{isProcessing ? (
<View>
<Loader size="xl" />
</View>
) : (
<View
style={[a.w_full, a.gap_md, isWeb && [a.flex_row_reverse]]}>
<Button
label={_(msg`Copy QR code`)}
variant="solid"
color="secondary"
size="small"
onPress={isWeb ? onCopyPress : onSharePress}>
<ButtonText>
{isWeb ? <Trans>Copy</Trans> : <Trans>Share</Trans>}
</ButtonText>
</Button>
<Button
label={_(msg`Save QR code`)}
variant="solid"
color="secondary"
size="small"
onPress={onSavePress}>
<ButtonText>
<Trans>Save</Trans>
</ButtonText>
</Button>
</View>
)}
</>
)}
</React.Suspense>
</View>
</Dialog.ScrollableInner>
</Dialog.Outer>
)
}

function Loading() {
return (
<View style={[a.align_center, a.p_xl]}>
<Loader size="xl" />
</View>
)
}
14 changes: 9 additions & 5 deletions src/screens/Onboarding/StepProfile/PlaceholderCanvas.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import React from 'react'
import {View} from 'react-native'
import ViewShot from 'react-native-view-shot'

import {useAvatar} from '#/screens/Onboarding/StepProfile/index'
import {atoms as a} from '#/alf'

const LazyViewShot = React.lazy(
// @ts-expect-error dynamic import
() => import('react-native-view-shot/src/index'),
)

const SIZE_MULTIPLIER = 5

export interface PlaceholderCanvasRef {
capture: () => Promise<string>
capture: () => Promise<string> | undefined
}

// This component is supposed to be invisible to the user. We only need this for ViewShot to have something to
Expand All @@ -33,12 +37,12 @@ export const PlaceholderCanvas = React.forwardRef<PlaceholderCanvasRef, {}>(

React.useImperativeHandle(ref, () => ({
// @ts-ignore this library doesn't have types
capture: viewshotRef.current.capture,
capture: viewshotRef.current?.capture,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are we ok with this method being null in some cases? should we instead wrap to make it a noop?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i tweaked it like this so it will show up as possibly being undefined 3b220f6 (#5517)

im going to see if i can just get rid of that ts-ignore though, i think there actually is a type for this

Copy link
Contributor Author

@haileyok haileyok Sep 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay yea, this feels nicer 1006d70 (#5517) (plus this to only import the type, oops 18462ad)

}))

return (
<View style={styles.container}>
<ViewShot
<LazyViewShot
// @ts-ignore this library doesn't have types
ref={viewshotRef}
options={{
Expand All @@ -60,7 +64,7 @@ export const PlaceholderCanvas = React.forwardRef<PlaceholderCanvasRef, {}>(
style={{color: 'white'}}
/>
</View>
</ViewShot>
</LazyViewShot>
</View>
)
},
Expand Down
16 changes: 10 additions & 6 deletions src/screens/Onboarding/StepProfile/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'

import {useAnalytics} from '#/lib/analytics/analytics'
import {usePhotoLibraryPermission} from '#/lib/hooks/usePermissions'
import {compressIfNeeded} from '#/lib/media/manip'
import {openCropper} from '#/lib/media/picker'
import {getDataUriSize} from '#/lib/media/util'
import {useRequestNotificationsPermission} from '#/lib/notifications/notifications'
import {logEvent, useGate} from '#/lib/statsig/statsig'
import {usePhotoLibraryPermission} from 'lib/hooks/usePermissions'
import {compressIfNeeded} from 'lib/media/manip'
import {openCropper} from 'lib/media/picker'
import {getDataUriSize} from 'lib/media/util'
import {useRequestNotificationsPermission} from 'lib/notifications/notifications'
import {isNative, isWeb} from 'platform/detection'
import {isNative, isWeb} from '#/platform/detection'
import {
DescriptionText,
OnboardingControls,
Expand Down Expand Up @@ -132,6 +132,10 @@ export function StepProfile() {

const onContinue = React.useCallback(async () => {
let imageUri = avatar?.image?.path

// In the event that view-shot didn't load in time and the user pressed continue, this will just be undefined
// and the default avatar will be used. We don't want to block getting through create if this fails for some
// reason
if (!imageUri || avatar.useCreatedAvatar) {
imageUri = await canvasRef.current?.capture()
}
Expand Down
14 changes: 8 additions & 6 deletions src/screens/Onboarding/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,15 @@ export type OnboardingAction =
| {
type: 'setProfileStepResults'
isCreatedAvatar: boolean
image?: OnboardingState['profileStepResults']['image']
imageUri: string
image: OnboardingState['profileStepResults']['image'] | undefined
imageUri: string | undefined
imageMime: string
creatorState?: {
emoji: Emoji
backgroundColor: AvatarColor
}
creatorState:
| {
emoji: Emoji
backgroundColor: AvatarColor
}
| undefined
}

export type ApiResponseMap = {
Expand Down
5 changes: 4 additions & 1 deletion src/screens/Signup/StepInfo/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import * as EmailValidator from 'email-validator'
import type tldts from 'tldts'

import {logEvent} from '#/lib/statsig/statsig'
import {isEmailMaybeInvalid} from '#/lib/strings/email'
import {logger} from '#/logger'
import {isEmailMaybeInvalid} from 'lib/strings/email'
import {ScreenTransition} from '#/screens/Login/ScreenTransition'
import {is13, is18, useSignupContext} from '#/screens/Signup/state'
import {Policies} from '#/screens/Signup/StepInfo/Policies'
Expand Down Expand Up @@ -59,6 +59,9 @@ export function StepInfo({
import('tldts/dist/index.cjs.min.js').then(tldts => {
tldtsRef.current = tldts
})
// This will get used in the avatar creator a few steps later, so lets preload it now
// @ts-expect-error - valid path
import('react-native-view-shot/src/index')
}, [])

const onNextPress = () => {
Expand Down
Loading