Skip to content

Commit 8dc3263

Browse files
authored
Merge pull request #130 from Resgrid/develop
CU-868cu9311 Added in audio stream player. Multi delete inbox messages.
2 parents f1d1ee6 + 740bba9 commit 8dc3263

File tree

19 files changed

+1752
-26
lines changed

19 files changed

+1752
-26
lines changed

.cursorrules

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ Code Style and Structure:
66
- Use functional components and hooks over class components.
77
- Ensure components are modular, reusable, and maintainable.
88
- 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.
910

1011
Naming Conventions:
1112

1213
- Use camelCase for variable and function names (e.g., `isFetchingData`, `handleUserInput`).
1314
- Use PascalCase for component names (e.g., `UserProfile`, `ChatScreen`).
14-
- Directory names should be lowercase and hyphenated (e.g., `user-profile`, `chat-screen`).
15+
- Directory and File names should be lowercase and hyphenated (e.g., `user-profile`, `chat-screen`).
1516

1617
TypeScript Usage:
1718

@@ -30,17 +31,23 @@ Performance Optimization:
3031

3132
UI and Styling:
3233

33-
- Use consistent styling, either through `StyleSheet.create()` or Styled Components.
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.
3435
- Ensure responsive design by considering different screen sizes and orientations.
3536
- Optimize image handling using libraries designed for React Native, like `react-native-fast-image`.
3637

3738
Best Practices:
3839

3940
- Follow React Native's threading model to ensure smooth UI performance.
40-
- Utilize Expo's EAS Build and Updates for continuous deployment and Over-The-Air (OTA) updates.
4141
- Use React Navigation for handling navigation and deep linking with best practices.
4242
- Create and use Jest to test to validate all generated components
4343
- 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.
4451

4552
Additional Rules:
4653

@@ -53,7 +60,6 @@ Additional Rules:
5360
- Use `react-i18next` for internationalization
5461
- Use `react-native-mmkv` for local storage
5562
- Use `axios` for API requests
56-
- Use `gluestack-ui` for UI
5763
- Use `@rnmapbox/maps` for maps, mapping or vehicle navigation
5864
- Use `lucide-react-native` for icons and use those components directly in the markup and don't use the gluestack-ui icon component
5965
- Use ? : for conditional rendering and not &&

.github/copilot-instructions.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Code Style and Structure:
66
- Use functional components and hooks over class components.
77
- Ensure components are modular, reusable, and maintainable.
88
- 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.
910

1011
Naming Conventions:
1112

@@ -40,6 +41,13 @@ Best Practices:
4041
- Use React Navigation for handling navigation and deep linking with best practices.
4142
- Create and use Jest to test to validate all generated components
4243
- 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.
4351

4452
Additional Rules:
4553

app.config.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,14 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
208208
bluetoothAlwaysPermission: 'Allow Resgrid Unit to connect to bluetooth devices',
209209
},
210210
],
211+
[
212+
'expo-navigation-bar',
213+
{
214+
position: 'relative',
215+
visibility: 'hidden',
216+
behavior: 'inset-touch',
217+
},
218+
],
211219
'expo-audio',
212220
'@livekit/react-native-expo-plugin',
213221
'@config-plugins/react-native-webrtc',

docs/audio-stream-refactoring.md

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
# Audio Stream Store Refactoring
2+
3+
## Overview
4+
5+
The audio stream store has been refactored to use `expo-av` instead of `expo-audio` to resolve issues with playing remote MP3 streams over the internet in the new Expo architecture.
6+
7+
## Key Changes
8+
9+
### 1. Replaced expo-audio with expo-av
10+
11+
**Before:**
12+
```typescript
13+
import { type AudioPlayer, createAudioPlayer } from 'expo-audio';
14+
```
15+
16+
**After:**
17+
```typescript
18+
import { Audio, type AVPlaybackSource, type AVPlaybackStatus } from 'expo-av';
19+
```
20+
21+
### 2. Updated Audio Player Management
22+
23+
**Before:**
24+
- Used `createAudioPlayer()` function
25+
- Audio player instance stored as `AudioPlayer`
26+
27+
**After:**
28+
- Uses `Audio.Sound.createAsync()` method
29+
- Audio player instance stored as `Audio.Sound`
30+
31+
### 3. Enhanced Audio Configuration
32+
33+
Added proper audio mode configuration for streaming:
34+
35+
```typescript
36+
await Audio.setAudioModeAsync({
37+
allowsRecordingIOS: false,
38+
staysActiveInBackground: true,
39+
playsInSilentModeIOS: true,
40+
shouldDuckAndroid: true,
41+
playThroughEarpieceAndroid: false,
42+
});
43+
```
44+
45+
### 4. Improved State Management
46+
47+
Added new state properties for better stream status tracking:
48+
49+
```typescript
50+
interface AudioStreamState {
51+
// ... existing properties
52+
isLoading: boolean; // Track loading state
53+
isBuffering: boolean; // Track buffering state
54+
soundObject: Audio.Sound | null; // Sound instance
55+
}
56+
```
57+
58+
### 5. Better Error Handling
59+
60+
Enhanced error handling with proper cleanup and status updates.
61+
62+
## Installation
63+
64+
Make sure you have `expo-av` installed:
65+
66+
```bash
67+
yarn add expo-av
68+
```
69+
70+
## Usage Example
71+
72+
```typescript
73+
import { useAudioStreamStore } from '@/stores/app/audio-stream-store';
74+
75+
const MyComponent = () => {
76+
const {
77+
availableStreams,
78+
isLoadingStreams,
79+
currentStream,
80+
isPlaying,
81+
isLoading,
82+
isBuffering,
83+
fetchAvailableStreams,
84+
playStream,
85+
stopStream,
86+
} = useAudioStreamStore();
87+
88+
useEffect(() => {
89+
fetchAvailableStreams();
90+
}, []);
91+
92+
const handlePlay = async (stream) => {
93+
try {
94+
await playStream(stream);
95+
} catch (error) {
96+
console.error('Failed to play stream:', error);
97+
}
98+
};
99+
100+
// ... render logic
101+
};
102+
```
103+
104+
## Benefits
105+
106+
1. **Better Remote Streaming Support**: `expo-av` provides more robust support for remote MP3 streams
107+
2. **Improved Audio Configuration**: Proper audio mode settings for background playback and silent mode
108+
3. **Enhanced Error Handling**: Better error recovery and cleanup
109+
4. **Loading States**: More granular loading and buffering states for better UX
110+
5. **Memory Management**: Proper cleanup of audio resources
111+
112+
## Migration Notes
113+
114+
If you were using the previous audio stream store:
115+
116+
1. Replace any direct `audioPlayer` references with `soundObject`
117+
2. Update any custom audio handling code to use `expo-av` APIs
118+
3. The store API remains largely the same, so most usage code should work without changes
119+
120+
## Troubleshooting
121+
122+
### Common Issues
123+
124+
1. **Audio not playing on iOS in silent mode**: Make sure `playsInSilentModeIOS: true` is set
125+
2. **Buffering issues**: The store now properly tracks buffering state - use `isBuffering` to show loading indicators
126+
3. **Background playback**: Ensure your app has proper background audio permissions configured
127+
128+
### Audio Permissions
129+
130+
Make sure your app's configuration includes proper audio permissions:
131+
132+
**app.json/app.config.js:**
133+
```json
134+
{
135+
"expo": {
136+
"ios": {
137+
"infoPlist": {
138+
"UIBackgroundModes": ["audio"]
139+
}
140+
},
141+
"android": {
142+
"permissions": [
143+
"android.permission.RECORD_AUDIO"
144+
]
145+
}
146+
}
147+
}
148+
```
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
# NotificationInbox Multi-Select Refactor
2+
3+
## Overview
4+
5+
The NotificationInbox component has been refactored to support multi-select functionality with bulk delete operations. Users can now select multiple notifications and delete them all at once with a confirmation modal.
6+
7+
## Features Added
8+
9+
### 1. Multi-Select Mode
10+
- **Long Press to Enter**: Long press any notification to enter selection mode
11+
- **Visual Indicators**: Selected notifications are highlighted with a blue background and checkmark icon
12+
- **Selection Counter**: Shows the count of selected notifications in the header
13+
14+
### 2. Selection Controls
15+
- **Select All/Deselect All**: Toggle button to select or deselect all notifications at once
16+
- **Individual Toggle**: Tap notifications to toggle their selection state in selection mode
17+
- **Cancel**: Exit selection mode without performing any actions
18+
19+
### 3. Bulk Delete
20+
- **Delete Button**: Trash icon button that's only enabled when notifications are selected
21+
- **Confirmation Modal**: Shows a confirmation dialog before deleting multiple notifications
22+
- **Batch Processing**: Iterates through selected notifications and calls the `deleteMessage` API for each
23+
- **Loading State**: Shows loading indicator while deletion is in progress
24+
- **Error Handling**: Displays appropriate toast messages for success/failure
25+
26+
### 4. Enhanced UI/UX
27+
- **Responsive Header**: Changes layout based on normal vs selection mode
28+
- **Loading States**: Visual feedback during delete operations
29+
- **Toast Notifications**: Success/error messages for user feedback
30+
- **Clean State Management**: Resets selection state when component closes
31+
32+
## Technical Implementation
33+
34+
### New State Variables
35+
```typescript
36+
const [isSelectionMode, setIsSelectionMode] = useState(false);
37+
const [selectedNotificationIds, setSelectedNotificationIds] = useState<Set<string>>(new Set());
38+
const [showDeleteConfirmModal, setShowDeleteConfirmModal] = useState(false);
39+
const [isDeletingSelected, setIsDeletingSelected] = useState(false);
40+
```
41+
42+
### Key Functions
43+
- `enterSelectionMode()`: Enters multi-select mode
44+
- `exitSelectionMode()`: Exits multi-select mode and clears selections
45+
- `toggleNotificationSelection(id)`: Toggles selection state of a notification
46+
- `selectAllNotifications()`: Selects all visible notifications
47+
- `deselectAllNotifications()`: Clears all selections
48+
- `handleBulkDelete()`: Shows confirmation modal for bulk delete
49+
- `confirmBulkDelete()`: Executes the bulk delete operation
50+
51+
### Component Structure
52+
53+
#### Header Modes
54+
1. **Normal Mode**: Shows "Notifications" title with close and menu buttons
55+
2. **Selection Mode**: Shows selection count with Select All, Delete, and Cancel buttons
56+
57+
#### Notification Items
58+
- **Normal Mode**: Tap to view details, long press to enter selection
59+
- **Selection Mode**: Tap to toggle selection, shows checkmark/circle icons
60+
61+
#### Confirmation Modal
62+
- Uses the existing `Modal` component from `@/components/ui/modal`
63+
- Displays count of notifications to be deleted
64+
- Provides Cancel and Delete buttons
65+
66+
## Usage
67+
68+
### Entering Selection Mode
69+
1. Long press any notification item
70+
2. Or tap the menu (three dots) button in the header (future enhancement)
71+
72+
### Selecting Notifications
73+
1. In selection mode, tap individual notifications to toggle selection
74+
2. Use "Select All" to select all visible notifications
75+
3. Use "Deselect All" to clear all selections
76+
77+
### Bulk Delete
78+
1. Select one or more notifications
79+
2. Tap the trash icon button
80+
3. Confirm deletion in the modal dialog
81+
4. Wait for completion and see success/error toast
82+
83+
### Exiting Selection Mode
84+
1. Tap "Cancel" button
85+
2. Complete a bulk delete operation
86+
3. Close the notification inbox
87+
88+
## API Integration
89+
90+
The component uses the existing `deleteMessage` API function:
91+
```typescript
92+
import { deleteMessage } from '@/api/novu/inbox';
93+
```
94+
95+
Each selected notification is deleted individually using Promise.all:
96+
```typescript
97+
const deletePromises = Array.from(selectedNotificationIds).map((id) => deleteMessage(id));
98+
await Promise.all(deletePromises);
99+
```
100+
101+
## Error Handling
102+
103+
- Network errors during deletion show error toast
104+
- Individual notification delete failures are handled gracefully
105+
- Loading states prevent multiple simultaneous operations
106+
- State is properly reset on errors
107+
108+
## Accessibility
109+
110+
- Visual indicators for selection state
111+
- Clear labeling of actions
112+
- Confirmation dialogs for destructive actions
113+
- Loading states for better user feedback
114+
115+
## Testing
116+
117+
Comprehensive test suite includes:
118+
- Component rendering in different states
119+
- Selection mode entry/exit
120+
- Multi-select functionality
121+
- Bulk delete operations
122+
- Error handling
123+
- State management
124+
- API integration
125+
126+
Run tests with:
127+
```bash
128+
npm test -- --testPathPattern="NotificationInbox.test.tsx"
129+
```
130+
131+
## Styling
132+
133+
New styles added:
134+
- `selectedNotificationItem`: Blue background for selected items
135+
- `selectionIndicator`: Spacing for checkmark/circle icons
136+
- `selectionHeader`: Layout for selection mode header
137+
- `selectionCount`: Styling for selection counter
138+
- `selectionActions`: Layout for selection mode buttons
139+
- `headerActions`: Layout for normal mode header buttons
140+
- `actionButton`: Styling for menu button
141+
142+
## Dependencies
143+
144+
- `lucide-react-native`: Added CheckCircle, Circle, MoreVertical, Trash2 icons
145+
- `@/components/ui/modal`: Used for confirmation dialog
146+
- Existing dependencies: Button, Text, animations, etc.
147+
148+
## Future Enhancements
149+
150+
1. **Keyboard Shortcuts**: Add keyboard support for desktop usage
151+
2. **Swipe Actions**: Swipe to delete individual notifications
152+
3. **Filtering**: Select notifications by type or status
153+
4. **Archive Functionality**: Bulk archive instead of delete
154+
5. **Undo Feature**: Allow undoing bulk deletions
155+
6. **Performance**: Virtual scrolling for large notification lists

0 commit comments

Comments
 (0)