Skip to content
Open
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
3 changes: 1 addition & 2 deletions .github/workflows/lint-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ jobs:
- name: Install dependencies
run: |
cd front
npm ci
npm install
npm ci --legacy-peer-deps

- name: Run lint
run: |
Expand Down
6 changes: 3 additions & 3 deletions front/api/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ export type PartialAccount = Partial<Account> & { pin: number };
export const getAccount = async (): Promise<Account | null> => {
try {
const deviceTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const { data, ok, status } = await apiClient<Account>(`/user?timezone=${deviceTimeZone}`);
const { data, status } = await apiClient<Account>(`/user?timezone=${deviceTimeZone}`);

if (!ok) {
if (status !== 200) {
throw new Error(`Failed to fetch account with status ${status}`);
Comment on lines +17 to 20
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 | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "1) Locate /user and /user/delete route or contract definitions"
rg -n -C3 --hidden \
  -g '!**/node_modules/**' -g '!**/dist/**' \
  -e '"/user/delete"|'\''/user/delete'\''' \
  -e '"/user"|'\''/user'\'''

echo
echo "2) Locate explicit response statuses near API handlers/specs"
rg -n -C4 --hidden \
  -g '!**/node_modules/**' -g '!**/dist/**' \
  -e 'status\((200|201|202|204)\)' \
  -e 'sendStatus\((200|201|202|204)\)' \
  -e 'responses:\s*\{'

Repository: tpaulshippy/bots

Length of output: 941


🏁 Script executed:

# Find and examine apiClient.ts to understand ok vs status
find . -name "apiClient.ts" -o -name "apiClient.js" | head -5

Repository: tpaulshippy/bots

Length of output: 84


🏁 Script executed:

# Check what apiClient returns and how ok/status are defined
rg -n "ApiResponse|interface.*Response" front/api/ -A 5

Repository: tpaulshippy/bots

Length of output: 1148


🏁 Script executed:

# Look for backend code in the repository (common locations)
fd -e "ts" -e "js" -e "json" "route\|server\|handler" --max-depth 3 | grep -E "back|server|route" | head -20

Repository: tpaulshippy/bots

Length of output: 42


🏁 Script executed:

# Search for OpenAPI or API specification files
find . -name "*.yaml" -o -name "*.yml" -o -name "*.json" | xargs grep -l "user\|delete" 2>/dev/null | head -10

Repository: tpaulshippy/bots

Length of output: 76


🏁 Script executed:

# Check how other API calls in the codebase handle success verification
rg -n "if.*ok\b|if.*status" front/api/ -B 2 -A 1

Repository: tpaulshippy/bots

Length of output: 5810


🏁 Script executed:

cat -n front/api/apiClient.ts | head -80

Repository: tpaulshippy/bots

Length of output: 2635


Use ok for success checks to match codebase pattern.

Lines 19 and 60 check exact status codes (200 and 204), but the entire codebase—and the ApiResponse interface itself—uses the ok flag for success validation. This exact-match approach is stricter and inconsistent. Line 41 in the same file (updateAccount) already uses !ok, confirming the intended pattern.

Replace both exact status checks with !ok to align with devices.ts, profiles.ts, bots.ts, chats.ts, and aiModels.ts:

Suggested fix
-        const { data, status } = await apiClient<Account>(`/user?timezone=${deviceTimeZone}`);
+        const { data, ok, status } = await apiClient<Account>(`/user?timezone=${deviceTimeZone}`);

-        if (status !== 200) {
+        if (!ok) {
             throw new Error(`Failed to fetch account with status ${status}`);
         }
...
-        const { status } = await apiClient<void>('/user/delete', {
+        const { ok, status } = await apiClient<void>('/user/delete', {
             method: 'DELETE',
         });

-        if (status !== 204) {
+        if (!ok) {
             throw new Error(`Failed to delete account with status ${status}`);
         }

Also applies to: 56–61

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@front/api/account.ts` around lines 17 - 20, The success checks in the account
API calls currently compare HTTP status codes (e.g., using status === 200 and
status === 204) rather than using the ApiResponse.ok flag used across the
codebase; update the handlers that call apiClient<Account>(...) and
apiClient<void>(...) so they validate response.ok (or !ok) instead of exact
status codes, and throw errors when ok is false while keeping the same error
messages and returned data variables (data, status, ok); reference the apiClient
call sites in this file (the functions that fetch the Account and the one that
updates the account) and follow the pattern used in updateAccount where !ok is
already used.

}
return data;
Expand Down Expand Up @@ -53,7 +53,7 @@ export const updateAccount = async (account: PartialAccount): Promise<void> => {

export const deleteAccount = async (): Promise<void> => {
try {
const { ok, status } = await apiClient<void>('/user/delete', {
const { status } = await apiClient<void>('/user/delete', {
method: 'DELETE',
});

Expand Down
2 changes: 1 addition & 1 deletion front/api/apiClient.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { clearUser, getTokens, setTokens, TokenData } from "./tokens";
import { getTokens, setTokens, TokenData } from "./tokens";

const BASE_URL = process.env.EXPO_PUBLIC_API_BASE_URL;

Expand Down
2 changes: 1 addition & 1 deletion front/api/bots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export const fetchBot = async (id: string): Promise<Bot | null> => {

export const upsertBot = async (bot: Bot): Promise<Bot | null> => {
try {
if (bot.id == -1) {
if (bot.id === -1) {
const { data, ok, status } = await apiClient<Bot>("/bots.json", {
method: "POST",
body: JSON.stringify(bot),
Expand Down
3 changes: 1 addition & 2 deletions front/api/chats.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as Sentry from "@sentry/react-native";
import { apiClient } from './apiClient';
import { UnauthorizedError } from './apiClient';
import { apiClient, UnauthorizedError } from './apiClient';

export interface PaginatedResponse<T> {
results: T[];
Expand Down
2 changes: 1 addition & 1 deletion front/api/devices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export const fetchDeviceByToken = async (

export const upsertDevice = async (device: Device): Promise<Device | null> => {
try {
if (device.id == -1) {
if (device.id === -1) {
const { data, ok, status } = await apiClient<Device>("/devices.json", {
method: "POST",
body: JSON.stringify(device),
Expand Down
2 changes: 1 addition & 1 deletion front/api/profiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const fetchProfile = async (id: string): Promise<Profile | null> => {

export const upsertProfile = async (profile: Profile): Promise<Profile | null> => {
try {
if (profile.id == -1) {
if (profile.id === -1) {
const { data, ok, status } = await apiClient<Profile>('/profiles.json', {
method: 'POST',
body: JSON.stringify(profile),
Expand Down
11 changes: 8 additions & 3 deletions front/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
"icon": "./assets/images/syft.png",
"scheme": "botsforkids",
"userInterfaceStyle": "automatic",
"newArchEnabled": true,
"sdkVersion": "53.0.0",
"androidStatusBar": {
"backgroundColor": "#000000"
},
"ios": {
"supportsTablet": true,
"infoPlist": {
Expand Down Expand Up @@ -53,7 +54,11 @@
"photosPermission": "The app accesses your photos to let you send them to your bots.",
"cameraPermission": "The app accesses your camera to let you send photos to your bots."
}
]
],
"@sentry/react-native",
"expo-font",
"expo-image",
"expo-web-browser"
],
"experiments": {
"typedRoutes": true
Expand Down
20 changes: 12 additions & 8 deletions front/app/__tests__/_layout-test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import React from 'react';
import { render, act } from '@testing-library/react-native';
import RootLayout from '../_layout';
import * as SplashScreen from 'expo-splash-screen';
import * as Notifications from 'expo-notifications';
import { fetchBots } from '@/api/bots';
import { fetchChat } from '@/api/chats';
import { UnauthorizedError } from '@/api/apiClient';
import { useRouter, usePathname } from 'expo-router';
import { View } from 'react-native';

jest.mock('@sentry/react-native', () => ({
reactNavigationIntegration: jest.fn(() => ({
Expand All @@ -12,13 +19,6 @@ jest.mock('@sentry/react-native', () => ({
jest.mock('../_layout', () => ({
default: function MockRootLayout() { return null; },
}));
import RootLayout from '../_layout';
import * as SplashScreen from 'expo-splash-screen';
import * as Notifications from 'expo-notifications';
import { fetchBots } from '@/api/bots';
import { fetchChat } from '@/api/chats';
import { UnauthorizedError } from '@/api/apiClient';
import { View } from 'react-native';

// Create a mock Stack component
interface MockStackProps {
Expand Down Expand Up @@ -177,6 +177,10 @@ describe('RootLayout', () => {

unmount();

expect(Notifications.removeNotificationSubscription).toHaveBeenCalled();
const receivedSubscription = (Notifications.addNotificationReceivedListener as jest.Mock).mock.results[0]?.value;
const responseSubscription = (Notifications.addNotificationResponseReceivedListener as jest.Mock).mock.results[0]?.value;

expect(receivedSubscription.remove).toHaveBeenCalled();
expect(responseSubscription.remove).toHaveBeenCalled();
Comment on lines +180 to +184
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 | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the test file structure and the specific lines mentioned
cd front/app/__tests__ && cat -n _layout-test.tsx | head -30

Repository: tpaulshippy/bots

Length of output: 1182


🏁 Script executed:

# Now let's look at lines 15-25 to see the mock setup
cat -n _layout-test.tsx | sed -n '15,25p'

Repository: tpaulshippy/bots

Length of output: 108


🏁 Script executed:

# Look at lines 170-190 to see the full context of the assertions
cat -n _layout-test.tsx | sed -n '170,190p'

Repository: tpaulshippy/bots

Length of output: 108


🏁 Script executed:

# Let's also check the actual implementation to understand cleanup behavior
cat -n ../../../app/_layout.tsx | sed -n '95,130p'

Repository: tpaulshippy/bots

Length of output: 116


🏁 Script executed:

# Check if the test is skipped (using "skip" or "pending" keywords)
grep -n "\.skip\|\.todo\|pending\|xit" _layout-test.tsx | head -20

Repository: tpaulshippy/bots

Length of output: 109


🏁 Script executed:

fd -type f -name "_layout-test.tsx"

Repository: tpaulshippy/bots

Length of output: 230


🏁 Script executed:

fd -type f -name "_layout.tsx" | grep app

Repository: tpaulshippy/bots

Length of output: 230


🏁 Script executed:

fd -t f -name "_layout-test.tsx"

Repository: tpaulshippy/bots

Length of output: 290


🏁 Script executed:

fd -t f -name "_layout.tsx" | grep app

Repository: tpaulshippy/bots

Length of output: 290


🏁 Script executed:

fd "_layout-test.tsx"

Repository: tpaulshippy/bots

Length of output: 96


🏁 Script executed:

fd "_layout.tsx"

Repository: tpaulshippy/bots

Length of output: 81


🏁 Script executed:

cat -n front/app/__tests__/_layout-test.tsx | sed -n '170,195p'

Repository: tpaulshippy/bots

Length of output: 857


🏁 Script executed:

wc -l front/app/_layout.tsx

Repository: tpaulshippy/bots

Length of output: 85


🏁 Script executed:

cat -n front/app/_layout.tsx | sed -n '90,140p'

Repository: tpaulshippy/bots

Length of output: 1882


🏁 Script executed:

# Check the entire test to understand the render call and mock.results
cat -n front/app/__tests__/_layout-test.tsx | sed -n '150,185p'

Repository: tpaulshippy/bots

Length of output: 1600


This assertion never exercises the real cleanup path.

The test is currently skipped (line 172). Because ../_layout is mocked to return null on lines 19-21, render(<RootLayout />) never executes the real useEffect that registers notification listeners (lines 100-130 in _layout.tsx). Once this test is enabled, both mock.results[0] lookups will be undefined, causing the .remove() calls on lines 183-184 to fail. Remove the jest.mock('../_layout', ...) or seed the Notifications mocks with subscription objects before rendering.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@front/app/__tests__/_layout-test.tsx` around lines 180 - 184, The test fails
to exercise the real cleanup because ../_layout is mocked to return null so
RootLayout's useEffect never registers notification listeners; either remove the
jest.mock('../_layout', ...) so render(<RootLayout />) runs the real useEffect
(which registers via Notifications.addNotificationReceivedListener and
Notifications.addNotificationResponseReceivedListener) or, if you must keep the
mock, seed the Notifications.addNotificationReceivedListener and
Notifications.addNotificationResponseReceivedListener mocks with fake
subscription objects that have a remove method before calling render so
(Notifications.addNotificationReceivedListener as
jest.Mock).mock.results[0]?.value and
(Notifications.addNotificationResponseReceivedListener as
jest.Mock).mock.results[0]?.value are defined and their .remove() calls can be
asserted.

});
});
71 changes: 36 additions & 35 deletions front/app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { ErrorBoundary } from "@/components/ErrorBoundary";
import { useFonts } from "expo-font";
import * as SplashScreen from "expo-splash-screen";
import { StatusBar } from "expo-status-bar";
import { useEffect, useRef } from "react";
import { useCallback, useEffect, useRef } from "react";
import * as Notifications from "expo-notifications";
import "react-native-reanimated";
import { useColorScheme } from "@/hooks/useColorScheme";
Expand Down Expand Up @@ -77,7 +77,7 @@ export default function RootLayout() {
const notificationListener = useRef<Notifications.EventSubscription | null>(null);
const responseListener = useRef<Notifications.EventSubscription | null>(null);

const navigateToChat = (chatId: string, title: string) => {
const navigateToChat = useCallback((chatId: string, title: string) => {
if (pathname === "/chat") {
router.replace({
pathname: "/chat",
Expand All @@ -89,7 +89,7 @@ export default function RootLayout() {
params: { chatId, title },
});
}
};
}, [pathname, router]);

useEffect(() => {
if (ref?.current) {
Expand All @@ -99,7 +99,7 @@ export default function RootLayout() {

useEffect(() => {
notificationListener.current =
Notifications.addNotificationReceivedListener(async (notification) => {});
Notifications.addNotificationReceivedListener(async () => {});

responseListener.current =
Notifications.addNotificationResponseReceivedListener(
Expand All @@ -124,33 +124,12 @@ export default function RootLayout() {
);

return () => {
notificationListener.current &&
Notifications.removeNotificationSubscription(
notificationListener.current
);
responseListener.current &&
Notifications.removeNotificationSubscription(responseListener.current);
notificationListener.current?.remove();
responseListener.current?.remove();
};
}, []);

const getJWTFromLink = async (event?: any) => {
const url = event?.url;
if (url) {
const { queryParams } = Linking.parse(url);

if (queryParams && queryParams.access && queryParams.refresh) {
const access = queryParams.access as string;
const refresh = queryParams.refresh as string;
await setTokens({ access, refresh });
WebBrowser.dismissBrowser();
}, [navigateToChat]);

router.replace("/");
await initialNavigationChecks();
}
}
};

const setProfile = async () => {
const setProfile = useCallback(async () => {
const profileData = await AsyncStorage.getItem("selectedProfile");
const profiles = await fetchProfiles();
if (profileData) {
Expand All @@ -168,9 +147,9 @@ export default function RootLayout() {
}
}
}
}
}, []);

const initialNavigationChecks = async () => {
const initialNavigationChecks = useCallback(async () => {
try {
await fetchBots();
await setProfile();
Expand All @@ -180,19 +159,41 @@ export default function RootLayout() {
router.replace("/login");
}
}
};
}, [router, setProfile]);

const getJWTFromLink = useCallback(async (event?: any) => {
const url = event?.url;
if (url) {
const { queryParams } = Linking.parse(url);

if (queryParams && queryParams.access && queryParams.refresh) {
const access = queryParams.access as string;
const refresh = queryParams.refresh as string;
await setTokens({ access, refresh });
WebBrowser.dismissBrowser();

router.replace("/");
await initialNavigationChecks();
}
}
}, [initialNavigationChecks, router]);

useEffect(() => {
if (loaded) {
const subscription = Linking.addEventListener("url", getJWTFromLink);

const initialize = async () => {
SplashScreen.hideAsync();
Linking.addEventListener("url", getJWTFromLink);
await initialNavigationChecks();
};

initialize();
void initialize();

return () => {
subscription.remove();
};
}
}, [loaded]);
}, [getJWTFromLink, initialNavigationChecks, loaded]);
Comment on lines 181 to +196
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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's read the actual code to see what's at lines 181-196
cat -n front/app/_layout.tsx | sed -n '175,205p'

Repository: tpaulshippy/bots

Length of output: 957


🏁 Script executed:

# Check if Linking.getInitialURL is used anywhere in the codebase
rg "getInitialURL" front/

Repository: tpaulshippy/bots

Length of output: 42


🏁 Script executed:

# Look at the entire _layout.tsx file to understand the context
wc -l front/app/_layout.tsx

Repository: tpaulshippy/bots

Length of output: 85


🏁 Script executed:

# Let's search for getJWTFromLink definition
rg "getJWTFromLink" front/app/_layout.tsx -B 5 -A 10

Repository: tpaulshippy/bots

Length of output: 1188


🏁 Script executed:

# Check if there's any deep link handling in app initialization
rg "Linking\." front/app/_layout.tsx

Repository: tpaulshippy/bots

Length of output: 185


🏁 Script executed:

# Look for any other Expo deep link handling patterns in the codebase
rg "Linking\.getInitialURL|Linking\.addEventListener" front/

Repository: tpaulshippy/bots

Length of output: 157


🏁 Script executed:

# Let's see the full _layout.tsx file to understand the overall structure
cat -n front/app/_layout.tsx | head -100

Repository: tpaulshippy/bots

Length of output: 3902


🏁 Script executed:

# Let's see more of the file, particularly the getJWTFromLink function definition
cat -n front/app/_layout.tsx | sed -n '100,180p'

Repository: tpaulshippy/bots

Length of output: 3031


🏁 Script executed:

# Check Expo documentation for Linking API best practices by searching for any comments or setup
rg "Linking|deep.*link" front/app/_layout.tsx -i

Repository: tpaulshippy/bots

Length of output: 226


🏁 Script executed:

# Let's verify the Expo Linking API pattern by checking if there are any other files that handle deep linking
rg "getInitialURL" . --type ts --type tsx

Repository: tpaulshippy/bots

Length of output: 87


🏁 Script executed:

# Check the app.json or app configuration to see if deep linking is configured
fd -e "json" -e "config" | xargs grep -l "scheme\|deep.*link\|linking" 2>/dev/null | head -10

Repository: tpaulshippy/bots

Length of output: 117


🏁 Script executed:

# Check the app.json for deep linking configuration
cat front/app.json

Repository: tpaulshippy/bots

Length of output: 2118


🏁 Script executed:

# Search for deep linking patterns in the entire front directory
rg "getInitialURL|deeplink|deep.*link" front/ -i

Repository: tpaulshippy/bots

Length of output: 42


Process the initial deep link on cold start, not just runtime events.

This effect only subscribes to future "url" events via addEventListener. When the app is cold-started from a deep link (e.g., clicking an auth email link), Linking.getInitialURL() must be called to process that initial URL—otherwise, getJWTFromLink never runs and the token handoff/login flow fails.

Suggested fix
  useEffect(() => {
    if (loaded) {
      const subscription = Linking.addEventListener("url", getJWTFromLink);

      const initialize = async () => {
+       const initialUrl = await Linking.getInitialURL();
+       if (initialUrl) {
+         await getJWTFromLink({ url: initialUrl });
+       }
        await initialNavigationChecks();
-       SplashScreen.hideAsync();
+       await SplashScreen.hideAsync();
      };

      void initialize();

      return () => {
        subscription.remove();
      };
    }
  }, [getJWTFromLink, initialNavigationChecks, loaded]);
📝 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
useEffect(() => {
if (loaded) {
const subscription = Linking.addEventListener("url", getJWTFromLink);
const initialize = async () => {
SplashScreen.hideAsync();
Linking.addEventListener("url", getJWTFromLink);
await initialNavigationChecks();
};
initialize();
void initialize();
return () => {
subscription.remove();
};
}
}, [loaded]);
}, [getJWTFromLink, initialNavigationChecks, loaded]);
useEffect(() => {
if (loaded) {
const subscription = Linking.addEventListener("url", getJWTFromLink);
const initialize = async () => {
const initialUrl = await Linking.getInitialURL();
if (initialUrl) {
await getJWTFromLink({ url: initialUrl });
}
await initialNavigationChecks();
await SplashScreen.hideAsync();
};
void initialize();
return () => {
subscription.remove();
};
}
}, [getJWTFromLink, initialNavigationChecks, loaded]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@front/app/_layout.tsx` around lines 181 - 196, The effect currently only
subscribes to future Linking "url" events and misses processing a cold-start
deep link; update the useEffect that references getJWTFromLink,
initialNavigationChecks, SplashScreen.hideAsync and loaded to also call
Linking.getInitialURL() once during initialization (before or as part of the
initialize async function) and, if it returns a URL, invoke getJWTFromLink with
that URL (handle it the same way the event handler does), then proceed to
addEventListener as before; ensure the initialize function awaits
SplashScreen.hideAsync(), calls initialNavigationChecks(), safely handles errors
from getInitialURL(), and still returns the subscription.remove() cleanup.


if (!loaded) {
return null;
Expand Down
23 changes: 7 additions & 16 deletions front/app/botChat.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { ThemedText } from "@/components/ThemedText";
import { ThemedView } from "@/components/ThemedView";
import { ThemedTextInput } from "@/components/ThemedTextInput";
import React, { useEffect, useState, useMemo } from "react";
import { Platform, KeyboardAvoidingView, FlatList, ActivityIndicator, Dimensions, Keyboard, FlexAlignType, View } from "react-native";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { Platform, KeyboardAvoidingView, FlatList, ActivityIndicator, Dimensions, Keyboard, FlexAlignType } from "react-native";
import { SafeAreaView } from 'react-native-safe-area-context';
import Constants from 'expo-constants';
import { ThemedButton } from "@/components/ThemedButton";
import AsyncStorage from "@react-native-async-storage/async-storage";
import { useLocalSearchParams } from "expo-router";
Expand All @@ -14,19 +12,17 @@ import { fetchChatMessages, sendChat, ChatMessage as ApiChatMessage } from "@/ap
import { IconSymbol } from "@/components/ui/IconSymbol";
import ChatMessage from '@/components/ChatMessage';

const ITEM_HEIGHT = 50;

export default function Chat() {
const local = useLocalSearchParams();
const [chatId, setChatId] = useState<string>();
const [input, setInput] = useState<string>("");
const [messages, setMessages] = useState<ApiChatMessage[]>([]);
const [page, setPage] = useState(1);
const [, setPage] = useState(1);
const [loadingMore, setLoadingMore] = useState(false);
const [hasMore, setHasMore] = useState(true);
const [image, setImage] = useState<string | null>(null);

const refresh = async (nextPage: number) => {
const refresh = useCallback(async (nextPage: number) => {
const chatIdQueryString = local.chatId?.toString();
if (chatIdQueryString) {
setChatId(chatIdQueryString);
Expand All @@ -39,11 +35,11 @@ export default function Chat() {
setLoadingMore(false);
});
}
};
}, [local.chatId]);

useEffect(() => {
refresh(page);
}, []);
void refresh(1);
}, [refresh]);

const getProfileId = async () => {
const profileData = await AsyncStorage.getItem("selectedProfile");
Expand Down Expand Up @@ -173,11 +169,6 @@ export default function Chat() {
data={[...messages].reverse()}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) => <ChatMessage message={item} />}
getItemLayout={(data, index) => ({
length: ITEM_HEIGHT,
offset: ITEM_HEIGHT * index,
index,
})}
onStartReached={handleLoadMore}
onStartReachedThreshold={0.5}
ListHeaderComponent={loadingMore ? <ActivityIndicator /> : null}
Expand Down
Loading
Loading