vendor progress circle
This commit is contained in:
@@ -222,7 +222,6 @@
|
||||
"react-native-gesture-handler": "~2.28.0",
|
||||
"react-native-keyboard-controller": "^1.21.7",
|
||||
"react-native-pager-view": "6.8.0",
|
||||
"react-native-progress": "bluesky-social/react-native-progress",
|
||||
"react-native-qrcode-styled": "^0.3.3",
|
||||
"react-native-reanimated": "3.19.1",
|
||||
"react-native-safe-area-context": "~5.6.0",
|
||||
|
||||
Generated
-14
@@ -454,9 +454,6 @@ importers:
|
||||
react-native-pager-view:
|
||||
specifier: 6.8.0
|
||||
version: 6.8.0(patch_hash=0979d6fe1e2fa43b2784cc0b5e0ff0117d53b53423e85ee5855eefdc41a00108)(react-native@0.81.5(patch_hash=2656ac6deb71b92a4df4af4593d13ace6a5740936432e5e7e8ef32bc3cd05194)(@babel/core@7.29.0)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
|
||||
react-native-progress:
|
||||
specifier: bluesky-social/react-native-progress
|
||||
version: https://codeload.github.com/bluesky-social/react-native-progress/tar.gz/5a372f4f2ce5feb26f4f47b6a4d187ab9b923ab4(react-native-svg@15.12.1(patch_hash=31401fa6ff01498773afb51a7b066821c471eb3e71b0764a10017938beed49e9)(react-native@0.81.5(patch_hash=2656ac6deb71b92a4df4af4593d13ace6a5740936432e5e7e8ef32bc3cd05194)(@babel/core@7.29.0)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))
|
||||
react-native-qrcode-styled:
|
||||
specifier: ^0.3.3
|
||||
version: 0.3.3(react-native-svg@15.12.1(patch_hash=31401fa6ff01498773afb51a7b066821c471eb3e71b0764a10017938beed49e9)(react-native@0.81.5(patch_hash=2656ac6deb71b92a4df4af4593d13ace6a5740936432e5e7e8ef32bc3cd05194)(@babel/core@7.29.0)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=2656ac6deb71b92a4df4af4593d13ace6a5740936432e5e7e8ef32bc3cd05194)(@babel/core@7.29.0)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
|
||||
@@ -8686,12 +8683,6 @@ packages:
|
||||
react: '*'
|
||||
react-native: '*'
|
||||
|
||||
react-native-progress@https://codeload.github.com/bluesky-social/react-native-progress/tar.gz/5a372f4f2ce5feb26f4f47b6a4d187ab9b923ab4:
|
||||
resolution: {gitHosted: true, integrity: sha512-gDJJirvdhK5uUuI2OkiuleTbmeCyjkf1G6kPd4HAMc1pW8pUUeA0wKyPxZaI+s9htw8IDH6mFlmBB1ob8QMB4g==, tarball: https://codeload.github.com/bluesky-social/react-native-progress/tar.gz/5a372f4f2ce5feb26f4f47b6a4d187ab9b923ab4}
|
||||
version: 5.0.0
|
||||
peerDependencies:
|
||||
react-native-svg: '*'
|
||||
|
||||
react-native-qrcode-styled@0.3.3:
|
||||
resolution: {integrity: sha512-DjxFhFAJBY3y2FEBbWSpwtPaSjsEc5RutTHejxzfRNuyTzg1f5thplGyON8yx0kbi6h0h+57s8cH4aSFGj1mQg==}
|
||||
engines: {node: '>= 16.0.0'}
|
||||
@@ -19954,11 +19945,6 @@ snapshots:
|
||||
react: 19.1.0
|
||||
react-native: 0.81.5(patch_hash=2656ac6deb71b92a4df4af4593d13ace6a5740936432e5e7e8ef32bc3cd05194)(@babel/core@7.29.0)(@types/react@19.1.17)(react@19.1.0)
|
||||
|
||||
react-native-progress@https://codeload.github.com/bluesky-social/react-native-progress/tar.gz/5a372f4f2ce5feb26f4f47b6a4d187ab9b923ab4(react-native-svg@15.12.1(patch_hash=31401fa6ff01498773afb51a7b066821c471eb3e71b0764a10017938beed49e9)(react-native@0.81.5(patch_hash=2656ac6deb71b92a4df4af4593d13ace6a5740936432e5e7e8ef32bc3cd05194)(@babel/core@7.29.0)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)):
|
||||
dependencies:
|
||||
prop-types: 15.8.1
|
||||
react-native-svg: 15.12.1(patch_hash=31401fa6ff01498773afb51a7b066821c471eb3e71b0764a10017938beed49e9)(react-native@0.81.5(patch_hash=2656ac6deb71b92a4df4af4593d13ace6a5740936432e5e7e8ef32bc3cd05194)(@babel/core@7.29.0)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
|
||||
|
||||
react-native-qrcode-styled@0.3.3(react-native-svg@15.12.1(patch_hash=31401fa6ff01498773afb51a7b066821c471eb3e71b0764a10017938beed49e9)(react-native@0.81.5(patch_hash=2656ac6deb71b92a4df4af4593d13ace6a5740936432e5e7e8ef32bc3cd05194)(@babel/core@7.29.0)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=2656ac6deb71b92a4df4af4593d13ace6a5740936432e5e7e8ef32bc3cd05194)(@babel/core@7.29.0)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0):
|
||||
dependencies:
|
||||
qrcode: 1.5.4
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
import {useEffect} from 'react'
|
||||
import Animated, {
|
||||
Easing,
|
||||
useAnimatedProps,
|
||||
useSharedValue,
|
||||
withTiming,
|
||||
} from 'react-native-reanimated'
|
||||
import Svg, {Circle as SvgCircle} from 'react-native-svg'
|
||||
|
||||
import {clamp01} from './shared'
|
||||
|
||||
const AnimatedCircle = Animated.createAnimatedComponent(SvgCircle)
|
||||
|
||||
export function ProgressCircle({
|
||||
progress,
|
||||
size,
|
||||
color,
|
||||
thickness = 3,
|
||||
borderWidth = 1,
|
||||
borderColor,
|
||||
unfilledColor,
|
||||
}: {
|
||||
progress: number
|
||||
size: number
|
||||
color: string
|
||||
thickness?: number
|
||||
borderWidth?: number
|
||||
borderColor?: string
|
||||
unfilledColor?: string
|
||||
}) {
|
||||
const stroke = borderColor ?? color
|
||||
const r = (size - Math.max(borderWidth, thickness)) / 2
|
||||
const cx = size / 2
|
||||
const circumference = 2 * Math.PI * r
|
||||
const sv = useSharedValue(clamp01(progress))
|
||||
|
||||
useEffect(() => {
|
||||
sv.set(
|
||||
withTiming(clamp01(progress), {
|
||||
duration: 200,
|
||||
easing: Easing.out(Easing.quad),
|
||||
}),
|
||||
)
|
||||
}, [progress, sv])
|
||||
|
||||
const animatedProps = useAnimatedProps(() => ({
|
||||
strokeDashoffset: circumference * (1 - sv.get()),
|
||||
}))
|
||||
|
||||
return (
|
||||
<Svg width={size} height={size}>
|
||||
{borderWidth > 0 && (
|
||||
<SvgCircle
|
||||
cx={cx}
|
||||
cy={cx}
|
||||
r={r}
|
||||
stroke={stroke}
|
||||
strokeWidth={borderWidth}
|
||||
fill="none"
|
||||
/>
|
||||
)}
|
||||
{unfilledColor && (
|
||||
<SvgCircle
|
||||
cx={cx}
|
||||
cy={cx}
|
||||
r={r}
|
||||
stroke={unfilledColor}
|
||||
strokeWidth={thickness}
|
||||
fill="none"
|
||||
/>
|
||||
)}
|
||||
<AnimatedCircle
|
||||
cx={cx}
|
||||
cy={cx}
|
||||
r={r}
|
||||
stroke={color}
|
||||
strokeWidth={thickness}
|
||||
fill="none"
|
||||
strokeDasharray={circumference}
|
||||
animatedProps={animatedProps}
|
||||
transform={`rotate(-90 ${cx} ${cx})`}
|
||||
/>
|
||||
</Svg>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import {useEffect} from 'react'
|
||||
import Animated, {
|
||||
Easing,
|
||||
useAnimatedProps,
|
||||
useSharedValue,
|
||||
withTiming,
|
||||
} from 'react-native-reanimated'
|
||||
import Svg, {Circle as SvgCircle, Path} from 'react-native-svg'
|
||||
|
||||
import {clamp01} from './shared'
|
||||
|
||||
const AnimatedPath = Animated.createAnimatedComponent(Path)
|
||||
|
||||
function describeWedge(cx: number, cy: number, r: number, progress: number) {
|
||||
'worklet'
|
||||
// avoid degenerate path at 360deg where start and end points collide
|
||||
const p = progress >= 1 ? 0.9999 : progress
|
||||
if (p <= 0) return ''
|
||||
const angle = p * 2 * Math.PI - Math.PI / 2
|
||||
const x = cx + r * Math.cos(angle)
|
||||
const y = cy + r * Math.sin(angle)
|
||||
const largeArc = p > 0.5 ? 1 : 0
|
||||
return `M ${cx} ${cy} L ${cx} ${cy - r} A ${r} ${r} 0 ${largeArc} 1 ${x} ${y} Z`
|
||||
}
|
||||
|
||||
export function ProgressPie({
|
||||
progress,
|
||||
size,
|
||||
color,
|
||||
borderWidth = 1,
|
||||
borderColor,
|
||||
}: {
|
||||
progress: number
|
||||
size: number
|
||||
color: string
|
||||
borderWidth?: number
|
||||
borderColor?: string
|
||||
}) {
|
||||
const stroke = borderColor ?? color
|
||||
const r = (size - borderWidth) / 2
|
||||
const cx = size / 2
|
||||
const sv = useSharedValue(clamp01(progress))
|
||||
|
||||
useEffect(() => {
|
||||
sv.set(
|
||||
withTiming(clamp01(progress), {
|
||||
duration: 200,
|
||||
easing: Easing.out(Easing.quad),
|
||||
}),
|
||||
)
|
||||
}, [progress, sv])
|
||||
|
||||
const animatedProps = useAnimatedProps(() => ({
|
||||
d: describeWedge(cx, cx, r, sv.get()),
|
||||
}))
|
||||
|
||||
return (
|
||||
<Svg width={size} height={size}>
|
||||
<SvgCircle
|
||||
cx={cx}
|
||||
cy={cx}
|
||||
r={r}
|
||||
stroke={stroke}
|
||||
strokeWidth={borderWidth}
|
||||
fill="none"
|
||||
/>
|
||||
<AnimatedPath fill={color} animatedProps={animatedProps} />
|
||||
</Svg>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export {ProgressCircle} from './Circle'
|
||||
export {ProgressPie} from './Pie'
|
||||
@@ -0,0 +1,6 @@
|
||||
export function clamp01(n: number) {
|
||||
'worklet'
|
||||
if (n < 0) return 0
|
||||
if (n > 1) return 1
|
||||
return n
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import {View} from 'react-native'
|
||||
import * as Progress from 'react-native-progress'
|
||||
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {AnimatedCheck} from '../anim/AnimatedCheck'
|
||||
import {ProgressCircle} from '../Progress'
|
||||
import {Text} from '../Typography'
|
||||
|
||||
export function ProgressGuideTask({
|
||||
@@ -25,7 +25,7 @@ export function ProgressGuideTask({
|
||||
{current === total ? (
|
||||
<AnimatedCheck playOnMount fill={t.palette.primary_500} width={20} />
|
||||
) : (
|
||||
<Progress.Circle
|
||||
<ProgressCircle
|
||||
progress={current / total}
|
||||
color={t.palette.primary_400}
|
||||
size={20}
|
||||
|
||||
@@ -21,8 +21,6 @@ import {
|
||||
View,
|
||||
type ViewStyle,
|
||||
} from 'react-native'
|
||||
// @ts-expect-error no type definition
|
||||
import ProgressCircle from 'react-native-progress/Circle'
|
||||
import Animated, {
|
||||
type AnimatedRef,
|
||||
Easing,
|
||||
@@ -129,6 +127,7 @@ import {EmojiArc_Stroke2_Corner0_Rounded as EmojiSmileIcon} from '#/components/i
|
||||
import {PlusLarge_Stroke2_Corner0_Rounded as PlusIcon} from '#/components/icons/Plus'
|
||||
import {TimesLarge_Stroke2_Corner0_Rounded as XIcon} from '#/components/icons/Times'
|
||||
import {LazyQuoteEmbed} from '#/components/Post/Embed/LazyQuoteEmbed'
|
||||
import {ProgressCircle} from '#/components/Progress'
|
||||
import * as Prompt from '#/components/Prompt'
|
||||
import * as Toast from '#/components/Toast'
|
||||
import {Text} from '#/components/Typography'
|
||||
@@ -2072,10 +2071,7 @@ function ComposerFooter({
|
||||
onSelectLanguage={onSelectLanguage}
|
||||
nudgeAt={languageNudgeAt}
|
||||
/>
|
||||
<CharProgress
|
||||
count={post.shortenedGraphemeLength}
|
||||
style={{width: 65}}
|
||||
/>
|
||||
<CharProgress count={post.shortenedGraphemeLength} />
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
@@ -2413,7 +2409,7 @@ function ToolbarWrapper({
|
||||
if (IS_WEB) return children
|
||||
return (
|
||||
<Animated.View
|
||||
style={style}
|
||||
style={[style, {minHeight: 40}]}
|
||||
entering={FadeIn.duration(400)}
|
||||
exiting={FadeOut.duration(400)}>
|
||||
{children}
|
||||
@@ -2488,12 +2484,12 @@ function VideoUploadToolbar({state}: {state: VideoState}) {
|
||||
}
|
||||
|
||||
return (
|
||||
<ToolbarWrapper style={[a.flex_row, a.align_center, {paddingVertical: 5}]}>
|
||||
<ToolbarWrapper style={[a.flex_row, a.align_center]}>
|
||||
<Animated.View style={[animatedStyle]}>
|
||||
<ProgressCircle
|
||||
size={30}
|
||||
borderWidth={1}
|
||||
borderColor={t.atoms.border_contrast_low.borderColor}
|
||||
size={32}
|
||||
borderWidth={3}
|
||||
borderColor={t.palette.contrast_50}
|
||||
color={
|
||||
state.status === 'error'
|
||||
? t.palette.negative_500
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import {View} from 'react-native'
|
||||
// @ts-expect-error no type definition
|
||||
import ProgressPie from 'react-native-progress/Pie'
|
||||
import {type ImagePickerAsset} from 'expo-image-picker'
|
||||
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {ConstrainedImage} from '#/components/images/AutoSizedImage'
|
||||
import {ProgressPie} from '#/components/Progress'
|
||||
import {IS_WEB} from '#/env'
|
||||
import {ExternalEmbedRemoveBtn} from '../ExternalEmbedRemoveBtn'
|
||||
import {VideoTranscodeBackdrop} from './VideoTranscodeBackdrop'
|
||||
|
||||
Reference in New Issue
Block a user