From f0877d6f0a58ed1f1471fef54d4cfaf3b8ac3724 Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Thu, 7 May 2026 17:30:37 -0600 Subject: [PATCH] fix(app): address PR review comments - expo CatalogItem.createdAt/updatedAt: string | Date (fixes CI cast error) - replace crypto.randomUUID() with generateId() using crypto.getRandomValues() for older React Native / Hermes compatibility - catalogInfinite queryKey now includes limit + sort params - error.value instead of String(error) for Eden Treaty error messages --- apps/expo/features/catalog/types.ts | 4 ++-- packages/app/index.ts | 1 + packages/app/src/entities/catalog/queries.ts | 4 ++-- packages/app/src/entities/feed/queries.ts | 2 +- packages/app/src/features/pack/add-item/queries.ts | 3 ++- packages/app/src/features/pack/create/queries.ts | 3 ++- packages/app/src/features/trip/create/queries.ts | 3 ++- packages/app/src/shared/api/query-keys.ts | 10 ++++++++-- packages/app/src/shared/lib/uuid.ts | 10 ++++++++++ 9 files changed, 30 insertions(+), 10 deletions(-) create mode 100644 packages/app/src/shared/lib/uuid.ts diff --git a/apps/expo/features/catalog/types.ts b/apps/expo/features/catalog/types.ts index 019359c72f..57b1ea3138 100644 --- a/apps/expo/features/catalog/types.ts +++ b/apps/expo/features/catalog/types.ts @@ -91,8 +91,8 @@ export interface CatalogItem { embedding?: number[] | null; // vector(1536) - createdAt: string; - updatedAt: string; + createdAt: string | Date; + updatedAt: string | Date; } export type CatalogItemWithQuantity = CatalogItem & { quantity?: number }; diff --git a/packages/app/index.ts b/packages/app/index.ts index 167c5d758d..fd8939578e 100644 --- a/packages/app/index.ts +++ b/packages/app/index.ts @@ -8,4 +8,5 @@ export { useCreatePackMutation } from './src/features/pack/create/queries'; export { useCreateTripMutation } from './src/features/trip/create/queries'; export * from './src/shared/api'; export * from './src/shared/lib/date'; +export * from './src/shared/lib/uuid'; export * from './src/shared/lib/weight'; diff --git a/packages/app/src/entities/catalog/queries.ts b/packages/app/src/entities/catalog/queries.ts index 8e70715b81..cad2b1822f 100644 --- a/packages/app/src/entities/catalog/queries.ts +++ b/packages/app/src/entities/catalog/queries.ts @@ -27,7 +27,7 @@ export function useCatalogItemsInfinite({ }: UseCatalogItemsParams) { const client = useApiClient(); return useInfiniteQuery({ - queryKey: queryKeys.catalogInfinite(query, category), + queryKey: queryKeys.catalogInfinite({ search: query, category, limit, sort }), queryFn: async ({ pageParam = 1 }) => { const { data, error } = await client.catalog.get({ query: { @@ -38,7 +38,7 @@ export function useCatalogItemsInfinite({ ...(sort ? { sort } : {}), }, }); - if (error) throw new Error(`Failed to fetch catalog items: ${String(error)}`); + if (error) throw new Error(`Failed to fetch catalog items: ${error.value}`); const parseResult = CatalogItemsResponseSchema.safeParse(data); if (parseResult.success) return parseResult.data; return { diff --git a/packages/app/src/entities/feed/queries.ts b/packages/app/src/entities/feed/queries.ts index 5d5a1a24f5..a11a3141f9 100644 --- a/packages/app/src/entities/feed/queries.ts +++ b/packages/app/src/entities/feed/queries.ts @@ -9,7 +9,7 @@ export function useFeed() { const { data, error } = await client.feed.get({ query: { page: pageParam as number, limit: 20 }, }); - if (error) throw new Error(`Failed to fetch feed: ${String(error)}`); + if (error) throw new Error(`Failed to fetch feed: ${error.value}`); return data; }, getNextPageParam: (lastPage) => { diff --git a/packages/app/src/features/pack/add-item/queries.ts b/packages/app/src/features/pack/add-item/queries.ts index 524716161b..0473114c77 100644 --- a/packages/app/src/features/pack/add-item/queries.ts +++ b/packages/app/src/features/pack/add-item/queries.ts @@ -1,5 +1,6 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { queryKeys, useApiClient } from '../../../shared/api'; +import { generateId } from '../../../shared/lib/uuid'; interface AddPackItemInput { name: string; @@ -22,7 +23,7 @@ export function useAddPackItemMutation() { mutationFn: async ({ packId, body }: { packId: string; body: AddPackItemInput }) => { const { data, error } = await client.packs({ packId }).items.post({ ...body, - id: crypto.randomUUID(), + id: generateId(), quantity: body.quantity ?? 1, consumable: body.consumable ?? false, worn: body.worn ?? false, diff --git a/packages/app/src/features/pack/create/queries.ts b/packages/app/src/features/pack/create/queries.ts index eab7309740..fc45e84680 100644 --- a/packages/app/src/features/pack/create/queries.ts +++ b/packages/app/src/features/pack/create/queries.ts @@ -1,5 +1,6 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { queryKeys, useApiClient } from '../../../shared/api'; +import { generateId } from '../../../shared/lib/uuid'; interface CreatePackInput { name: string; @@ -18,7 +19,7 @@ export function useCreatePackMutation() { const now = new Date().toISOString(); const { data, error } = await client.packs.post({ ...input, - id: crypto.randomUUID(), + id: generateId(), isPublic: input.isPublic ?? false, localCreatedAt: now, localUpdatedAt: now, diff --git a/packages/app/src/features/trip/create/queries.ts b/packages/app/src/features/trip/create/queries.ts index 4fc5e8f257..c257d49e07 100644 --- a/packages/app/src/features/trip/create/queries.ts +++ b/packages/app/src/features/trip/create/queries.ts @@ -1,5 +1,6 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { queryKeys, useApiClient } from '../../../shared/api'; +import { generateId } from '../../../shared/lib/uuid'; interface CreateTripInput { name: string; @@ -19,7 +20,7 @@ export function useCreateTripMutation() { const now = new Date().toISOString(); const { data, error } = await client.trips.post({ ...input, - id: crypto.randomUUID(), + id: generateId(), localCreatedAt: now, localUpdatedAt: now, }); diff --git a/packages/app/src/shared/api/query-keys.ts b/packages/app/src/shared/api/query-keys.ts index 21dd19b37c..e4994ac5b7 100644 --- a/packages/app/src/shared/api/query-keys.ts +++ b/packages/app/src/shared/api/query-keys.ts @@ -6,8 +6,14 @@ export const queryKeys = { trip: (id: string) => ['trip', id] as const, catalog: (opts: { page?: number; search?: string; category?: string } = {}) => ['catalog', { page: opts.page ?? 1, search: opts.search, category: opts.category }] as const, - catalogInfinite: (search?: string, category?: string) => - ['catalogInfinite', { search, category }] as const, + catalogInfinite: ( + opts: { + search?: string; + category?: string; + limit?: number; + sort?: { field: string; order: string }; + } = {}, + ) => ['catalogInfinite', opts] as const, catalogItem: (id: number) => ['catalogItem', id] as const, feed: (page = 1, filter?: 'trending' | 'recent' | 'following') => ['feed', { page, filter }] as const, diff --git a/packages/app/src/shared/lib/uuid.ts b/packages/app/src/shared/lib/uuid.ts new file mode 100644 index 0000000000..b4c35da162 --- /dev/null +++ b/packages/app/src/shared/lib/uuid.ts @@ -0,0 +1,10 @@ +// crypto.randomUUID() is not available in older React Native (Hermes < 0.71). +// crypto.getRandomValues() is available since RN 0.60 via the Hermes polyfill. +export function generateId(): string { + const bytes = new Uint8Array(16); + crypto.getRandomValues(bytes); + bytes[6] = ((bytes[6] ?? 0) & 0x0f) | 0x40; + bytes[8] = ((bytes[8] ?? 0) & 0x3f) | 0x80; + const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join(''); + return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`; +}