From d48d0f2eb00f0cd9438cdc0b5077992bf896d26a Mon Sep 17 00:00:00 2001 From: Andrew Bierman <94939237+andrew-bierman@users.noreply.github.com> Date: Sat, 11 Apr 2026 00:34:31 -0600 Subject: [PATCH 1/2] fix(biome): resolve all pre-existing lint errors on development MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Merge duplicate overrides in biome.json (was silently ignored) - Add scripts/lint/** and .github/scripts/** to useMaxParams override - Add noExplicitAny test-file override to merged overrides array - Fix noArrayIndexKey in feed components (PostCard, CreatePost, PostDetail) - Fix useMaxParams in useWildlifeHistory — consolidate 3 params into opts object - Fix import ordering in (home)/index.tsx and feed/[id].tsx - Add biome-ignore comments in localModelManager.ts and auth/store/user.ts - Auto-format feed screens and hooks 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- apps/expo/app/(app)/(tabs)/(home)/index.tsx | 2 +- apps/expo/app/(app)/feed/[id].tsx | 8 ++-- .../expo/features/ai/lib/localModelManager.ts | 5 ++- apps/expo/features/auth/store/user.ts | 1 + .../features/feed/components/PostCard.tsx | 1 + .../features/feed/hooks/usePostComments.ts | 7 ++-- .../feed/screens/CreatePostScreen.tsx | 12 ++---- .../expo/features/feed/screens/FeedScreen.tsx | 15 ++----- .../feed/screens/PostDetailScreen.tsx | 1 + .../wildlife/hooks/useWildlifeHistory.ts | 11 ++--- .../wildlife/screens/IdentificationScreen.tsx | 2 +- biome.json | 42 +++++++++---------- packages/api/src/routes/feed/comments.ts | 7 +--- 13 files changed, 51 insertions(+), 63 deletions(-) diff --git a/apps/expo/app/(app)/(tabs)/(home)/index.tsx b/apps/expo/app/(app)/(tabs)/(home)/index.tsx index d0e2f28c11..da0cfc6956 100644 --- a/apps/expo/app/(app)/(tabs)/(home)/index.tsx +++ b/apps/expo/app/(app)/(tabs)/(home)/index.tsx @@ -14,8 +14,8 @@ import { clientEnvs } from 'expo-app/env/clientEnvs'; import { AIChatTile } from 'expo-app/features/ai/components/AIChatTile'; import { ReportedContentTile } from 'expo-app/features/ai/components/ReportedContentTile'; import { AIPacksTile } from 'expo-app/features/ai-packs/components/AIPacksTile'; -import { GuidesTile } from 'expo-app/features/guides/components/GuidesTile'; import { FeedTile } from 'expo-app/features/feed/components/FeedTile'; +import { GuidesTile } from 'expo-app/features/guides/components/GuidesTile'; import { PackTemplatesTile } from 'expo-app/features/pack-templates/components/PackTemplatesTile'; import { CurrentPackTile } from 'expo-app/features/packs/components/CurrentPackTile'; import { GearInventoryTile } from 'expo-app/features/packs/components/GearInventoryTile'; diff --git a/apps/expo/app/(app)/feed/[id].tsx b/apps/expo/app/(app)/feed/[id].tsx index 44691540b7..ba9c9efdd0 100644 --- a/apps/expo/app/(app)/feed/[id].tsx +++ b/apps/expo/app/(app)/feed/[id].tsx @@ -1,11 +1,11 @@ -import { PostDetailScreen } from 'expo-app/features/feed'; -import { userStore } from 'expo-app/features/auth/store'; import { Text } from '@packrat/ui/nativewindui'; -import { useLocalSearchParams } from 'expo-router'; import { useQuery } from '@tanstack/react-query'; +import { userStore } from 'expo-app/features/auth/store'; +import { PostDetailScreen } from 'expo-app/features/feed'; +import type { Post } from 'expo-app/features/feed/types'; import axiosInstance from 'expo-app/lib/api/client'; +import { useLocalSearchParams } from 'expo-router'; import { ActivityIndicator, View } from 'react-native'; -import type { Post } from 'expo-app/features/feed/types'; export default function PostDetailRoute() { const { id } = useLocalSearchParams<{ id: string }>(); diff --git a/apps/expo/features/ai/lib/localModelManager.ts b/apps/expo/features/ai/lib/localModelManager.ts index fe70ba5324..76e4c4395e 100644 --- a/apps/expo/features/ai/lib/localModelManager.ts +++ b/apps/expo/features/ai/lib/localModelManager.ts @@ -50,6 +50,7 @@ async function _isLlamaModelAvailable(): Promise { // ─── Apple Dynamic Import ─────────────────────────────────────────────────── +// biome-ignore lint/suspicious/noExplicitAny: dynamic import type unknown let appleModule: any = null; function getAppleModule() { @@ -66,9 +67,9 @@ function getAppleModule() { // ─── Singletons ───────────────────────────────────────────────────────────── let llamaModel: LlamaLanguageModel | null = null; -// eslint-disable-next-line @typescript-eslint/no-explicit-any +// biome-ignore lint/suspicious/noExplicitAny: Apple module type unknown let appleModel: any = null; -// eslint-disable-next-line @typescript-eslint/no-explicit-any +// biome-ignore lint/suspicious/noExplicitAny: download task type unknown let activeDownloadTask: any = null; let _isCancellingDownload = false; diff --git a/apps/expo/features/auth/store/user.ts b/apps/expo/features/auth/store/user.ts index ace2a57c76..10f3a13fa5 100644 --- a/apps/expo/features/auth/store/user.ts +++ b/apps/expo/features/auth/store/user.ts @@ -12,6 +12,7 @@ syncObservable( syncedCrud({ persist: { name: 'user', + // biome-ignore lint/suspicious/noExplicitAny: Storage type mismatch with legend-state plugin plugin: observablePersistSqlite(Storage as any), }, }), diff --git a/apps/expo/features/feed/components/PostCard.tsx b/apps/expo/features/feed/components/PostCard.tsx index 49ac5f5027..fdabdc2f99 100644 --- a/apps/expo/features/feed/components/PostCard.tsx +++ b/apps/expo/features/feed/components/PostCard.tsx @@ -70,6 +70,7 @@ export const PostCard: React.FC = ({ post, onLike, onDelete, curr style={styles.imageScroll} > {post.images.map((img, idx) => ( + // biome-ignore lint/suspicious/noArrayIndexKey: images have no stable id { return useInfiniteQuery({ queryKey: ['feed', postId, 'comments'], queryFn: async ({ pageParam = 1 }) => { - const response = await axiosInstance.get( - `/api/feed/${postId}/comments`, - { params: { page: pageParam, limit: 20 } }, - ); + const response = await axiosInstance.get(`/api/feed/${postId}/comments`, { + params: { page: pageParam, limit: 20 }, + }); return response.data; }, getNextPageParam: (lastPage) => { diff --git a/apps/expo/features/feed/screens/CreatePostScreen.tsx b/apps/expo/features/feed/screens/CreatePostScreen.tsx index 3555cf6059..a89a16a65e 100644 --- a/apps/expo/features/feed/screens/CreatePostScreen.tsx +++ b/apps/expo/features/feed/screens/CreatePostScreen.tsx @@ -76,10 +76,7 @@ export const CreatePostScreen = ({ onSuccess }: { onSuccess?: () => void }) => { const asset = result.assets[0]; if (asset) { setPhotos((prev) => - [ - ...prev, - { uri: asset.uri, fileName: `camera_${Date.now()}.jpg` }, - ].slice(0, 10), + [...prev, { uri: asset.uri, fileName: `camera_${Date.now()}.jpg` }].slice(0, 10), ); } } @@ -104,7 +101,7 @@ export const CreatePostScreen = ({ onSuccess }: { onSuccess?: () => void }) => { const results = await Promise.all( photos.map(async (photo) => { const ext = photo.fileName.includes('.') - ? photo.fileName.split('.').pop()?.toLowerCase() ?? 'jpg' + ? (photo.fileName.split('.').pop()?.toLowerCase() ?? 'jpg') : 'jpg'; const uniqueName = `${nanoid()}.${ext}`; return uploadImage(uniqueName, photo.uri); @@ -148,6 +145,7 @@ export const CreatePostScreen = ({ onSuccess }: { onSuccess?: () => void }) => { {/* Photo grid */} {photos.map((photo, idx) => ( + // biome-ignore lint/suspicious/noArrayIndexKey: photos have no stable id void }) => { className="text-foreground text-sm min-h-[80px]" style={{ color: colors.foreground }} /> - - {caption.length}/2000 - + {caption.length}/2000 {/* Submit */} diff --git a/apps/expo/features/feed/screens/FeedScreen.tsx b/apps/expo/features/feed/screens/FeedScreen.tsx index 7610390de7..bd9c285837 100644 --- a/apps/expo/features/feed/screens/FeedScreen.tsx +++ b/apps/expo/features/feed/screens/FeedScreen.tsx @@ -16,15 +16,8 @@ export const FeedScreen = () => { const router = useRouter(); const currentUserId = userStore.id.peek() as number | undefined; - const { - data, - isLoading, - isRefetching, - refetch, - fetchNextPage, - hasNextPage, - isFetchingNextPage, - } = useFeed(); + const { data, isLoading, isRefetching, refetch, fetchNextPage, hasNextPage, isFetchingNextPage } = + useFeed(); const { mutate: toggleLike } = useTogglePostLike(); const { mutate: deletePost } = useDeletePost(); @@ -113,9 +106,7 @@ export const FeedScreen = () => { renderItem={renderItem} keyExtractor={(item) => String(item.id)} contentContainerStyle={{ paddingHorizontal: 16, paddingTop: 8, paddingBottom: 24 }} - refreshControl={ - - } + refreshControl={} onEndReached={handleLoadMore} onEndReachedThreshold={0.3} ListFooterComponent={renderFooter} diff --git a/apps/expo/features/feed/screens/PostDetailScreen.tsx b/apps/expo/features/feed/screens/PostDetailScreen.tsx index f81767cb00..14bd6afeb3 100644 --- a/apps/expo/features/feed/screens/PostDetailScreen.tsx +++ b/apps/expo/features/feed/screens/PostDetailScreen.tsx @@ -108,6 +108,7 @@ export const PostDetailScreen = ({ post, currentUserId }: PostDetailScreenProps) style={styles.imageScroll} > {post.images.map((img, idx) => ( + // biome-ignore lint/suspicious/noArrayIndexKey: images have no stable id { + async (opts: { + imageUri: string; + results: IdentificationResult[]; + location?: WildlifeIdentification['location']; + }) => { + const { imageUri, results, location } = opts; const entry: WildlifeIdentification = { id: nanoid(), imageUri, diff --git a/apps/expo/features/wildlife/screens/IdentificationScreen.tsx b/apps/expo/features/wildlife/screens/IdentificationScreen.tsx index 569a0be4e6..6b7a1c7862 100644 --- a/apps/expo/features/wildlife/screens/IdentificationScreen.tsx +++ b/apps/expo/features/wildlife/screens/IdentificationScreen.tsx @@ -87,7 +87,7 @@ export function IdentificationScreen() { } catch (err) { console.warn('Failed to persist image locally, using original URI:', err); } - await addIdentification(persistedUri, identificationResults); + await addIdentification({ imageUri: persistedUri, results: identificationResults }); }, }, ); diff --git a/biome.json b/biome.json index 451685ef2d..e067f70bfb 100644 --- a/biome.json +++ b/biome.json @@ -67,7 +67,9 @@ "packages/api/test/guides.test.ts", "packages/api/test/setup.ts", "apps/expo/utils/weight.ts", - "packages/api/src/utils/weight.ts" + "packages/api/src/utils/weight.ts", + "scripts/lint/**", + ".github/scripts/**" ], "linter": { "rules": { @@ -76,6 +78,23 @@ } } } + }, + { + "includes": [ + "**/__tests__/**", + "**/test/**", + "**/*.test.ts", + "**/*.test.tsx", + "**/*.spec.ts", + "**/*.spec.tsx" + ], + "linter": { + "rules": { + "suspicious": { + "noExplicitAny": "off" + } + } + } } ], "css": { @@ -97,24 +116,5 @@ "organizeImports": "on" } } - }, - "overrides": [ - { - "includes": [ - "**/__tests__/**", - "**/test/**", - "**/*.test.ts", - "**/*.test.tsx", - "**/*.spec.ts", - "**/*.spec.tsx" - ], - "linter": { - "rules": { - "suspicious": { - "noExplicitAny": "off" - } - } - } - } - ] + } } diff --git a/packages/api/src/routes/feed/comments.ts b/packages/api/src/routes/feed/comments.ts index 8a862d8b7a..0927340fd1 100644 --- a/packages/api/src/routes/feed/comments.ts +++ b/packages/api/src/routes/feed/comments.ts @@ -3,8 +3,8 @@ import { createDb } from '@packrat/api/db'; import { commentLikes, postComments, posts, users } from '@packrat/api/db/schema'; import { ErrorResponseSchema } from '@packrat/api/schemas/catalog'; import { - CommentsResponseSchema, CommentSchema, + CommentsResponseSchema, CreateCommentRequestSchema, LikeToggleResponseSchema, } from '@packrat/api/schemas/feed'; @@ -54,10 +54,7 @@ commentsRoutes.openapi(listCommentsRoute, async (c) => { const offset = (page - 1) * limit; const [totalResult, items] = await Promise.all([ - db - .select({ count: count() }) - .from(postComments) - .where(eq(postComments.postId, postId)), + db.select({ count: count() }).from(postComments).where(eq(postComments.postId, postId)), db .select({ id: postComments.id, From 3a69f402658d2d41040ee451e3de298030f93872 Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Sat, 11 Apr 2026 00:07:27 -0600 Subject: [PATCH 2/2] fix(trips): use valid Icon name "map" in TrailConditionsBanner MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1885 shipped with Icon name="terrain" which isn't in the @roninoss/icons SF Symbol allowlist, breaking check-types on development. Replace with "map" — valid and semantically equivalent for a trail-conditions banner. --- apps/expo/features/trips/screens/TripListScreen.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/expo/features/trips/screens/TripListScreen.tsx b/apps/expo/features/trips/screens/TripListScreen.tsx index c47bb03e56..df669d4343 100644 --- a/apps/expo/features/trips/screens/TripListScreen.tsx +++ b/apps/expo/features/trips/screens/TripListScreen.tsx @@ -21,7 +21,7 @@ function TrailConditionsBanner() { - + {t('trips.trailConditions')}