authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-07-24 00:39:09+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-07-24 00:39:09+02:00
logce467da9cfae3d7952ce13a9e2450e2908e9f224
tree1d0c05f030ee27ce4f71426cd0aa3b1a90ab0cb6
parent6ef7aa8399640fd2bf10fcb126c0db93fee47ff0
signature Signed by PGP key B5690EEEBB952194

Polish iOS git progress overlay with glass effects (#4387)


1 files changed, 62 insertions(+), 14 deletions(-)

apps/mobile/src/features/threads/GitActionProgressOverlay.tsx+62-14
...@@ -1,8 +1,9 @@...@@ -1,8 +1,9 @@
1import * as Haptics from "expo-haptics";1import * as Haptics from "expo-haptics";
2import { isLiquidGlassSupported, LiquidGlassView } from "@callstack/liquid-glass";
2import { SymbolView } from "../../components/AppSymbol";3import { SymbolView } from "../../components/AppSymbol";
3import { useCallback, useEffect, useRef } from "react";4import { useCallback, useEffect, useRef } from "react";
4import { ActivityIndicator, Pressable, View } from "react-native";5import { ActivityIndicator, Pressable, StyleSheet, useColorScheme, View } from "react-native";
5import Animated, { FadeIn, FadeOut } from "react-native-reanimated";6import Animated, { FadeIn, FadeOut, LinearTransition } from "react-native-reanimated";
6import { useSafeAreaInsets } from "react-native-safe-area-context";7import { useSafeAreaInsets } from "react-native-safe-area-context";
78
8import { AppText as Text } from "../../components/AppText";9import { AppText as Text } from "../../components/AppText";
...@@ -10,6 +11,9 @@ import { tryOpenExternalUrl } from "../../lib/openExternalUrl";...@@ -10,6 +11,9 @@ import { tryOpenExternalUrl } from "../../lib/openExternalUrl";
10import { useThemeColor } from "../../lib/useThemeColor";11import { useThemeColor } from "../../lib/useThemeColor";
11import type { GitActionProgress } from "../../state/use-vcs-action-state";12import type { GitActionProgress } from "../../state/use-vcs-action-state";
1213
14const OVERLAY_LAYOUT_TRANSITION = LinearTransition.duration(220);
15const AnimatedLiquidGlassView = Animated.createAnimatedComponent(LiquidGlassView);
16
13export function GitActionProgressOverlay(props: {17export function GitActionProgressOverlay(props: {
14 readonly progress: GitActionProgress;18 readonly progress: GitActionProgress;
15 readonly onDismiss: () => void;19 readonly onDismiss: () => void;
...@@ -45,7 +49,7 @@ export function GitActionProgressOverlay(props: {...@@ -45,7 +49,7 @@ export function GitActionProgressOverlay(props: {
4549
46 return (50 return (
47 <Animated.View51 <Animated.View
48 entering={FadeIn.duration(200)}52 entering={isLiquidGlassSupported ? undefined : FadeIn.duration(200)}
49 exiting={FadeOut.duration(150)}53 exiting={FadeOut.duration(150)}
50 className="absolute inset-x-3 z-[100]"54 className="absolute inset-x-3 z-[100]"
51 style={{ top: insets.top + 48 }}55 style={{ top: insets.top + 48 }}
...@@ -61,16 +65,11 @@ export function GitActionProgressOverlay(props: {...@@ -61,16 +65,11 @@ export function GitActionProgressOverlay(props: {
61function OverlayContent(props: { readonly progress: GitActionProgress }) {65function OverlayContent(props: { readonly progress: GitActionProgress }) {
62 const { progress } = props;66 const { progress } = props;
63 const iconColor = useThemeColor("--color-icon");67 const iconColor = useThemeColor("--color-icon");
6468 const glassBorder = useThemeColor("--color-header-border");
65 const bgClass =69 const glassTint = useThemeColor("--color-glass-tint");
66 progress.phase === "error"70 const isDarkMode = useColorScheme() === "dark";
67 ? "bg-red-50 dark:bg-red-950/80 border-red-200 dark:border-red-800"71 const content = (
68 : "bg-card border-border";72 <>
69
70 return (
71 <View
72 className={`flex-row items-center gap-2.5 rounded-2xl border px-3.5 py-3 shadow-lg shadow-black/10 ${bgClass}`}
73 >
74 <OverlayIcon phase={progress.phase} iconColor={iconColor} />73 <OverlayIcon phase={progress.phase} iconColor={iconColor} />
7574
76 <View className="flex-1 gap-0.5">75 <View className="flex-1 gap-0.5">
...@@ -89,7 +88,56 @@ function OverlayContent(props: { readonly progress: GitActionProgress }) {...@@ -89,7 +88,56 @@ function OverlayContent(props: { readonly progress: GitActionProgress }) {
89 {progress.prUrl ? (88 {progress.prUrl ? (
90 <SymbolView name="arrow.up.right" size={13} tintColor={iconColor} type="monochrome" />89 <SymbolView name="arrow.up.right" size={13} tintColor={iconColor} type="monochrome" />
91 ) : null}90 ) : null}
92 </View>91 </>
92 );
93
94 if (isLiquidGlassSupported) {
95 return (
96 <Animated.View
97 layout={OVERLAY_LAYOUT_TRANSITION}
98 style={{
99 backgroundColor: glassTint,
100 borderColor: glassBorder,
101 borderCurve: "continuous",
102 borderRadius: 26,
103 borderWidth: StyleSheet.hairlineWidth,
104 overflow: "hidden",
105 }}
106 >
107 <AnimatedLiquidGlassView
108 colorScheme={isDarkMode ? "dark" : "light"}
109 effect="regular"
110 interactive
111 layout={OVERLAY_LAYOUT_TRANSITION}
112 style={{
113 borderCurve: "continuous",
114 borderRadius: 26,
115 overflow: "hidden",
116 }}
117 >
118 <Animated.View
119 entering={FadeIn.delay(60).duration(140)}
120 className="flex-row items-center gap-2.5 px-3.5 py-3"
121 >
122 {content}
123 </Animated.View>
124 </AnimatedLiquidGlassView>
125 </Animated.View>
126 );
127 }
128
129 const bgClass =
130 progress.phase === "error"
131 ? "bg-red-50 dark:bg-red-950/80 border-red-200 dark:border-red-800"
132 : "bg-card border-border";
133
134 return (
135 <Animated.View
136 layout={OVERLAY_LAYOUT_TRANSITION}
137 className={`flex-row items-center gap-2.5 rounded-[26px] border border-continuous px-3.5 py-3 shadow-lg shadow-black/10 ${bgClass}`}
138 >
139 {content}
140 </Animated.View>
93 );141 );
94}142}
95143