Skip to content

Commit 43dcd16

Browse files
authored
Merge pull request #210 from Resgrid/develop
RU-T47 Build version number fix
2 parents 63321a8 + 1282cf1 commit 43dcd16

File tree

13 files changed

+498
-939
lines changed

13 files changed

+498
-939
lines changed

.agent/rules/rules.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
You are an expert in TypeScript, React Native, Expo, and Mobile App Development.
2+
3+
Code Style and Structure:
4+
5+
- Write concise, type-safe TypeScript code.
6+
- Use functional components and hooks over class components.
7+
- Ensure components are modular, reusable, and maintainable.
8+
- Organize files by feature, grouping related components, hooks, and styles.
9+
- This is a mobile application, so ensure all components are mobile friendly and responsive and support both iOS and Android platforms and ensure that the app is optimized for both platforms.
10+
11+
Naming Conventions:
12+
13+
- Use camelCase for variable and function names (e.g., `isFetchingData`, `handleUserInput`).
14+
- Use PascalCase for component names (e.g., `UserProfile`, `ChatScreen`).
15+
- Directory and File names should be lowercase and hyphenated (e.g., `user-profile`, `chat-screen`).
16+
17+
TypeScript Usage:
18+
19+
- Use TypeScript for all components, favoring interfaces for props and state.
20+
- Enable strict typing in `tsconfig.json`.
21+
- Avoid using `any`; strive for precise types.
22+
- Utilize `React.FC` for defining functional components with props.
23+
24+
Performance Optimization:
25+
26+
- Minimize `useEffect`, `useState`, and heavy computations inside render methods.
27+
- Use `React.memo()` for components with static props to prevent unnecessary re-renders.
28+
- Optimize FlatLists with props like `removeClippedSubviews`, `maxToRenderPerBatch`, and `windowSize`.
29+
- Use `getItemLayout` for FlatLists when items have a consistent size to improve performance.
30+
- Avoid anonymous functions in `renderItem` or event handlers to prevent re-renders.
31+
32+
UI and Styling:
33+
34+
- Use consistent styling leveraging `gluestack-ui`. If there isn't a Gluestack component in the `components/ui` directory for the component you are trying to use consistently style it either through `StyleSheet.create()` or Styled Components.
35+
- Ensure responsive design by considering different screen sizes and orientations.
36+
- Optimize image handling using libraries designed for React Native, like `react-native-fast-image`.
37+
38+
Best Practices:
39+
40+
- Follow React Native's threading model to ensure smooth UI performance.
41+
- Use React Navigation for handling navigation and deep linking with best practices.
42+
- Create and use Jest to test to validate all generated components
43+
- Generate tests for all components, services and logic generated. Ensure tests run without errors and fix any issues.
44+
- The app is multi-lingual, so ensure all text is wrapped in `t()` from `react-i18next` for translations with the dictonary files stored in `src/translations`.
45+
- Ensure support for dark mode and light mode.
46+
- Ensure the app is accessible, following WCAG guidelines for mobile applications.
47+
- Make sure the app is optimized for performance, especially for low-end devices.
48+
- Handle errors gracefully and provide user feedback.
49+
- Implement proper offline support.
50+
- Ensure the user interface is intuitive and user-friendly and works seamlessly across different devices and screen sizes.
51+
- This is an expo managed project that uses prebuild, do not make native code changes outside of expo prebuild capabilities.
52+
53+
Additional Rules:
54+
55+
- Use `yarn` as the package manager.
56+
- Use Expo's secure store for sensitive data
57+
- Implement proper offline support
58+
- Use `zustand` for state management
59+
- Use `react-hook-form` for form handling
60+
- Use `react-query` for data fetching
61+
- Use `react-i18next` for internationalization
62+
- Use `react-native-mmkv` for local storage
63+
- Use `axios` for API requests
64+
- Use `@rnmapbox/maps` for maps, mapping or vehicle navigation
65+
- Use `lucide-react-native` for icons and use those components directly in the markup and don't use the gluestack-ui icon component
66+
- Use ? : for conditional rendering and not &&

.github/workflows/react-native-cicd.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ jobs:
186186
# Create a backup
187187
cp package.json package.json.bak
188188
# Update the package.json
189-
jq '.version = "7.${{ github.run_number }}.0"' package.json > package.json.tmp && mv package.json.tmp package.json
189+
jq '.version = "7.${{ github.run_number }}"' package.json > package.json.tmp && mv package.json.tmp package.json
190190
jq '.versionCode = "7${{ github.run_number }}"' package.json > package.json.tmp && mv package.json.tmp package.json
191191
echo "Updated package.json versions"
192192
cat package.json | grep "version"
@@ -522,7 +522,7 @@ jobs:
522522
# Update the package.json
523523
if [ -f ./package.json ]; then
524524
cp package.json package.json.bak
525-
jq '.version = "7.${{ github.run_number }}.0"' package.json > package.json.tmp && mv package.json.tmp package.json
525+
jq '.version = "7.${{ github.run_number }}"' package.json > package.json.tmp && mv package.json.tmp package.json
526526
echo "Updated package.json version"
527527
cat package.json | grep "version"
528528
else

src/components/settings/bluetooth-device-selection-bottom-sheet.tsx

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,57 @@ export function BluetoothDeviceSelectionBottomSheet({ isOpen, onClose }: Bluetoo
237237
</Box>
238238
)}
239239

240+
{/* System Audio Option */}
241+
<Box className="mb-4">
242+
<Text className="mb-2 text-sm text-neutral-600 dark:text-neutral-400">{t('bluetooth.audio_output')}</Text>
243+
<Pressable
244+
onPress={async () => {
245+
try {
246+
useBluetoothAudioStore.getState().setIsConnecting(true);
247+
// We use a dummy ID for loading state tracking if needed, or just rely on global loading
248+
setConnectingDeviceId('system-audio');
249+
250+
await bluetoothAudioService.connectToSystemAudio();
251+
252+
// Update preferred device manually here to ensure UI reflects it immediately
253+
// preventing race conditions with store updates
254+
await setPreferredDevice({ id: 'system-audio', name: 'System Audio' });
255+
256+
onClose();
257+
} catch (error) {
258+
logger.error({ message: 'Failed to select System Audio', context: { error } });
259+
showMessage({
260+
message: t('bluetooth.connection_error_title') || 'Selection Failed',
261+
description: t('bluetooth.system_audio_error') || 'Could not switch to System Audio',
262+
type: 'danger',
263+
});
264+
} finally {
265+
useBluetoothAudioStore.getState().setIsConnecting(false);
266+
setConnectingDeviceId(null);
267+
}
268+
}}
269+
disabled={!!connectingDeviceId}
270+
className={`rounded-lg border p-4 ${preferredDevice?.id === 'system-audio' ? 'border-primary-500 bg-primary-50 dark:bg-primary-950' : 'border-neutral-200 bg-white dark:border-neutral-700 dark:bg-neutral-800'} ${!!connectingDeviceId ? 'opacity-70' : ''}`}
271+
>
272+
<HStack className="items-center justify-between">
273+
<HStack className="items-center">
274+
<BluetoothIcon size={16} className="mr-2 text-primary-600" />
275+
<VStack>
276+
<Text className={`font-medium ${preferredDevice?.id === 'system-audio' ? 'text-primary-700 dark:text-primary-300' : 'text-neutral-900 dark:text-neutral-100'}`}>
277+
System Audio
278+
</Text>
279+
<Text className="text-xs text-neutral-500">AirPods, Car, Wired Headset</Text>
280+
</VStack>
281+
</HStack>
282+
{preferredDevice?.id === 'system-audio' && (
283+
<VStack className="items-end">
284+
<Text className="text-sm font-medium text-primary-600 dark:text-primary-400">{t('bluetooth.selected')}</Text>
285+
</VStack>
286+
)}
287+
</HStack>
288+
</Pressable>
289+
</Box>
290+
240291
{/* Scan Button */}
241292
<HStack className="mb-4 w-full items-center justify-between">
242293
<Text className="text-sm text-neutral-600 dark:text-neutral-400">{t('bluetooth.available_devices')}</Text>

src/services/__tests__/bluetooth-audio.service.test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,18 @@ jest.mock('@/services/audio.service', () => ({
5151
},
5252
}));
5353

54+
jest.mock('@/stores/app/livekit-store', () => {
55+
const actions = {
56+
toggleMicrophone: jest.fn(),
57+
setMicrophoneEnabled: jest.fn(),
58+
};
59+
return {
60+
useLiveKitStore: {
61+
getState: jest.fn(() => actions),
62+
},
63+
};
64+
});
65+
5466
import { bluetoothAudioService } from '../bluetooth-audio.service';
5567

5668
describe('BluetoothAudioService Refactoring', () => {
@@ -159,4 +171,28 @@ describe('BluetoothAudioService Refactoring', () => {
159171
expect(service.hasAttemptedPreferredDeviceConnection).toBe(false);
160172
});
161173
});
174+
describe('Microphone Control Delegation', () => {
175+
it('should delegate mute toggle to livekitStore', async () => {
176+
const service = bluetoothAudioService as any;
177+
178+
// Call private method via casting to any
179+
await service.handleMuteToggle();
180+
181+
const storeMock = require('@/stores/app/livekit-store').useLiveKitStore.getState();
182+
expect(storeMock.toggleMicrophone).toHaveBeenCalled();
183+
});
184+
185+
it('should delegate setMicrophoneEnabled to livekitStore', async () => {
186+
const service = bluetoothAudioService as any;
187+
188+
// Call private method via casting to any
189+
await service.setMicrophoneEnabled(true);
190+
191+
const storeMock = require('@/stores/app/livekit-store').useLiveKitStore.getState();
192+
expect(storeMock.setMicrophoneEnabled).toHaveBeenCalledWith(true);
193+
194+
await service.setMicrophoneEnabled(false);
195+
expect(storeMock.setMicrophoneEnabled).toHaveBeenCalledWith(false);
196+
});
197+
});
162198
});

0 commit comments

Comments
 (0)