New user progress guides (#4716)
* Add the animated checkmark svg * Add progress guide list and task components * Add ProgressGuide Toast component * Implement progress-guide controller * Add 7 follows to the progress guide * Wire up action captures * Wire up progress-guide persistence * Trigger progress guide on account creation * Clear the progress guide from storage on complete * Add progress guide interstitial, put behind gate * Fix: read progress guide state from prefs * Some defensive type checks * Create separate toast for completion * List tweaks * Only show on Discover * Spacing and progress tweaks * Completely hide when complete * Capture the progress guide in local state, and only render toasts while guide is active * Fix: ensure persisted hydrates into local state * Gate --------- Co-authored-by: Eric Bailey <git@esb.lol> Co-authored-by: Dan Abramov <dan.abramov@gmail.com>
This commit is contained in:
parent
aa7117edb6
commit
0ed99b840d
19 changed files with 721 additions and 22 deletions
92
src/components/anim/AnimatedCheck.tsx
Normal file
92
src/components/anim/AnimatedCheck.tsx
Normal file
|
@ -0,0 +1,92 @@
|
|||
import React from 'react'
|
||||
import Animated, {
|
||||
Easing,
|
||||
useAnimatedProps,
|
||||
useSharedValue,
|
||||
withDelay,
|
||||
withTiming,
|
||||
} from 'react-native-reanimated'
|
||||
import Svg, {Circle, Path} from 'react-native-svg'
|
||||
|
||||
import {Props, useCommonSVGProps} from '#/components/icons/common'
|
||||
|
||||
const AnimatedPath = Animated.createAnimatedComponent(Path)
|
||||
const AnimatedCircle = Animated.createAnimatedComponent(Circle)
|
||||
|
||||
const PATH = 'M14.1 27.2l7.1 7.2 16.7-16.8'
|
||||
|
||||
export interface AnimatedCheckRef {
|
||||
play(cb?: () => void): void
|
||||
}
|
||||
|
||||
export interface AnimatedCheckProps extends Props {
|
||||
playOnMount?: boolean
|
||||
}
|
||||
|
||||
export const AnimatedCheck = React.forwardRef<
|
||||
AnimatedCheckRef,
|
||||
AnimatedCheckProps
|
||||
>(function AnimatedCheck({playOnMount, ...props}, ref) {
|
||||
const {fill, size, style, ...rest} = useCommonSVGProps(props)
|
||||
const circleAnim = useSharedValue(0)
|
||||
const checkAnim = useSharedValue(0)
|
||||
|
||||
const circleAnimatedProps = useAnimatedProps(() => ({
|
||||
strokeDashoffset: 166 - circleAnim.value * 166,
|
||||
}))
|
||||
const checkAnimatedProps = useAnimatedProps(() => ({
|
||||
strokeDashoffset: 48 - 48 * checkAnim.value,
|
||||
}))
|
||||
|
||||
const play = React.useCallback(
|
||||
(cb?: () => void) => {
|
||||
circleAnim.value = 0
|
||||
checkAnim.value = 0
|
||||
|
||||
circleAnim.value = withTiming(1, {duration: 500, easing: Easing.linear})
|
||||
checkAnim.value = withDelay(
|
||||
500,
|
||||
withTiming(1, {duration: 300, easing: Easing.linear}, cb),
|
||||
)
|
||||
},
|
||||
[circleAnim, checkAnim],
|
||||
)
|
||||
|
||||
React.useImperativeHandle(ref, () => ({
|
||||
play,
|
||||
}))
|
||||
|
||||
React.useEffect(() => {
|
||||
if (playOnMount) {
|
||||
play()
|
||||
}
|
||||
}, [play, playOnMount])
|
||||
|
||||
return (
|
||||
<Svg
|
||||
fill="none"
|
||||
{...rest}
|
||||
viewBox="0 0 52 52"
|
||||
width={size}
|
||||
height={size}
|
||||
style={style}>
|
||||
<AnimatedCircle
|
||||
animatedProps={circleAnimatedProps}
|
||||
cx="26"
|
||||
cy="26"
|
||||
r="24"
|
||||
fill="none"
|
||||
stroke={fill}
|
||||
strokeWidth={4}
|
||||
strokeDasharray={166}
|
||||
/>
|
||||
<AnimatedPath
|
||||
animatedProps={checkAnimatedProps}
|
||||
stroke={fill}
|
||||
d={PATH}
|
||||
strokeWidth={4}
|
||||
strokeDasharray={48}
|
||||
/>
|
||||
</Svg>
|
||||
)
|
||||
})
|
Loading…
Add table
Add a link
Reference in a new issue