Skip to content
Merged
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
1 change: 1 addition & 0 deletions src/app/(app)/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ export default function Map() {
title: t('tabs.map'),
headerTitle: t('app.title'),
headerShown: true,
headerBackTitle: '',
}}
/>
<View className="size-full flex-1" testID="map-container">
Expand Down
4 changes: 4 additions & 0 deletions src/app/call/[id].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ export default function CallDetail() {
title: t('call_detail.title'),
headerShown: true,
headerRight: () => <HeaderRightMenu />,
headerBackTitle: '',
}}
/>
<View className="size-full flex-1">
Expand All @@ -228,6 +229,7 @@ export default function CallDetail() {
title: t('call_detail.title'),
headerShown: true,
headerRight: () => <HeaderRightMenu />,
headerBackTitle: '',
}}
/>
<View className="size-full flex-1">
Expand All @@ -247,6 +249,7 @@ export default function CallDetail() {
options={{
title: t('call_detail.title'),
headerShown: true,
headerBackTitle: '',
}}
/>
<SafeAreaView className="size-full flex-1">
Expand Down Expand Up @@ -478,6 +481,7 @@ export default function CallDetail() {
title: t('call_detail.title'),
headerShown: true,
headerRight: () => <HeaderRightMenu />,
headerBackTitle: '',
}}
/>
<ScrollView className={`size-full w-full flex-1 ${colorScheme === 'dark' ? 'bg-neutral-950' : 'bg-neutral-50'}`}>
Expand Down
3 changes: 3 additions & 0 deletions src/app/call/[id]/edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@ export default function EditCall() {
options={{
title: t('calls.edit_call'),
headerShown: true,
headerBackTitle: '',
}}
/>
<Loading />
Expand All @@ -418,6 +419,7 @@ export default function EditCall() {
options={{
title: t('calls.edit_call'),
headerShown: true,
headerBackTitle: '',
}}
/>
<View className="size-full flex-1">
Expand All @@ -435,6 +437,7 @@ export default function EditCall() {
options={{
title: t('calls.edit_call'),
headerShown: true,
headerBackTitle: '',
}}
/>
<View className="size-full flex-1">
Expand Down
1 change: 1 addition & 0 deletions src/app/call/new/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,7 @@ export default function NewCall() {
options={{
title: t('calls.new_call'),
headerShown: true,
headerBackTitle: '',
}}
/>
<View className="size-full flex-1">
Expand Down
58 changes: 47 additions & 11 deletions src/components/livekit/livekit-bottom-sheet.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { t } from 'i18next';
import { Headphones, Mic, MicOff, PhoneOff, Settings } from 'lucide-react-native';
import { useColorScheme } from 'nativewind';
import React, { useCallback, useEffect, useState } from 'react';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { ScrollView, StyleSheet, TouchableOpacity, View } from 'react-native';

import { useAnalytics } from '@/hooks/use-analytics';
Expand Down Expand Up @@ -35,6 +35,16 @@ export const LiveKitBottomSheet = () => {
const [isMuted, setIsMuted] = useState(true); // Default to muted
const [permissionsRequested, setPermissionsRequested] = useState(false);

// Use ref to track if component is mounted to prevent state updates after unmount
const isMountedRef = useRef(true);

// Cleanup function to prevent state updates after unmount
useEffect(() => {
return () => {
isMountedRef.current = false;
};
}, []);

// Track when LiveKit bottom sheet is opened/rendered
useEffect(() => {
if (isBottomSheetVisible) {
Expand Down Expand Up @@ -67,23 +77,49 @@ export const LiveKitBottomSheet = () => {
permissionsRequested,
]);

// Request permissions once when the component becomes visible
// Request permissions when the component becomes visible
useEffect(() => {
const requestPermissionsOnce = async () => {
if (isBottomSheetVisible && !permissionsRequested) {
if (isBottomSheetVisible && !permissionsRequested && isMountedRef.current) {
// Check if we're in a test environment
const isTestEnvironment = process.env.NODE_ENV === 'test' || process.env.JEST_WORKER_ID !== undefined;

if (isTestEnvironment) {
// In tests, handle permissions synchronously to avoid act warnings
try {
await requestPermissions();
// Call requestPermissions but don't await it in tests
const result = requestPermissions();
// Only call .catch if the result is a promise
if (result && typeof result.catch === 'function') {
result.catch(() => {
// Silently handle any errors in test environment
});
}
setPermissionsRequested(true);
} catch (error) {
console.error('Failed to request permissions:', error);
}
} else {
// In production, use the async approach with timeout
const timeoutId = setTimeout(async () => {
if (isMountedRef.current && !permissionsRequested) {
try {
await requestPermissions();
if (isMountedRef.current) {
setPermissionsRequested(true);
}
} catch (error) {
if (isMountedRef.current) {
console.error('Failed to request permissions:', error);
}
}
}
}, 0);

return () => {
clearTimeout(timeoutId);
};
}
};

// Don't await in useEffect - just call the async function
requestPermissionsOnce().catch((error) => {
console.error('Failed to request permissions:', error);
});
}
}, [isBottomSheetVisible, permissionsRequested, requestPermissions]);

// Sync mute state with LiveKit room
Expand Down
Loading
Loading