diff --git a/package.json b/package.json
index 2fda0beb2b..068c130ab9 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ab086e0b70..03dd909f04 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -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
diff --git a/src/components/Progress/Circle.tsx b/src/components/Progress/Circle.tsx
new file mode 100644
index 0000000000..3369250101
--- /dev/null
+++ b/src/components/Progress/Circle.tsx
@@ -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 (
+
+ )
+}
diff --git a/src/components/Progress/Pie.tsx b/src/components/Progress/Pie.tsx
new file mode 100644
index 0000000000..89eda7078e
--- /dev/null
+++ b/src/components/Progress/Pie.tsx
@@ -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 (
+
+ )
+}
diff --git a/src/components/Progress/index.tsx b/src/components/Progress/index.tsx
new file mode 100644
index 0000000000..09e7143aa5
--- /dev/null
+++ b/src/components/Progress/index.tsx
@@ -0,0 +1,2 @@
+export {ProgressCircle} from './Circle'
+export {ProgressPie} from './Pie'
diff --git a/src/components/Progress/shared.ts b/src/components/Progress/shared.ts
new file mode 100644
index 0000000000..800cce46e3
--- /dev/null
+++ b/src/components/Progress/shared.ts
@@ -0,0 +1,6 @@
+export function clamp01(n: number) {
+ 'worklet'
+ if (n < 0) return 0
+ if (n > 1) return 1
+ return n
+}
diff --git a/src/components/ProgressGuide/Task.tsx b/src/components/ProgressGuide/Task.tsx
index 997c777af6..88b035e60a 100644
--- a/src/components/ProgressGuide/Task.tsx
+++ b/src/components/ProgressGuide/Task.tsx
@@ -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 ? (
) : (
-
-
+
)
@@ -2413,7 +2409,7 @@ function ToolbarWrapper({
if (IS_WEB) return children
return (
{children}
@@ -2488,12 +2484,12 @@ function VideoUploadToolbar({state}: {state: VideoState}) {
}
return (
-
+