0ed99b840d
* 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>
93 lines
2.1 KiB
TypeScript
93 lines
2.1 KiB
TypeScript
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>
|
|
)
|
|
})
|