Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/app/main/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export default wrapSentry(function TabLayout() {
options={{
title: "Settings",
tabBarIcon: getTabBarIcon("Settings", "cog"),
headerShown: true,
headerShown: false,
}}
/>
</Tabs>
Expand Down
5 changes: 0 additions & 5 deletions app/app/main/settings.tsx

This file was deleted.

5 changes: 5 additions & 0 deletions app/app/main/settings/_layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Stack } from "expo-router";

export default function SettingsLayout() {
return <Stack />;
}
57 changes: 57 additions & 0 deletions app/app/main/settings/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { useRouter } from "expo-router";
import { useEffect, useState } from "react";
import AsyncStorage from "@react-native-async-storage/async-storage";
import { DevSettings } from "react-native";
import { Box } from "@/components/ui/box";
import { Pressable } from "@/components/ui/pressable";
import { Text } from "@/components/ui/text";
import { HStack } from "@/components/ui/hstack";

export default function SettingsScreen() {
const router = useRouter();
const [sentryEnabled, setSentryEnabled] = useState(false);

useEffect(() => {
AsyncStorage.getItem("sentryConsent").then((v) =>
setSentryEnabled(!!parseInt(v ?? "0")),
);
}, []);

const toggleSentry = async () => {
const next = !sentryEnabled;
setSentryEnabled(next);
await AsyncStorage.setItem("sentryConsent", next ? "1" : "0");
DevSettings.reload();
};
Comment on lines +20 to +25
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Production-safe reload: prefer expo-updates over DevSettings.reload()

DevSettings.reload() is dev-only and won’t work in production. Use expo-updates when available, falling back to DevSettings.reload().

-import { DevSettings } from "react-native";
+import { DevSettings } from "react-native";
+import * as Updates from "expo-updates";
@@
   const toggleSentry = async () => {
     const next = !sentryEnabled;
     setSentryEnabled(next);
     await AsyncStorage.setItem("sentryConsent", next ? "1" : "0");
-    DevSettings.reload();
+    if (Updates?.reloadAsync) {
+      await Updates.reloadAsync();
+    } else if (DevSettings?.reload) {
+      DevSettings.reload();
+    }
   };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const toggleSentry = async () => {
const next = !sentryEnabled;
setSentryEnabled(next);
await AsyncStorage.setItem("sentryConsent", next ? "1" : "0");
DevSettings.reload();
};
// at the top of app/app/main/settings/index.tsx
import { DevSettings } from "react-native";
import * as Updates from "expo-updates";
// …
const toggleSentry = async () => {
const next = !sentryEnabled;
setSentryEnabled(next);
await AsyncStorage.setItem("sentryConsent", next ? "1" : "0");
if (Updates?.reloadAsync) {
await Updates.reloadAsync();
} else if (DevSettings?.reload) {
DevSettings.reload();
}
};
🤖 Prompt for AI Agents
In app/app/main/settings/index.tsx around lines 20 to 25, the toggleSentry
function calls DevSettings.reload() which only works in dev builds; replace that
call with expo-updates' reload when available and fall back to
DevSettings.reload() for older setups. Import { Updates } from 'expo-updates'
(or require dynamically), then after persisting the consent call await
Updates.reloadAsync() when Updates.isAvailable is true (or wrap in try/catch),
otherwise call DevSettings.reload(); ensure the reload invocation is awaited or
handled and gracefully falls back on error.


return (
<Box className="flex-1 p-4">
<Pressable
className="py-4 border-b border-gray-200"
onPress={() => router.push("/main/settings/licenses")}
>
<Text>Licenses</Text>
</Pressable>
<HStack className="justify-between items-center py-4">
<Text>Sentry telemetry</Text>
<Pressable onPress={toggleSentry}>
<Box
className={`w-10 h-6 rounded-full px-1 flex-row items-center ${
sentryEnabled ? "bg-primary-500" : "bg-gray-300"
}`}
>
<Box
className={`h-4 w-4 rounded-full bg-white transition-all ${
sentryEnabled ? "ml-4" : "ml-0"
}`}
/>
</Box>
</Pressable>
</HStack>
</Box>
);
}

export const options = {
title: "Settings",
};
23 changes: 23 additions & 0 deletions app/app/main/settings/licenses/[pkg].tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useLocalSearchParams } from "expo-router";
import { ScrollView } from "@/components/ui/scroll-view";
import { Text } from "@/components/ui/text";
import licenses from "@/licenses.json";

export default function LicenseDetailScreen() {
const { pkg } = useLocalSearchParams<{ pkg: string }>();
const info = licenses.find((p) => p.name === pkg);
return (
<ScrollView className="flex-1 p-4">
<Text bold className="mb-4">
{info?.name}
</Text>
<Text className="whitespace-pre-wrap">
{info?.text || "License not found"}
</Text>
</ScrollView>
);
}

export const options = ({ params }: { params: { pkg: string } }) => ({
title: params.pkg,
});
32 changes: 32 additions & 0 deletions app/app/main/settings/licenses/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useRouter } from "expo-router";
import { Pressable } from "@/components/ui/pressable";
import { Text } from "@/components/ui/text";
import { ScrollView } from "@/components/ui/scroll-view";
import licenses from "@/licenses.json";

export default function LicenseListScreen() {
const router = useRouter();
return (
<ScrollView className="flex-1 p-4">
{licenses.map((pkg) => (
<Pressable
key={pkg.name}
className="py-2 border-b border-gray-200"
onPress={() =>
router.push({
pathname: "/main/settings/licenses/[pkg]",
params: { pkg: pkg.name },
})
}
>
<Text bold>{pkg.name}</Text>
<Text className="text-sm text-gray-500">{pkg.license}</Text>
</Pressable>
))}
</ScrollView>
);
}

export const options = {
title: "Licenses",
};
23 changes: 23 additions & 0 deletions app/components/ui/scroll-view/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from "react";
import { ScrollView as RNScrollView, ScrollViewProps } from "react-native";
import type { VariantProps } from "@gluestack-ui/nativewind-utils";
import { scrollViewStyle } from "./styles";

type IScrollViewProps = ScrollViewProps &
VariantProps<typeof scrollViewStyle> & { className?: string };

const ScrollView = React.forwardRef<
React.ComponentRef<typeof RNScrollView>,
IScrollViewProps
>(function ScrollView({ className, ...props }, ref) {
return (
<RNScrollView
ref={ref}
{...props}
className={scrollViewStyle({ class: className })}
/>
);
});

ScrollView.displayName = "ScrollView";
export { ScrollView };
15 changes: 15 additions & 0 deletions app/components/ui/scroll-view/index.web.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from "react";
import type { VariantProps } from "@gluestack-ui/nativewind-utils";
import { scrollViewStyle } from "./styles";

type IScrollViewProps = React.ComponentPropsWithoutRef<"div"> &
VariantProps<typeof scrollViewStyle>;

const ScrollView = React.forwardRef<React.ComponentRef<"div">, IScrollViewProps>(
function ScrollView({ className, ...props }, ref) {
return <div ref={ref} {...props} className={scrollViewStyle({ class: className })} />;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix Prettier failure by wrapping long JSX line

CI flagged a Prettier issue. Wrap props across lines.

-    return <div ref={ref} {...props} className={scrollViewStyle({ class: className })} />;
+    return (
+      <div
+        ref={ref}
+        {...props}
+        className={scrollViewStyle({ class: className })}
+      />
+    );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return <div ref={ref} {...props} className={scrollViewStyle({ class: className })} />;
return (
<div
ref={ref}
{...props}
className={scrollViewStyle({ class: className })}
/>
);
🤖 Prompt for AI Agents
In app/components/ui/scroll-view/index.web.tsx around line 10, the single long
JSX return line causes Prettier failure; split the JSX attributes across
multiple lines so the element is wrapped: place the opening tag on its own line,
put ref={ref}, {...props}, and className={scrollViewStyle({ class: className })}
each on separate indented lines, and close the tag on its own line to satisfy
line-length/formatting rules.

},
);

ScrollView.displayName = "ScrollView";
export { ScrollView };
10 changes: 10 additions & 0 deletions app/components/ui/scroll-view/styles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { tva } from "@gluestack-ui/nativewind-utils/tva";
import { isWeb } from "@gluestack-ui/nativewind-utils/IsWeb";

const baseStyle = isWeb
? "flex flex-col relative z-0 box-border border-0 list-none min-w-0 min-h-0 bg-transparent items-stretch m-0 p-0 text-decoration-none"
: "";

export const scrollViewStyle = tva({
base: baseStyle,
});
5,150 changes: 5,150 additions & 0 deletions app/licenses.json

Large diffs are not rendered by default.

Loading