Skip to content

Commit 8863990

Browse files
authored
fix(android): Use MD3 loading spinners (#14282)
1 parent 2e19207 commit 8863990

32 files changed

Lines changed: 159 additions & 113 deletions
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { LoadingIndicator, Host } from "@expo/ui/jetpack-compose";
2+
import { size as composeSize } from "@expo/ui/jetpack-compose/modifiers";
3+
import { StyleSheet, View, type ActivityIndicatorProps } from "react-native";
4+
import { useResolveClassNames, withUniwind } from "uniwind";
5+
6+
function AndroidActivityIndicator({
7+
animating = true,
8+
color,
9+
colorClassName: _colorClassName,
10+
className: _className,
11+
hidesWhenStopped: _hidesWhenStopped,
12+
size = "small",
13+
style,
14+
...viewProps
15+
}: ActivityIndicatorProps) {
16+
const { color: primaryColor } = useResolveClassNames("text-primary");
17+
const diameter = typeof size === "number" ? size : size === "large" ? 48 : 20;
18+
19+
return (
20+
<View
21+
accessible={animating}
22+
accessibilityRole="progressbar"
23+
accessibilityState={{ busy: animating }}
24+
{...viewProps}
25+
style={[styles.container, style]}
26+
>
27+
<View
28+
pointerEvents="none"
29+
importantForAccessibility="no-hide-descendants"
30+
style={{ width: diameter, height: diameter }}
31+
>
32+
{animating ? (
33+
<Host ignoreSafeAreaKeyboardInsets style={{ flex: 1 }}>
34+
<LoadingIndicator
35+
color={color ?? primaryColor}
36+
modifiers={[composeSize(diameter, diameter)]}
37+
/>
38+
</Host>
39+
) : null}
40+
</View>
41+
</View>
42+
);
43+
}
44+
45+
export const AppActivityIndicator = withUniwind(AndroidActivityIndicator);
46+
47+
const styles = StyleSheet.create({
48+
container: { alignItems: "center", justifyContent: "center" },
49+
});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { ActivityIndicator } from "react-native";
2+
3+
export const AppActivityIndicator = ActivityIndicator;

‎apps/mobile/src/components/LoadingScreen.tsx‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { ActivityIndicator, StatusBar, View } from "react-native";
1+
import { StatusBar, View } from "react-native";
22
import { useSafeAreaInsets } from "react-native-safe-area-context";
3+
import { AppActivityIndicator } from "./AppActivityIndicator";
34
import { useAppearancePreferences } from "../features/settings/appearance/AppearancePreferencesProvider";
45

56
import { AppText as Text } from "./AppText";
@@ -26,7 +27,7 @@ export function LoadingScreen(props: {
2627
{messagePlacement === "above-spinner" ? (
2728
<Text className="font-t3-bold text-lg text-foreground">{props.message}</Text>
2829
) : null}
29-
<ActivityIndicator size="large" />
30+
<AppActivityIndicator size="large" />
3031
{messagePlacement === "below-spinner" ? (
3132
<Text className="font-t3-bold text-lg text-foreground">{props.message}</Text>
3233
) : null}

‎apps/mobile/src/components/MaterialButton.android.tsx‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {
22
Box,
33
Button,
4-
CircularProgressIndicator,
4+
LoadingIndicator,
55
FilledTonalButton,
66
Host,
77
Row,
@@ -78,9 +78,8 @@ export function MaterialButton(props: MaterialButtonProps) {
7878
<Row verticalAlignment="center">
7979
{props.loading ? (
8080
<>
81-
<CircularProgressIndicator
81+
<LoadingIndicator
8282
modifiers={[size(mediumIconSize, mediumIconSize)]}
83-
strokeWidth={2}
8483
color={colors["--color-foreground-muted"]}
8584
/>
8685
<Box modifiers={[size(8 * scale, 1)]} />

‎apps/mobile/src/components/MediaVideoPlayer.tsx‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ import { useIsFocused } from "@react-navigation/native";
22
import { useEvent } from "expo";
33
import { useVideoPlayer, VideoView } from "expo-video";
44
import { useEffect, useEffectEvent, useRef, useState } from "react";
5-
import { ActivityIndicator, AppState, Pressable, View } from "react-native";
5+
import { AppState, Pressable, View } from "react-native";
66

7+
import { AppActivityIndicator } from "./AppActivityIndicator";
78
import { AppText } from "./AppText";
89
import { SymbolView } from "./AppSymbol";
910
import { VideoThumbnailImage } from "./VideoThumbnailImage";
@@ -96,7 +97,7 @@ function LoadedMediaVideo(props: {
9697
</View>
9798
) : loadState === "pending" || status === "loading" ? (
9899
<View pointerEvents="none" className="absolute inset-0 items-center justify-center">
99-
<ActivityIndicator color="#ffffff" accessibilityLabel="Loading video" />
100+
<AppActivityIndicator color="#ffffff" accessibilityLabel="Loading video" />
100101
</View>
101102
) : null}
102103
</View>
@@ -160,7 +161,7 @@ function MediaVideoPlayerContent(props: MediaVideoPlayerProps) {
160161
{props.unavailable ? (
161162
<AppText className="text-sm text-white/80">Video unavailable</AppText>
162163
) : props.uri === null ? (
163-
<ActivityIndicator color="#ffffff" accessibilityLabel="Loading video" />
164+
<AppActivityIndicator color="#ffffff" accessibilityLabel="Loading video" />
164165
) : (
165166
<View className="size-12 items-center justify-center rounded-full bg-black/60">
166167
<SymbolView name="play" size={28} tintColor="#ffffff" type="monochrome" />

‎apps/mobile/src/components/ProjectCloneBanner.tsx‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ import {
33
projectCloneProgressSummary,
44
type ProjectCloneSnapshot,
55
} from "@t3tools/contracts";
6-
import { ActivityIndicator, Pressable, View } from "react-native";
6+
import { Pressable, View } from "react-native";
77

8+
import { AppActivityIndicator } from "./AppActivityIndicator";
89
import { cn } from "../lib/cn";
910
import { AppText as Text } from "./AppText";
1011

@@ -24,7 +25,7 @@ export function ProjectCloneBanner(props: {
2425
if (clone.phase === "running") {
2526
return (
2627
<View className="flex-row items-center gap-3 rounded-2xl border border-border bg-card px-3.5 py-3">
27-
<ActivityIndicator size="small" />
28+
<AppActivityIndicator size="small" />
2829
<View className="min-w-0 flex-1">
2930
<Text className="font-t3-medium text-sm" numberOfLines={1}>
3031
Cloning {name}

‎apps/mobile/src/components/VideoPreviewModal.tsx‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { useIsFocused } from "@react-navigation/native";
22
import { videoMimeType } from "@t3tools/shared/video";
33
import { useEffect, useRef, useState } from "react";
4-
import { ActivityIndicator, Keyboard, Modal, Pressable, View } from "react-native";
4+
import { Keyboard, Modal, Pressable, View } from "react-native";
55
import { useSafeAreaInsets } from "react-native-safe-area-context";
66

7+
import { AppActivityIndicator } from "./AppActivityIndicator";
78
import { loadLocalAttachmentPreview } from "../lib/localAttachmentPreview";
89
import { useMediaActions } from "../state/mediaActions";
910
import { type MediaActionsSource } from "../lib/mediaActionsSource";
@@ -165,7 +166,7 @@ function OpenVideoPreviewModal(props: {
165166
<MediaSourceCaption source={mediaActions.title} />
166167
{playback.uri === null && !playback.unavailable ? (
167168
<View className="flex-1 items-center justify-center gap-3 px-6">
168-
<ActivityIndicator color="#ffffff" />
169+
<AppActivityIndicator color="#ffffff" />
169170
<AppText className="text-sm text-white/80">Loading video...</AppText>
170171
</View>
171172
) : (

‎apps/mobile/src/features/archive/ArchivedThreadsScreen.tsx‎

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,11 @@ import {
1010
resolveEnvironmentMachineKind,
1111
} from "@t3tools/contracts";
1212
import { useNavigation } from "@react-navigation/native";
13+
import { AppActivityIndicator } from "../../components/AppActivityIndicator";
1314
import { ScreenHeader } from "../../components/ScreenHeader";
1415
import { SymbolView } from "../../components/AppSymbol";
1516
import { useCallback, useMemo, useRef, type ComponentProps } from "react";
16-
import {
17-
ActivityIndicator,
18-
Platform,
19-
Pressable,
20-
RefreshControl,
21-
useWindowDimensions,
22-
View,
23-
} from "react-native";
17+
import { Platform, Pressable, RefreshControl, useWindowDimensions, View } from "react-native";
2418
import { GestureDetector, useNativeGesture } from "react-native-gesture-handler";
2519
import type { SwipeableMethods } from "react-native-gesture-handler/ReanimatedSwipeable";
2620

@@ -399,7 +393,7 @@ export function ArchivedThreadsScreen(props: {
399393
if (isInitialLoad) {
400394
return (
401395
<View className="items-center py-16">
402-
<ActivityIndicator colorClassName="accent-icon" />
396+
<AppActivityIndicator colorClassName="accent-icon" />
403397
<Text className="mt-3 text-sm text-foreground-muted">Loading archive...</Text>
404398
</View>
405399
);

‎apps/mobile/src/features/cloud/T3ConnectProfilePage.tsx‎

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,9 @@ import type { MenuAction } from "@react-native-menu/menu";
77
import type { EnvironmentId } from "@t3tools/contracts";
88
import type { RelayClientEnvironmentRecord } from "@t3tools/contracts/relay";
99
import { type ReactNode, useRef, useState } from "react";
10-
import {
11-
ActivityIndicator,
12-
Alert,
13-
Pressable,
14-
RefreshControl,
15-
ScrollView,
16-
Text,
17-
View,
18-
} from "react-native";
10+
import { Alert, Pressable, RefreshControl, ScrollView, Text, View } from "react-native";
1911

12+
import { AppActivityIndicator } from "../../components/AppActivityIndicator";
2013
import { SymbolView } from "../../components/AppSymbol";
2114
import { showConfirmDialog } from "../../components/ConfirmDialogHost";
2215
import { ControlPillMenu } from "../../components/ControlPill";
@@ -167,7 +160,7 @@ export function T3ConnectProfilePage() {
167160
</>
168161
) : isInitialLoad ? (
169162
<View className="flex-row items-center gap-3 px-6 py-4">
170-
<ActivityIndicator colorClassName={"accent-clerk-foreground-muted"} size="small" />
163+
<AppActivityIndicator colorClassName={"accent-clerk-foreground-muted"} size="small" />
171164
<Text className="text-base text-clerk-foreground-muted">Loading environments</Text>
172165
</View>
173166
) : environments.length > 0 ? (
@@ -178,7 +171,10 @@ export function T3ConnectProfilePage() {
178171
subtitle={`${linkedAtLabel(environment.linkedAt)} · ${endpointLabel(environment)}`}
179172
accessory={
180173
deregisteringEnvironmentId === environment.environmentId ? (
181-
<ActivityIndicator colorClassName={"accent-clerk-foreground-muted"} size="small" />
174+
<AppActivityIndicator
175+
colorClassName={"accent-clerk-foreground-muted"}
176+
size="small"
177+
/>
182178
) : (
183179
<ControlPillMenu
184180
actions={ENVIRONMENT_MENU_ACTIONS}

‎apps/mobile/src/features/connection/CloudEnvironmentRows.tsx‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { AppActivityIndicator } from "../../components/AppActivityIndicator";
12
import { ConnectionTraceId } from "./ConnectionTraceId";
23
import { useAuth } from "@clerk/expo";
34
import { SymbolView } from "../../components/AppSymbol";
@@ -13,7 +14,7 @@ import {
1314
} from "@t3tools/contracts";
1415
import { useAtomValue } from "@effect/atom-react";
1516
import { useCallback, useState } from "react";
16-
import { ActivityIndicator, Pressable, type TextLayoutEvent, View } from "react-native";
17+
import { Pressable, type TextLayoutEvent, View } from "react-native";
1718

1819
import { AppText as Text } from "../../components/AppText";
1920
import { EnvironmentMachineSymbol } from "../../components/EnvironmentMachineSymbol";
@@ -151,7 +152,7 @@ function CloudEnvironmentRowsContent(
151152
</View>
152153
) : controller.relayDiscovery.isRefreshing ? (
153154
<View collapsable={false} className="items-center gap-3 rounded-[24px] bg-grouped-card p-6">
154-
<ActivityIndicator colorClassName={"accent-icon"} />
155+
<AppActivityIndicator colorClassName={"accent-icon"} />
155156
<Text className="text-center text-sm leading-normal text-foreground-muted">
156157
Loading linked cloud environments.
157158
</Text>

0 commit comments

Comments
 (0)