summaryrefslogtreecommitdiff
path: root/src/react-native-app/app
diff options
context:
space:
mode:
authorSaumit <justsaumit@protonmail.com>2025-09-27 02:14:26 +0530
committerSaumit <justsaumit@protonmail.com>2025-09-27 02:14:26 +0530
commit82e03978b89938219958032efb1448cc76baa181 (patch)
tree626f3e54d52ecd49be0ed3bee30abacc0453d081 /src/react-native-app/app
Initial snapshot - OpenTelemetry demo 2.1.3 -f
Diffstat (limited to 'src/react-native-app/app')
-rw-r--r--src/react-native-app/app/(tabs)/_layout.tsx62
-rw-r--r--src/react-native-app/app/(tabs)/cart.tsx145
-rw-r--r--src/react-native-app/app/(tabs)/index.tsx39
-rw-r--r--src/react-native-app/app/(tabs)/settings.tsx40
-rw-r--r--src/react-native-app/app/_layout.tsx59
5 files changed, 345 insertions, 0 deletions
diff --git a/src/react-native-app/app/(tabs)/_layout.tsx b/src/react-native-app/app/(tabs)/_layout.tsx
new file mode 100644
index 0000000..ba2a7b0
--- /dev/null
+++ b/src/react-native-app/app/(tabs)/_layout.tsx
@@ -0,0 +1,62 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+import { Tabs } from "expo-router";
+import React from "react";
+import { TabBarIcon } from "@/components/navigation/TabBarIcon";
+import { useCart } from "@/providers/Cart.provider";
+
+export default function TabLayout() {
+ const {
+ cart: { items },
+ } = useCart();
+
+ let itemsInCart = 0;
+ items.forEach((item) => {
+ itemsInCart += item.quantity;
+ });
+
+ return (
+ <Tabs>
+ <Tabs.Screen
+ name="index"
+ options={{
+ title: "Products",
+ tabBarShowLabel: false,
+ tabBarIcon: ({ color, focused }) => (
+ <TabBarIcon
+ name={focused ? "list" : "list-outline"}
+ color={color}
+ />
+ ),
+ }}
+ />
+ <Tabs.Screen
+ name="cart"
+ options={{
+ title: "Cart",
+ tabBarShowLabel: false,
+ tabBarBadge: itemsInCart || undefined,
+ tabBarIcon: ({ color, focused }) => (
+ <TabBarIcon
+ name={focused ? "cart" : "cart-outline"}
+ color={color}
+ />
+ ),
+ }}
+ />
+ <Tabs.Screen
+ name="settings"
+ options={{
+ title: "Settings",
+ tabBarShowLabel: false,
+ tabBarIcon: ({ color, focused }) => (
+ <TabBarIcon
+ name={focused ? "settings" : "settings-outline"}
+ color={color}
+ />
+ ),
+ }}
+ />
+ </Tabs>
+ );
+}
diff --git a/src/react-native-app/app/(tabs)/cart.tsx b/src/react-native-app/app/(tabs)/cart.tsx
new file mode 100644
index 0000000..c91f847
--- /dev/null
+++ b/src/react-native-app/app/(tabs)/cart.tsx
@@ -0,0 +1,145 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+/**
+ * Copied with modification from src/frontend/components/Cart/CartDetail.tsx
+ */
+import { router } from "expo-router";
+import { ThemedView } from "@/components/ThemedView";
+import { ThemedText } from "@/components/ThemedText";
+import { Pressable, StyleSheet } from "react-native";
+import { useCart } from "@/providers/Cart.provider";
+import CheckoutForm from "@/components/CheckoutForm";
+import EmptyCart from "@/components/EmptyCart";
+import { ThemedScrollView } from "@/components/ThemedScrollView";
+import { useCallback, useMemo } from "react";
+import { IFormData } from "@/components/CheckoutForm/CheckoutForm";
+import Toast from "react-native-toast-message";
+import SessionGateway from "@/gateways/Session.gateway";
+import { useThemeColor } from "@/hooks/useThemeColor";
+
+export default function Cart() {
+ const tint = useThemeColor({}, "tint");
+ const styles = useMemo(() => getStyles(tint), [tint]);
+ const {
+ cart: { items },
+ emptyCart,
+ placeOrder,
+ } = useCart();
+
+ const onEmptyCart = useCallback(() => {
+ emptyCart();
+ Toast.show({
+ type: "success",
+ position: "bottom",
+ text1: "Your cart was emptied",
+ });
+ }, [emptyCart]);
+
+ const onPlaceOrder = useCallback(
+ async ({
+ email,
+ state,
+ streetAddress,
+ country,
+ city,
+ zipCode,
+ creditCardCvv,
+ creditCardExpirationMonth,
+ creditCardExpirationYear,
+ creditCardNumber,
+ }: IFormData) => {
+ const { userId } = await SessionGateway.getSession();
+ await placeOrder({
+ userId,
+ email,
+ address: {
+ streetAddress,
+ state,
+ country,
+ city,
+ zipCode,
+ },
+ // TODO simplify react native demo for now by hard-coding the selected currency
+ userCurrency: "USD",
+ creditCard: {
+ creditCardCvv,
+ creditCardExpirationMonth,
+ creditCardExpirationYear,
+ creditCardNumber,
+ },
+ });
+
+ Toast.show({
+ type: "success",
+ position: "bottom",
+ text1: "Your order is Complete!",
+ text2: "We've sent you a confirmation email.",
+ });
+
+ router.replace("/");
+ },
+ [placeOrder],
+ );
+
+ if (!items.length) {
+ return <EmptyCart />;
+ }
+
+ return (
+ <ThemedView style={styles.container}>
+ <ThemedView>
+ <ThemedScrollView>
+ {items.map((item) => (
+ <ThemedView key={item.productId} style={styles.cartItem}>
+ <ThemedText>{item.product.name}</ThemedText>
+ <ThemedText style={styles.bold}>{item.quantity}</ThemedText>
+ </ThemedView>
+ ))}
+ </ThemedScrollView>
+ </ThemedView>
+ <ThemedView style={styles.emptyCartContainer}>
+ <Pressable style={styles.emptyCart} onPress={onEmptyCart}>
+ <ThemedText style={styles.emptyCartText}>Empty Cart</ThemedText>
+ </Pressable>
+ </ThemedView>
+ <CheckoutForm onSubmit={onPlaceOrder} />
+ </ThemedView>
+ );
+}
+
+const getStyles = (tint: string) =>
+ StyleSheet.create({
+ container: {
+ flex: 1,
+ gap: 20,
+ justifyContent: "flex-start",
+ },
+ emptyCartContainer: {
+ display: "flex",
+ alignItems: "flex-end",
+ },
+ emptyCart: {
+ borderRadius: 4,
+ backgroundColor: "green",
+ alignItems: "center",
+ width: 100,
+ right: 20,
+ position: "relative",
+ },
+ emptyCartText: {
+ color: "white",
+ },
+ cartItem: {
+ marginLeft: 20,
+ marginRight: 20,
+ display: "flex",
+ flexDirection: "row",
+ justifyContent: "space-between",
+ borderStyle: "solid",
+ borderBottomWidth: 1,
+ borderColor: tint,
+ },
+ bold: {
+ fontWeight: "bold",
+ },
+ });
diff --git a/src/react-native-app/app/(tabs)/index.tsx b/src/react-native-app/app/(tabs)/index.tsx
new file mode 100644
index 0000000..c01e1ea
--- /dev/null
+++ b/src/react-native-app/app/(tabs)/index.tsx
@@ -0,0 +1,39 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+import { ThemedView } from "@/components/ThemedView";
+import ProductList from "@/components/ProductList";
+import { useQuery } from "@tanstack/react-query";
+import { ScrollView, StyleSheet } from "react-native";
+import { ThemedText } from "@/components/ThemedText";
+import ApiGateway from "@/gateways/Api.gateway";
+
+export default function Index() {
+ const { data: productList = [] } = useQuery({
+ // TODO simplify react native demo for now by hard-coding the selected currency
+ queryKey: ["products", "USD"],
+ queryFn: () => ApiGateway.listProducts("USD"),
+ });
+
+ return (
+ <ThemedView style={styles.container}>
+ <ScrollView>
+ {productList.length ? (
+ <ProductList productList={productList} />
+ ) : (
+ <ThemedText>
+ No products found, make sure the backend services for the
+ OpenTelemetry demo are running
+ </ThemedText>
+ )}
+ </ScrollView>
+ </ThemedView>
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ justifyContent: "center",
+ alignItems: "center",
+ },
+});
diff --git a/src/react-native-app/app/(tabs)/settings.tsx b/src/react-native-app/app/(tabs)/settings.tsx
new file mode 100644
index 0000000..d278918
--- /dev/null
+++ b/src/react-native-app/app/(tabs)/settings.tsx
@@ -0,0 +1,40 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+import { useQueryClient } from '@tanstack/react-query'
+import { ThemedView } from "@/components/ThemedView";
+import { StyleSheet } from "react-native";
+import { getFrontendProxyURL, setFrontendProxyURL } from "@/utils/Settings";
+import { setupTracerProvider } from "@/hooks/useTracer";
+import { trace } from "@opentelemetry/api";
+import { Setting } from "@/components/Setting";
+
+export default function Settings() {
+ const queryClient = useQueryClient()
+
+ const onSetFrontendProxyURL = async (value: string) => {
+ await setFrontendProxyURL(value);
+
+ // Clear any cached queries since we now have a new endpoint to hit for everything
+ await queryClient.invalidateQueries();
+
+ // Need to setup a new tracer provider since the export URL for traces has now changed
+ trace.disable();
+ const provider = setupTracerProvider(value);
+ trace.setGlobalTracerProvider(provider);
+ };
+
+ return (
+ <ThemedView style={styles.container}>
+ <Setting name="Frontend Proxy URL" get={getFrontendProxyURL} set={onSetFrontendProxyURL} />
+ </ThemedView>
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ display: "flex",
+ gap: 20,
+ paddingLeft: 20,
+ height: "100%",
+ },
+});
diff --git a/src/react-native-app/app/_layout.tsx b/src/react-native-app/app/_layout.tsx
new file mode 100644
index 0000000..d8ac89c
--- /dev/null
+++ b/src/react-native-app/app/_layout.tsx
@@ -0,0 +1,59 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+import { SplashScreen, Stack } from "expo-router";
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
+import {
+ DarkTheme,
+ DefaultTheme,
+ ThemeProvider,
+} from "@react-navigation/native";
+import { useColorScheme } from "react-native";
+import { RootSiblingParent } from "react-native-root-siblings";
+import Toast from "react-native-toast-message";
+import { useFonts } from "expo-font";
+import { useEffect, useMemo } from "react";
+import { useTracer } from "@/hooks/useTracer";
+import CartProvider from "@/providers/Cart.provider";
+
+const queryClient = new QueryClient();
+
+export default function RootLayout() {
+ const colorScheme = useColorScheme();
+ const [fontsLoaded] = useFonts({
+ SpaceMono: require("../assets/fonts/SpaceMono-Regular.ttf"),
+ });
+ const { loaded: tracerLoaded } = useTracer();
+
+ const loaded = useMemo<boolean>(
+ () => fontsLoaded && tracerLoaded,
+ [fontsLoaded, tracerLoaded],
+ );
+ useEffect(() => {
+ if (loaded) {
+ SplashScreen.hideAsync();
+ }
+ }, [loaded]);
+
+ if (!loaded) {
+ return null;
+ }
+
+ return (
+ <ThemeProvider value={colorScheme === "dark" ? DarkTheme : DefaultTheme}>
+ <RootSiblingParent>
+ <QueryClientProvider client={queryClient}>
+ <CartProvider>
+ {/*
+ TODO Once https://github.com/open-telemetry/opentelemetry-js-contrib/pull/2359 is available it can
+ be used here to provide telemetry for navigation between tabs
+ */}
+ <Stack>
+ <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
+ </Stack>
+ </CartProvider>
+ </QueryClientProvider>
+ </RootSiblingParent>
+ <Toast />
+ </ThemeProvider>
+ );
+}