-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Replies: 1 comment · 5 replies
-
Hey @oystr29! Can you tell me which version of
Can you clarify what happened here? As far as I can understand, it worked before, then you changed something while working on the web implementation and it broke when you opened the mobile app again. Is that correct? Do you know what was changed before you revisited the mobile app? |
Beta Was this translation helpful? Give feedback.
All reactions
-
This is my package.json {
"name": "bazzar-expo",
"main": "expo-router/entry",
"version": "1.0.0",
"scripts": {
"dev": "expo start",
"reset-project": "node ./scripts/reset-project.js",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web",
"build:web": "npx expo export -p web",
"test": "jest --watchAll",
"lint": "expo lint"
},
"jest": {
"preset": "jest-expo"
},
"dependencies": {
"@expo/react-native-action-sheet": "^4.1.1",
"@expo/vector-icons": "^14.1.0",
"@gorhom/bottom-sheet": "^5.1.2",
"@hookform/resolvers": "^4.1.3",
"@legendapp/state": "3.0.0-beta.30",
"@radix-ui/react-checkbox": "^1.3.2",
"@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-popover": "^1.1.14",
"@radix-ui/react-radio-group": "^1.3.7",
"@radix-ui/react-select": "^2.2.5",
"@react-native-community/datetimepicker": "8.2.0",
"@react-native-material/core": "^1.3.7",
"@react-navigation/bottom-tabs": "^7.3.10",
"@react-navigation/material-top-tabs": "^7.2.13",
"@react-navigation/native": "^7.0.14",
"@rn-primitives/checkbox": "^1.1.0",
"@rn-primitives/label": "^1.1.0",
"@rn-primitives/popover": "^1.1.0",
"@rn-primitives/portal": "^1.1.0",
"@rn-primitives/radio-group": "^1.1.0",
"@rn-primitives/select": "^1.1.0",
"@rn-primitives/slot": "^1.1.0",
"@rn-primitives/switch": "^1.1.0",
"@rn-primitives/types": "^1.1.0",
"@shopify/flash-list": "1.7.3",
"@tanstack/react-query": "^5.69.0",
"axios": "^1.8.4",
"chroma-js": "^3.1.2",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"dayjs": "^1.11.13",
"expo": "~53.0.10",
"expo-applicatisn": "~6.0.2",
"expo-blur": "~14.0.3",
"expo-camera": "~16.0.18",
"expo-constants": "~17.0.8",
"expo-dev-client": "~5.0.14",
"expo-device": "~7.0.3",
"expo-file-system": "~18.0.12",
"expo-font": "~13.0.4",
"expo-haptics": "~14.0.1",
"expo-image": "~2.0.7",
"expo-linking": "~7.0.5",
"expo-media-library": "~17.0.6",
"expo-notifications": "~0.29.14",
"expo-router": "~4.0.21",
"expo-splash-screen": "~0.29.22",
"expo-status-bar": "~2.0.1",
"expo-symbols": "~0.2.2",
"expo-system-ui": "~4.0.8",
"expo-updates": "~0.27.4",
"expo-web-browser": "~14.0.2",
"immer": "^10.1.1",
"jotai": "^2.12.4",
"jotai-immer": "^0.4.1",
"libphonenumber-js": "^1.12.8",
"lucide-react-native": "^0.503.0",
"nativewind": "^4.1.23",
"react": "18.3.1",
"react-day-picker": "^9.7.0",
"react-dom": "18.3.1",
"react-error-boundary": "^6.0.0",
"react-hook-form": "^7.54.2",
"react-native": "0.76.9",
"react-native-format-currency": "^0.0.5",
"react-native-gesture-handler": "~2.20.2",
"react-native-keyboard-controller": "^1.16.8",
"react-native-mmkv": "^3.2.0",
"react-native-modal-datetime-picker": "^18.0.0",
"react-native-qrcode-svg": "^6.3.15",
"react-native-reanimated": "~3.16.1",
"react-native-reanimated-carousel": "^4.0.2",
"react-native-safe-area-context": "4.12.0",
"react-native-screens": "~4.4.0",
"react-native-svg": "15.8.0",
"react-native-tab-view": "^4.1.0",
"react-native-vector-icons": "^10.2.0",
"react-native-web": "~0.19.13",
"react-native-webview": "13.12.5",
"react-native-window-size": "^0.4.1",
"react-query-kit": "^3.3.1",
"sonner": "^2.0.5",
"sonner-native": "^0.17.0",
"tailwind-merge": "^3.0.2",
"tailwindcss": "^3.4.17",
"tailwindcss-animate": "^1.0.7",
"toastify-react-native": "^7.2.0",
"use-debounce": "^10.0.4",
"zod": "^3.24.2"
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@ianvs/prettier-plugin-sort-imports": "^4.4.1",
"@tanstack/eslint-plugin-query": "^5.68.0",
"@types/chroma-js": "^3.1.1",
"@types/jest": "^29.5.12",
"@types/react": "~18.3.12",
"@types/react-test-renderer": "^18.3.0",
"eslint": "^8.57.0",
"eslint-config-expo": "^8.0.1",
"eslint-plugin-expo": "^0.1.0",
"eslint-plugin-react-hooks": "^5.2.0",
"jest": "^29.2.1",
"jest-expo": "~52.0.6",
"prettier": "^3.5.3",
"prettier-plugin-tailwindcss": "^0.6.11",
"react-test-renderer": "18.3.1",
"typescript": "^5.3.3"
},
"private": true
}
Yes, you are correct
Mostly the thing that i changed was the styling, so i just made a condition if its a web then do this style and if mobile do this style This is the material tab _layout that used reanimated import type {
MaterialTopTabBarProps,
MaterialTopTabNavigationEventMap,
MaterialTopTabNavigationOptions,
} from "@react-navigation/material-top-tabs";
import { createMaterialTopTabNavigator } from "@react-navigation/material-top-tabs";
import {
type ParamListBase,
type TabNavigationState,
} from "@react-navigation/native";
import { usePathname, withLayoutContext } from "expo-router";
import { useEffect, useMemo, useState } from "react";
import {
LayoutChangeEvent,
Platform,
Pressable,
Text,
View,
} from "react-native";
import Animated, {
useAnimatedStyle,
useSharedValue,
withSpring,
} from "react-native-reanimated";
import { color } from "~/lib/color";
import { cn } from "~/lib/utils";
const { Navigator } = createMaterialTopTabNavigator();
const MaterialTopTabs = withLayoutContext<
MaterialTopTabNavigationOptions,
typeof Navigator,
TabNavigationState<ParamListBase>,
MaterialTopTabNavigationEventMap
>(Navigator);
const title: Record<string, string> = {
index: "Kupon Ku",
shop: "Beli Kupon",
};
const Tabbar = ({ state, descriptors, navigation }: MaterialTopTabBarProps) => {
const pathname = usePathname();
const [dimensions, setDimensions] = useState({ height: 20, width: 100 });
const btnWidth = dimensions.width / state.routes.length;
const tabPosX = useSharedValue(0);
const routes = useMemo(() => {
const v: Record<string, number> = {};
state.routes.forEach((r, i) => {
const { name } = r;
v[name === "index" ? "/coupon" : `/coupon/${name}`] = i;
});
return v;
}, [state.routes]);
const onTabbarLayout = (e: LayoutChangeEvent) => {
setDimensions({
width: e.nativeEvent.layout.width,
height: e.nativeEvent.layout.height,
});
};
const animatedStyle = useAnimatedStyle(() => {
if (Platform.OS === "web")
return { transform: `translateX(${tabPosX.value}px)` };
return { transform: [{ translateX: withSpring(tabPosX.value) }] };
});
useEffect(() => {
if (routes[pathname] !== undefined) {
tabPosX.value = withSpring(btnWidth * routes[pathname], {
duration: 1500,
});
}
}, [pathname, tabPosX, btnWidth, routes]);
return (
<>
<View
onLayout={onTabbarLayout}
className="bg-white flex flex-row justify-between items-center gap-4 px-4 relative mb-4"
>
<Animated.View
style={[
{
position: "absolute",
backgroundColor: `red`,
left: -3,
borderRadius: 40,
marginHorizontal: 12,
height: dimensions.height,
width: btnWidth - 20,
},
animatedStyle,
]}
/>
{state.routes.map((route, index) => {
const isFocused = state.index === index;
const onPress = () => {
tabPosX.value = withSpring(btnWidth * index, { duration: 1500 });
const event = navigation.emit({
type: "tabPress",
target: route.key,
canPreventDefault: true,
});
if (!isFocused && !event.defaultPrevented) {
navigation.navigate(route.name, route.params);
}
};
return (
<Pressable
onPress={onPress}
key={route.name}
className="active:bg-gray-200"
style={{
borderRadius: 99999,
backgroundColor: "transparent",
flex: 1,
paddingVertical: 4,
alignItems: "center",
justifyContent: "center",
}}
>
<Text className={cn("text-gray-500", isFocused && "")}>
{title[route.name]}
</Text>
</Pressable>
);
})}
</View>
</>
);
};
export default function MaterialTopTabsLayout() {
return (
<MaterialTopTabs
tabBar={(props) => <Tabbar {...props} />}
initialRouteName="index"
screenOptions={{
tabBarActiveTintColor: `hsl(${color().primary})`,
tabBarInactiveTintColor: "grey",
tabBarLabelStyle: {
fontSize: 14,
textTransform: "capitalize",
fontWeight: "bold",
},
tabBarIndicatorStyle: {
backgroundColor: `hsl(${color().primary})`,
},
tabBarScrollEnabled: true,
tabBarItemStyle: { flex: 1 },
}}
>
<MaterialTopTabs.Screen name="index" options={{ title: "Kupon Ku" }} />
<MaterialTopTabs.Screen name="shop" options={{ title: "Beli Kupon" }} />
</MaterialTopTabs>
);
} Also, this is my root _layout.tsx import "../global.css";
import "react-day-picker/src/style.css";
import { ActionSheetProvider } from "@expo/react-native-action-sheet";
import { DefaultTheme, Theme, ThemeProvider } from "@react-navigation/native";
import { Stack } from "expo-router";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import { StatusBar } from "expo-status-bar";
import { useEffect, useState } from "react";
import { maxWidth, NAV_THEME } from "~/lib/contants";
import { KeyboardProvider } from "react-native-keyboard-controller";
import { SafeAreaProvider } from "react-native-safe-area-context";
import { PortalHost } from "@rn-primitives/portal";
import { BottomSheetModalProvider } from "@gorhom/bottom-sheet";
import { Image, Platform, View } from "react-native";
import { Provider } from "~/context/auth";
import * as Notifications from "expo-notifications";
import { storage } from "~/lib/storage";
import { Session } from "~/kit/auth/schema";
import { setAxiosAuth } from "~/lib/axios";
import { useNotifStore } from "~/lib/hooks/useNotifStore";
import * as SplashScreen from "expo-splash-screen";
import { Toaster } from "~/lib/sonner";
// Prevent the splash screen from auto-hiding before asset loading is complete.
SplashScreen.preventAutoHideAsync();
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: true,
shouldSetBadge: true,
}),
});
const LIGHT_THEME: Theme = {
...DefaultTheme,
colors: NAV_THEME.light,
};
const queryClient = new QueryClient({
defaultOptions: {
queries: { refetchInterval: 60000, refetchOnMount: false },
},
});
export default function RootLayout() {
const [isReady, setIsReady] = useState(false);
const [loadedUser, setLoadedUser] = useState<Session | null>(null);
const setNotifData = useNotifStore((s) => s.setData);
const registerForPushNotificationsAsync = useNotifStore(
(s) => s.registerForPushNotificationsAsync,
);
const notificationListener = useNotifStore(
(s) => s.data.notificationListener,
);
const responseListener = useNotifStore((s) => s.data.responseListener);
const getUserFromStorage = async () => {
const user = storage.getString("user");
if (user) {
const u = JSON.parse(user) as Session;
setAxiosAuth(u.token);
setLoadedUser(u);
}
setIsReady(true);
await SplashScreen.hideAsync();
};
useEffect(() => {
getUserFromStorage();
}, []);
useEffect(() => {
registerForPushNotificationsAsync()
.then((token) => setNotifData({ expoPushToken: token ?? undefined }))
.catch((_) => setNotifData({ expoPushToken: undefined }));
setNotifData({
notificationListener: Notifications.addNotificationReceivedListener(
(notification) => {
setNotifData({ notification });
},
),
});
setNotifData({
responseListener: Notifications.addNotificationResponseReceivedListener(
(response) => {
console.log(response);
},
),
});
return () => {
notificationListener &&
Notifications.removeNotificationSubscription(notificationListener);
responseListener &&
Notifications.removeNotificationSubscription(responseListener);
};
}, []);
if (!isReady)
return (
<View
style={{
flex: 1,
backgroundColor: "#ECEDEE",
justifyContent: "center",
alignItems: "center",
}}
>
<Image
style={{ width: 200, height: 200 }}
source={require("../assets/images/adaptive-icon.png")}
/>
</View>
);
return (
<SafeAreaProvider>
<GestureHandlerRootView>
<BottomSheetModalProvider>
<ActionSheetProvider>
<KeyboardProvider>
<QueryClientProvider client={queryClient}>
<ThemeProvider value={LIGHT_THEME}>
<Provider userCredentials={loadedUser}>
{Platform.OS === "web" ? (
<View
className="bg-mainBg"
style={{
flex: 1,
alignItems: "center",
justifyContent: "center",
}}
>
<View
style={{
flex: 1,
width: "100%",
maxWidth: maxWidth,
}}
>
<Stack
screenOptions={{
headerShown: false,
}}
/>
</View>
</View>
) : (
<Stack
screenOptions={{
headerShown: false,
}}
/>
)}
<Toaster
richColors={Platform.OS === "web"}
theme="light"
closeButton
// @ts-ignore
position={
Platform.OS === "web" ? "top-right" : "top-center"
}
/>
</Provider>
<StatusBar style="light" />
</ThemeProvider>
</QueryClientProvider>
</KeyboardProvider>
</ActionSheetProvider>
</BottomSheetModalProvider>
<PortalHost />
</GestureHandlerRootView>
</SafeAreaProvider>
);
} I hope my explanation and my example is clear |
Beta Was this translation helpful? Give feedback.
All reactions
-
I simplified your code to the following small example that I can test on my end: Code snippetimport React, { useEffect } from 'react';
import { Platform, StyleSheet, Text, View } from 'react-native';
import Animated, {
useAnimatedStyle,
useSharedValue,
withSpring,
} from 'react-native-reanimated';
export default function EmptyExample() {
const tabPosX = useSharedValue(0);
const animatedStyle = useAnimatedStyle(() => {
if (Platform.OS === 'web') {
return { transform: `translateX(${tabPosX.value}px)` };
}
return { transform: [{ translateX: withSpring(tabPosX.value) }] };
});
useEffect(() => {
const int = setInterval(() => {
tabPosX.value = tabPosX.value === 0 ? 100 : 0;
}, 1000);
return () => clearInterval(int);
}, [tabPosX]);
return (
<View style={styles.container}>
<Text>Hello world!</Text>
<Animated.View
style={[
{
position: 'absolute',
backgroundColor: `red`,
left: -3,
borderRadius: 40,
zIndex: 10,
marginHorizontal: 12,
height: 100,
width: 100,
transform: [{ translateX: 0 }],
},
animatedStyle,
]}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
}); I ran the code on iOS/Android on latest RN/Reanimated and on your versions. Everything worked fine. I wonder if there is not something else that you changed that causes this issue. Can you somehow verify if the |
Beta Was this translation helpful? Give feedback.
All reactions
-
If nothing helps, can you possibly prepare a reproduction example or share a link to your repo with this implementation (if it is not private and can be shared)? |
Beta Was this translation helpful? Give feedback.
All reactions
-
Sure, here's the copy of the project. If you are on login screen, just press the logo on top to go home page, i only open the home and cart pages cuz' that has reanimated on it. Just tell me if something doesnt work because i removed some api call |
Beta Was this translation helpful? Give feedback.
All reactions
-
👀 1
-
Well, I can't build the project.
Can you tell me how can I properly install dependencies and run the project? Do I need any additional step? |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
So, i have a cart page with swipeable and right action button to delete it

It works great on android, so we then also built the web version of the app
It also work and look like this
After it works on web, i tried to revisit the mobile app, but now it looks like this

Turns out, all library that built on top reanimated seems also broke, like react-native-reanimated-carousel
I tried to investigated it on my material tabbar that use reanimated to show the current active tab by change the translateX of the View.
Turns out that the animatedStyle was not applied to animated view, but when i manually set the style, it shows up.
Can anybody help me on this case, every little help will be appreciated :)
Beta Was this translation helpful? Give feedback.
All reactions