Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
8ad25c7
Initial plan
Copilot Mar 13, 2026
bfb37fe
chore: add biome useMaxParams rule with maxParameters: 1
Copilot Mar 13, 2026
10f1669
chore: upgrade biome to 2.4.6, switch useMaxParams to error
Copilot Mar 13, 2026
71bbd93
chore: fix all biome check violations after upgrading to 2.4.6 with u…
Copilot Mar 13, 2026
068dcab
chore: fix minor warnings and remove unused biome-ignore suppression …
Copilot Mar 13, 2026
62d6deb
fix: resolve CI TypeScript errors and API test failures
Copilot Mar 21, 2026
a1e9b90
refactor: remove biome-ignore suppressions, fix useMaxParams violatio…
Copilot Mar 21, 2026
5617318
fix: replace all Legend-State @ts-expect-error suppressions with type…
Copilot Mar 21, 2026
8f3bcb3
fix: remove all ignore/expect-error directives and properly fix under…
Copilot Mar 21, 2026
4c21d2d
fix: improve r2-bucket.ts type safety with nullish coalescing and met…
Copilot Mar 21, 2026
29de3aa
chore: merge development into copilot/setup-use-max-params-rule
Copilot Apr 10, 2026
80272d0
fix(api,expo): resolve type errors blocking check-types
andrew-bierman Apr 11, 2026
1e17555
Merge remote-tracking branch 'origin/development' into copilot/setup-…
andrew-bierman Apr 11, 2026
f55422d
fix(expo): route assertDefined import through @packrat/guards
andrew-bierman Apr 11, 2026
840608c
Merge branch 'development' into copilot/setup-use-max-params-rule
andrew-bierman Apr 11, 2026
5205ef4
chore(analytics): refactor query-builder to object params for useMaxP…
andrew-bierman Apr 11, 2026
690dc37
Merge branch 'development' into copilot/setup-use-max-params-rule
andrew-bierman Apr 11, 2026
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
1 change: 1 addition & 0 deletions .github/scripts/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const expoFileContent = envFileContent
} else if (line.startsWith('EXPO_PUBLIC_')) {
return line;
}
return undefined;
})
.join('\n');
const expoNoTelemetry = 'EXPO_NO_TELEMETRY=true';
Expand Down
140 changes: 68 additions & 72 deletions apps/expo/app/(app)/upcoming-trips.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -142,83 +142,79 @@ export default function UpcomingTripsScreen() {
const selectedPack = selectedTrip ? packs.find((p) => p.id === selectedTrip.packId) : undefined;

return (
<>
<ScrollView className="flex-1">
<View className="p-4">
<Text variant="subhead" className="mb-2 text-muted-foreground">
{t('trips.plannedAdventures')}
</Text>
</View>
<ScrollView className="flex-1">
<View className="p-4">
<Text variant="subhead" className="mb-2 text-muted-foreground">
{t('trips.plannedAdventures')}
</Text>
</View>

{/* Trip List */}
<List
data={upcomingTrips.map((trip) => ({
id: trip.id,
trip,
title: trip.name,
subTitle: `${trip.location?.name ?? t('trips.unknown')} • ${formatDate(
trip.startDate,
)} to ${formatDate(trip.endDate)}`,
}))}
extraData={selectedTripId}
keyExtractor={(item) => item.id}
renderItem={(info) => {
const { trip } = info.item;
const { status, completion } = getTripStatus(trip, t);

return (
<ListItem
{...info}
// leftView={<TripImage uri={trip.imageUrl} />}
rightView={
<View className="flex-row items-center">
<PackStatus status={status} completion={completion} />
</View>
}
onPress={() => setSelectedTripId(trip.id)}
className={
selectedTripId === trip.id ? 'bg-muted/50 dark:bg-slate-950' : 'dark:bg-transparent'
}
/>
);
}}
/>

{/* Trip Summary */}
{selectedTrip && (
<View className="mx-4 my-4 rounded-lg bg-card">
<View className="border-border/25 dark:border-border/80 border-b p-4">
<Text variant="heading" className="font-semibold">
{selectedTrip.name}
</Text>
<Text variant="subhead" className="mt-1 text-muted-foreground">
{selectedTrip.location?.name ?? 'No location'}
</Text>
</View>

{/* Trip List */}
<List
data={upcomingTrips.map((trip) => ({
id: trip.id,
trip,
title: trip.name,
subTitle: `${trip.location?.name ?? t('trips.unknown')} • ${formatDate(
trip.startDate,
)} to ${formatDate(trip.endDate)}`,
}))}
extraData={selectedTripId}
keyExtractor={(item) => item.id}
renderItem={(info) => {
const { trip } = info.item;
const { status, completion } = getTripStatus(trip, t);

return (
<ListItem
{...info}
// leftView={<TripImage uri={trip.imageUrl} />}
rightView={
<View className="flex-row items-center">
<PackStatus status={status} completion={completion} />
</View>
}
onPress={() => setSelectedTripId(trip.id)}
className={
selectedTripId === trip.id
? 'bg-muted/50 dark:bg-slate-950'
: 'dark:bg-transparent'
}
/>
);
}}
/>

{/* Trip Summary */}
{selectedTrip && (
<View className="mx-4 my-4 rounded-lg bg-card">
<View className="border-border/25 dark:border-border/80 border-b p-4">
<Text variant="heading" className="font-semibold">
{selectedTrip.name}
<View className="flex-row justify-between p-4">
<View className="flex-1">
<Text variant="footnote" className="text-muted-foreground">
DATES
</Text>
<Text variant="subhead" className="mt-1 text-muted-foreground">
{selectedTrip.location?.name ?? 'No location'}
<Text variant="subhead" className="mt-1">
{formatDate(selectedTrip.startDate)} - {formatDate(selectedTrip.endDate)}
</Text>
</View>

<View className="flex-row justify-between p-4">
<View className="flex-1">
<Text variant="footnote" className="text-muted-foreground">
DATES
</Text>
<Text variant="subhead" className="mt-1">
{formatDate(selectedTrip.startDate)} - {formatDate(selectedTrip.endDate)}
</Text>
</View>
<View className="flex-1">
<Text variant="footnote" className="text-muted-foreground">
PACK
</Text>
<Text variant="subhead" className="mt-1">
{selectedPack ? `${selectedPack.items.length} items` : 'No pack assigned'}
</Text>
</View>
<View className="flex-1">
<Text variant="footnote" className="text-muted-foreground">
PACK
</Text>
<Text variant="subhead" className="mt-1">
{selectedPack ? `${selectedPack.items.length} items` : 'No pack assigned'}
</Text>
</View>
</View>
)}
</ScrollView>
</>
</View>
)}
</ScrollView>
);
}
4 changes: 2 additions & 2 deletions apps/expo/app/(app)/weight-analysis/[id].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export default function WeightAnalysisScreen() {
</Text>
</View>

{data.categories.map((category, categoryIndex) => (
{data.categories.map((category, _categoryIndex) => (
<View key={category.name} className="mx-4 mb-4 rounded-lg bg-card">
{/* Category Header */}
<View className="border-border/25 dark:border-border/80 flex-row items-center justify-between border-b p-4">
Expand All @@ -110,7 +110,7 @@ export default function WeightAnalysisScreen() {
.filter((item) => item.category.trim() === category.name.trim())
.map((item, itemIndex) => (
<View
key={`${categoryIndex}-${item.id}`}
key={`${category.name}-${item.id}`}
className={cn(
'flex-row items-center justify-between p-4',
itemIndex > 0 ? 'border-border/25 dark:border-border/80 border-t' : '',
Expand Down
7 changes: 6 additions & 1 deletion apps/expo/app/auth/(create-account)/credentials.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,12 @@ export default function CredentialsScreen() {
};

// Call signup function with all user data
await signUp(userData.email, userData.password, userData.firstName, userData.lastName);
await signUp({
email: userData.email,
password: userData.password,
firstName: userData.firstName,
lastName: userData.lastName,
});

// Navigate to verification code screen
router.push({
Expand Down
6 changes: 2 additions & 4 deletions apps/expo/features/ai-packs/hooks/useGeneratedPacks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useMutation } from '@tanstack/react-query';
import type { Pack } from 'expo-app/features/packs';
import { packsStore } from 'expo-app/features/packs/store';
import axiosInstance, { handleApiError } from 'expo-app/lib/api/client';
import { obs } from 'expo-app/lib/store';
import type { GenerationRequest } from '../types';

const generatePacks = async (request: GenerationRequest): Promise<Pack[]> => {
Expand All @@ -25,10 +26,7 @@ export function useGeneratePacks() {

const generatedPacksFromStore = use$(() => {
if (mutation.data) {
return mutation.data.map((pack) =>
// @ts-ignore: Safe because Legend-State uses Proxy
packsStore[pack.id].get(),
);
return mutation.data.map((pack) => obs(packsStore, pack.id).get());
}
return [];
});
Expand Down
2 changes: 1 addition & 1 deletion apps/expo/features/ai-packs/screens/AIPacksScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export function AIPacksScreen() {
<TextInput
className="border border-border rounded-lg px-3 py-2 text-foreground bg-background"
value={field.state.value.toString()}
onChangeText={(text) => field.handleChange(Number.parseInt(text) || 1)}
onChangeText={(text) => field.handleChange(Number.parseInt(text, 10) || 1)}
keyboardType="numeric"
placeholder={t('ai.enterCount')}
/>
Expand Down
1 change: 1 addition & 0 deletions apps/expo/features/ai/atoms/chatStorageAtoms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export async function loadChatMessages(context: ChatContext): Promise<UIMessage[
/**
* Saves chat messages to AsyncStorage
*/

export async function saveChatMessages(context: ChatContext, messages: UIMessage[]): Promise<void> {
try {
const key = getChatStorageKey(context);
Expand Down
2 changes: 2 additions & 0 deletions apps/expo/features/ai/components/ChatBubble.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ export const ChatBubble = React.memo(function ChatBubble({
/>
</View>
);

return null;
})}
</View>
{/* </ContextMenu> */}
Expand Down
12 changes: 11 additions & 1 deletion apps/expo/features/auth/hooks/useAuthActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,17 @@ export function useAuthActions() {
}
};

const signUp = async (email: string, password: string, firstName?: string, lastName?: string) => {
const signUp = async ({
email,
password,
firstName,
lastName,
}: {
email: string;
password: string;
firstName?: string;
lastName?: string;
}) => {
setIsLoading(true);
try {
const response = await fetch(`${clientEnvs.EXPO_PUBLIC_API_URL}/api/auth/register`, {
Expand Down
1 change: 0 additions & 1 deletion apps/expo/features/guides/components/GuideCard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Card, CardContent, CardTitle, Text } from '@packrat/ui/nativewindui';
import { Icon } from '@roninoss/icons';
import { useTranslation } from 'expo-app/lib/hooks/useTranslation';
import { TouchableOpacity, View } from 'react-native';
import type { Guide } from '../types';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,54 +98,50 @@ export function PackTemplateItemCard({
};

return (
<>
<TouchableWithoutFeedback key={item.id} onPress={() => onPress(item)}>
<View className="rounded-lg flex-row gap-3 border p-4 border-border bg-card">
{/* Image */}
<PackTemplateItemImage item={item} className="h-16 w-16 rounded-md" resizeMode="cover" />
<TouchableWithoutFeedback key={item.id} onPress={() => onPress(item)}>
<View className="rounded-lg flex-row gap-3 border p-4 border-border bg-card">
{/* Image */}
<PackTemplateItemImage item={item} className="h-16 w-16 rounded-md" resizeMode="cover" />

{/* Content */}
<View className="flex-1">
<View className="flex-row gap-2 justify-between items-start">
<Text className="flex-1 font-medium text-foreground" numberOfLines={2}>
{item.name}
</Text>
<Pressable onPress={handleActionsPress}>
<Icon name="dots-horizontal" size={20} color={colors.grey2} />
</Pressable>
</View>
<Text className="text-sm text-muted-foreground mb-2">{item.category}</Text>
{/* Content */}
<View className="flex-1">
<View className="flex-row gap-2 justify-between items-start">
<Text className="flex-1 font-medium text-foreground" numberOfLines={2}>
{item.name}
</Text>
<Pressable onPress={handleActionsPress}>
<Icon name="dots-horizontal" size={20} color={colors.grey2} />
</Pressable>
</View>
<Text className="text-sm text-muted-foreground mb-2">{item.category}</Text>

<View className="flex-row items-center gap-4 flex-wrap">
{item.consumable && (
<View className={cn('rounded-full px-2 py-0.5', 'bg-amber-100')}>
<Text className={cn('text-xs', 'text-amber-600')}>
{t('packTemplates.consumable')}
</Text>
</View>
)}
<View className="flex-row items-center gap-4 flex-wrap">
{item.consumable && (
<View className={cn('rounded-full px-2 py-0.5', 'bg-amber-100')}>
<Text className={cn('text-xs', 'text-amber-600')}>
{t('packTemplates.consumable')}
</Text>
</View>
)}

{item.worn && (
<View className={cn('rounded-full px-2 py-0.5', 'bg-emerald-100')}>
<Text className={cn('text-xs', 'text-emerald-600')}>
{t('packTemplates.worn')}
</Text>
</View>
)}
</View>
<View className="mt-2 flex-row items-center gap-4 flex-wrap">
<Text className="text-sm font-medium text-foreground">
{item.weight}
{item.weightUnit}
</Text>
{item.worn && (
<View className={cn('rounded-full px-2 py-0.5', 'bg-emerald-100')}>
<Text className={cn('text-xs', 'text-emerald-600')}>{t('packTemplates.worn')}</Text>
</View>
)}
</View>
<View className="mt-2 flex-row items-center gap-4 flex-wrap">
<Text className="text-sm font-medium text-foreground">
{item.weight}
{item.weightUnit}
</Text>

<Text className="text-sm text-muted-foreground">
{item.quantity} {t('packTemplates.qty')}
</Text>
</View>
<Text className="text-sm text-muted-foreground">
{item.quantity} {t('packTemplates.qty')}
</Text>
</View>
</View>
</TouchableWithoutFeedback>
</>
</View>
</TouchableWithoutFeedback>
);
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { obs } from 'expo-app/lib/store';
import { nanoid } from 'nanoid/non-secure';
import { useCallback } from 'react';
import { packTemplatesStore } from '../store/packTemplates';
Expand All @@ -16,8 +17,7 @@ export function useCreatePackTemplate() {
deleted: false,
};

// @ts-ignore: Safe because Legend-State uses Proxy
packTemplatesStore[id].set(newTemplate);
obs(packTemplatesStore, id).set(newTemplate);
}, []);

return createPackTemplate;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// useCreatePackTemplateItem.ts

import { obs } from 'expo-app/lib/store';
import { nanoid } from 'nanoid/non-secure';
import { useCallback } from 'react';
import { packTemplateItemsStore } from '../store/packTemplateItems';
Expand All @@ -18,10 +19,8 @@ export function useCreatePackTemplateItem() {
deleted: false,
};

// @ts-ignore: Safe because Legend-State uses Proxy
packTemplateItemsStore[id].set(newItem);
// @ts-ignore: Safe because Legend-State uses Proxy
packTemplatesStore[packTemplateId].localUpdatedAt.set(new Date().toISOString());
obs(packTemplateItemsStore, id).set(newItem);
obs(packTemplatesStore, packTemplateId).localUpdatedAt.set(new Date().toISOString());
},
[],
);
Expand Down
Loading
Loading