authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-08-30 23:47:39-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-08-30 23:47:39-07:00
log9b2d04317c68233782e0630464ac86d77d0686f3
treef53842d3df8b705d7185b508febbe0d23b75eec2
parente9c4775e8738381140af4e5248507b15fc1cd491
signature Signed by PGP key B5690EEEBB952194

fix(mobile): replace Callstack glass with Expo glass (#8862)


5 files changed, 39 insertions(+), 50 deletions(-)

apps/mobile/package.json-1
...@@ -45,7 +45,6 @@...@@ -45,7 +45,6 @@
45 "typecheck": "tsc --noEmit"45 "typecheck": "tsc --noEmit"
46 },46 },
47 "dependencies": {47 "dependencies": {
48 "@callstack/liquid-glass": "^0.7.1",
49 "@clerk/expo": "catalog:",48 "@clerk/expo": "catalog:",
50 "@effect/atom-react": "catalog:",49 "@effect/atom-react": "catalog:",
51 "@expo-google-fonts/dm-sans": "^0.4.2",50 "@expo-google-fonts/dm-sans": "^0.4.2",
apps/mobile/src/features/threads/GitActionProgressOverlay.tsx+9-8
...@@ -1,5 +1,5 @@...@@ -1,5 +1,5 @@
1import * as Haptics from "expo-haptics";1import * as Haptics from "expo-haptics";
2import { isLiquidGlassSupported, LiquidGlassView } from "@callstack/liquid-glass";2import { GlassView } from "expo-glass-effect";
3import { SymbolView } from "../../components/AppSymbol";3import { SymbolView } from "../../components/AppSymbol";
4import { useCallback, useEffect, useRef } from "react";4import { useCallback, useEffect, useRef } from "react";
5import { ActivityIndicator, Pressable, StyleSheet, useColorScheme, View } from "react-native";5import { ActivityIndicator, Pressable, StyleSheet, useColorScheme, View } from "react-native";
...@@ -11,11 +11,12 @@ import { APP_BAR_HEIGHT } from "../../lib/layoutMetrics";...@@ -11,11 +11,12 @@ import { APP_BAR_HEIGHT } from "../../lib/layoutMetrics";
11import { themeColorWithAlpha } from "../../lib/mobileTheme";11import { themeColorWithAlpha } from "../../lib/mobileTheme";
12import { tryOpenExternalUrl } from "../../lib/openExternalUrl";12import { tryOpenExternalUrl } from "../../lib/openExternalUrl";
13import { useUniwindTheme } from "../../lib/useUniwindTheme";13import { useUniwindTheme } from "../../lib/useUniwindTheme";
14import { NATIVE_LIQUID_GLASS_SUPPORTED } from "../../native/native-glass";
14import type { GitActionProgress } from "../../state/use-vcs-action-state";15import type { GitActionProgress } from "../../state/use-vcs-action-state";
1516
16const OVERLAY_LAYOUT_TRANSITION = LinearTransition.duration(220);17const OVERLAY_LAYOUT_TRANSITION = LinearTransition.duration(220);
17const OVERLAY_TOP_GAP = 8;18const OVERLAY_TOP_GAP = 8;
18const AnimatedLiquidGlassView = Animated.createAnimatedComponent(LiquidGlassView);19const AnimatedGlassView = Animated.createAnimatedComponent(GlassView);
1920
20export function GitActionProgressOverlay(props: {21export function GitActionProgressOverlay(props: {
21 readonly progress: GitActionProgress;22 readonly progress: GitActionProgress;
...@@ -52,7 +53,7 @@ export function GitActionProgressOverlay(props: {...@@ -52,7 +53,7 @@ export function GitActionProgressOverlay(props: {
5253
53 return (54 return (
54 <Animated.View55 <Animated.View
55 entering={isLiquidGlassSupported ? undefined : FadeIn.duration(200)}56 entering={NATIVE_LIQUID_GLASS_SUPPORTED ? undefined : FadeIn.duration(200)}
56 exiting={FadeOut.duration(150)}57 exiting={FadeOut.duration(150)}
57 className="absolute inset-x-3 z-[100]"58 className="absolute inset-x-3 z-[100]"
58 style={{ top: insets.top + APP_BAR_HEIGHT + OVERLAY_TOP_GAP }}59 style={{ top: insets.top + APP_BAR_HEIGHT + OVERLAY_TOP_GAP }}
...@@ -102,7 +103,7 @@ function OverlayContent(props: { readonly progress: GitActionProgress }) {...@@ -102,7 +103,7 @@ function OverlayContent(props: { readonly progress: GitActionProgress }) {
102 </>103 </>
103 );104 );
104105
105 if (isLiquidGlassSupported) {106 if (NATIVE_LIQUID_GLASS_SUPPORTED) {
106 return (107 return (
107 <Animated.View108 <Animated.View
108 layout={OVERLAY_LAYOUT_TRANSITION}109 layout={OVERLAY_LAYOUT_TRANSITION}
...@@ -116,10 +117,10 @@ function OverlayContent(props: { readonly progress: GitActionProgress }) {...@@ -116,10 +117,10 @@ function OverlayContent(props: { readonly progress: GitActionProgress }) {
116 shadowRadius: 18,117 shadowRadius: 18,
117 }}118 }}
118 >119 >
119 <AnimatedLiquidGlassView120 <AnimatedGlassView
120 colorScheme={isDarkMode ? "dark" : "light"}121 colorScheme={isDarkMode ? "dark" : "light"}
121 effect="regular"122 glassEffectStyle="regular"
122 interactive123 isInteractive
123 layout={OVERLAY_LAYOUT_TRANSITION}124 layout={OVERLAY_LAYOUT_TRANSITION}
124 tintColor={glassTint}125 tintColor={glassTint}
125 style={{126 style={{
...@@ -136,7 +137,7 @@ function OverlayContent(props: { readonly progress: GitActionProgress }) {...@@ -136,7 +137,7 @@ function OverlayContent(props: { readonly progress: GitActionProgress }) {
136 >137 >
137 {content}138 {content}
138 </Animated.View>139 </Animated.View>
139 </AnimatedLiquidGlassView>140 </AnimatedGlassView>
140 </Animated.View>141 </Animated.View>
141 );142 );
142 }143 }
apps/mobile/src/features/threads/floating-working-control.tsx+28-25
...@@ -1,8 +1,4 @@...@@ -1,8 +1,4 @@
1import {1import { GlassContainer, GlassView } from "expo-glass-effect";
2 isLiquidGlassSupported,
3 LiquidGlassContainerView,
4 LiquidGlassView,
5} from "@callstack/liquid-glass";
6import { useEffect, useState } from "react";2import { useEffect, useState } from "react";
7import { Text as SystemText, View } from "react-native";3import { Text as SystemText, View } from "react-native";
8import Animated, {4import Animated, {
...@@ -18,6 +14,7 @@ import { withUniwind } from "uniwind";...@@ -18,6 +14,7 @@ import { withUniwind } from "uniwind";
1814
19import { AppText as Text } from "../../components/AppText";15import { AppText as Text } from "../../components/AppText";
20import { ControlPill } from "../../components/ControlPill";16import { ControlPill } from "../../components/ControlPill";
17import { NATIVE_LIQUID_GLASS_SUPPORTED } from "../../native/native-glass";
2118
22const CONTROL_HEIGHT = 44;19const CONTROL_HEIGHT = 44;
23const CONTROL_COMPOSER_GAP = 8;20const CONTROL_COMPOSER_GAP = 8;
...@@ -31,9 +28,15 @@ const CONTROL_TIMING = {...@@ -31,9 +28,15 @@ const CONTROL_TIMING = {
31} as const;28} as const;
32const CONTROL_SEPARATION = (16 + CONTROL_HEIGHT) / 2;29const CONTROL_SEPARATION = (16 + CONTROL_HEIGHT) / 2;
3330
34const UniwindLiquidGlassView = withUniwind(LiquidGlassView);31// Expo reapplies glass after native layout and window reattachment, when UIKit
35const UniwindLiquidGlassContainerView = withUniwind(LiquidGlassContainerView);32// can otherwise leave the label visible but lose the material behind it.
36const AnimatedLiquidGlassView = Animated.createAnimatedComponent(UniwindLiquidGlassView);33const UniwindGlassView = withUniwind(GlassView, {
34 style: { fromClassName: "className" },
35});
36const UniwindGlassContainer = withUniwind(GlassContainer, {
37 style: { fromClassName: "className" },
38});
39const AnimatedGlassView = Animated.createAnimatedComponent(UniwindGlassView);
3740
38export const FLOATING_WORKING_CONTROL_COVERAGE = CONTROL_HEIGHT + CONTROL_COMPOSER_GAP;41export const FLOATING_WORKING_CONTROL_COVERAGE = CONTROL_HEIGHT + CONTROL_COMPOSER_GAP;
3942
...@@ -68,29 +71,29 @@ export function FloatingWorkingControl(props: {...@@ -68,29 +71,29 @@ export function FloatingWorkingControl(props: {
68 pointerEvents="box-none"71 pointerEvents="box-none"
69 className="absolute left-0 right-0 z-20 items-center"72 className="absolute left-0 right-0 z-20 items-center"
70 style={{ top: -FLOATING_WORKING_CONTROL_COVERAGE }}73 style={{ top: -FLOATING_WORKING_CONTROL_COVERAGE }}
71 entering={isLiquidGlassSupported ? undefined : CONTROL_ENTERING}74 entering={NATIVE_LIQUID_GLASS_SUPPORTED ? undefined : CONTROL_ENTERING}
72 exiting={isLiquidGlassSupported ? undefined : CONTROL_EXITING}75 exiting={NATIVE_LIQUID_GLASS_SUPPORTED ? undefined : CONTROL_EXITING}
73 >76 >
74 {props.startedAt !== null && isLiquidGlassSupported ? (77 {props.startedAt !== null && NATIVE_LIQUID_GLASS_SUPPORTED ? (
75 <UniwindLiquidGlassContainerView78 <UniwindGlassContainer
76 spacing={GLASS_MERGE_SPACING}79 spacing={GLASS_MERGE_SPACING}
77 pointerEvents="box-none"80 pointerEvents="box-none"
78 className="flex-row items-center gap-4"81 className="flex-row items-center gap-4"
79 >82 >
80 <AnimatedLiquidGlassView83 <AnimatedGlassView
81 colorScheme={props.colorScheme}84 colorScheme={props.colorScheme}
82 effect="regular"85 glassEffectStyle="regular"
83 pointerEvents="none"86 pointerEvents="none"
84 className="h-11 justify-center overflow-hidden rounded-full"87 className="h-11 justify-center overflow-hidden rounded-full"
85 style={timerStyle}88 style={timerStyle}
86 >89 >
87 <WorkingDuration startedAt={props.startedAt} />90 <WorkingDuration startedAt={props.startedAt} />
88 </AnimatedLiquidGlassView>91 </AnimatedGlassView>
8992
90 <AnimatedLiquidGlassView93 <AnimatedGlassView
91 colorScheme={props.colorScheme}94 colorScheme={props.colorScheme}
92 effect="regular"95 glassEffectStyle="regular"
93 interactive96 isInteractive
94 pointerEvents={props.showScrollToEnd ? "auto" : "none"}97 pointerEvents={props.showScrollToEnd ? "auto" : "none"}
95 accessibilityElementsHidden={!props.showScrollToEnd}98 accessibilityElementsHidden={!props.showScrollToEnd}
96 importantForAccessibility={props.showScrollToEnd ? "auto" : "no-hide-descendants"}99 importantForAccessibility={props.showScrollToEnd ? "auto" : "no-hide-descendants"}
...@@ -100,8 +103,8 @@ export function FloatingWorkingControl(props: {...@@ -100,8 +103,8 @@ export function FloatingWorkingControl(props: {
100 <Animated.View style={arrowContentStyle}>103 <Animated.View style={arrowContentStyle}>
101 <ScrollToEndButton disabled={!props.showScrollToEnd} onPress={props.onScrollToEnd} />104 <ScrollToEndButton disabled={!props.showScrollToEnd} onPress={props.onScrollToEnd} />
102 </Animated.View>105 </Animated.View>
103 </AnimatedLiquidGlassView>106 </AnimatedGlassView>
104 </UniwindLiquidGlassContainerView>107 </UniwindGlassContainer>
105 ) : props.startedAt !== null ? (108 ) : props.startedAt !== null ? (
106 <View pointerEvents="box-none" className="flex-row items-center gap-4">109 <View pointerEvents="box-none" className="flex-row items-center gap-4">
107 <Animated.View110 <Animated.View
...@@ -128,15 +131,15 @@ export function FloatingWorkingControl(props: {...@@ -128,15 +131,15 @@ export function FloatingWorkingControl(props: {
128 />131 />
129 </Animated.View>132 </Animated.View>
130 </View>133 </View>
131 ) : isLiquidGlassSupported ? (134 ) : NATIVE_LIQUID_GLASS_SUPPORTED ? (
132 <UniwindLiquidGlassView135 <UniwindGlassView
133 colorScheme={props.colorScheme}136 colorScheme={props.colorScheme}
134 effect="regular"137 glassEffectStyle="regular"
135 interactive138 isInteractive
136 className="h-11 w-11 items-center justify-center overflow-hidden rounded-full"139 className="h-11 w-11 items-center justify-center overflow-hidden rounded-full"
137 >140 >
138 <ScrollToEndButton onPress={props.onScrollToEnd} />141 <ScrollToEndButton onPress={props.onScrollToEnd} />
139 </UniwindLiquidGlassView>142 </UniwindGlassView>
140 ) : (143 ) : (
141 <ControlPill144 <ControlPill
142 accessibilityLabel="Scroll to end"145 accessibilityLabel="Scroll to end"
docs/user/mobile-appearance.md+2-2
...@@ -4,8 +4,8 @@ T3 Code Mobile includes the T3 Code, T3 Chat, Grove, Ocean, Ember, and Iris them...@@ -4,8 +4,8 @@ T3 Code Mobile includes the T3 Code, T3 Chat, Grove, Ocean, Ember, and Iris them
4light and dark colors that apply throughout the app, including code reviews, file previews, the4light and dark colors that apply throughout the app, including code reviews, file previews, the
5terminal, native headers, and sheets.5terminal, native headers, and sheets.
66
7On supported iOS versions, the new-task and thread composers use the system glass material.7On supported iOS versions, the new-task and thread composers, working timer, and scroll-to-end
8Other platforms use a themed background.8button use the system glass material. Other platforms use a themed background.
99
10To change themes:10To change themes:
1111
pnpm-lock.yaml-14
...@@ -211,9 +211,6 @@ importers:...@@ -211,9 +211,6 @@ importers:
211211
212 apps/mobile:212 apps/mobile:
213 dependencies:213 dependencies:
214 '@callstack/liquid-glass':
215 specifier: ^0.7.1
216 version: 0.7.1(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)
217 '@clerk/expo':214 '@clerk/expo':
218 specifier: 4.2.0215 specifier: 4.2.0
219 version: 4.2.0(patch_hash=72e426f44fc1cde16fc2cbba3d1e96cdca7c6d957faa73d0fe6b43948608a6c1)(1fcd0592788ddcf326eeeb90d875ed47)216 version: 4.2.0(patch_hash=72e426f44fc1cde16fc2cbba3d1e96cdca7c6d957faa73d0fe6b43948608a6c1)(1fcd0592788ddcf326eeeb90d875ed47)
...@@ -1700,12 +1697,6 @@ packages:...@@ -1700,12 +1697,6 @@ packages:
1700 cpu: [x64]1697 cpu: [x64]
1701 os: [win32]1698 os: [win32]
17021699
1703 '@callstack/liquid-glass@0.7.1':
1704 resolution: {integrity: sha512-N2rzs8g3kneI5G/98AZdVtjc6OUFBBFwPxVGVMoXUEI7QiioB4+aWwbImg5h8Z3Vb/T+fgLYWmmNhi27wHwE4g==}
1705 peerDependencies:
1706 react: '*'
1707 react-native: '*'
1708
1709 '@capsizecss/unpack@4.0.1':1700 '@capsizecss/unpack@4.0.1':
1710 resolution: {integrity: sha512-CuNiSqg7+e1cO/GjffyMOm5Tt2jUF9CWHHnvQ/UkqvtkGfHdgwEC0wpmq7fkN3gxwpRnrAN0WzO3vREKmNolMQ==}1701 resolution: {integrity: sha512-CuNiSqg7+e1cO/GjffyMOm5Tt2jUF9CWHHnvQ/UkqvtkGfHdgwEC0wpmq7fkN3gxwpRnrAN0WzO3vREKmNolMQ==}
1711 engines: {node: '>=18'}1702 engines: {node: '>=18'}
...@@ -11398,11 +11389,6 @@ snapshots:...@@ -11398,11 +11389,6 @@ snapshots:
11398 '@bruits/satteri-win32-x64-msvc@0.9.3':11389 '@bruits/satteri-win32-x64-msvc@0.9.3':
11399 optional: true11390 optional: true
1140011391
11401 '@callstack/liquid-glass@0.7.1(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3)':
11402 dependencies:
11403 react: 19.2.3
11404 react-native: 0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)
11405
11406 '@capsizecss/unpack@4.0.1':11392 '@capsizecss/unpack@4.0.1':
11407 dependencies:11393 dependencies:
11408 fontkitten: 1.0.311394 fontkitten: 1.0.3