Skip to content

RE1-T123 Modern push notification sounds#120

Merged
ucswift merged 2 commits into
masterfrom
develop
Jun 30, 2026
Merged

RE1-T123 Modern push notification sounds#120
ucswift merged 2 commits into
masterfrom
develop

Conversation

@ucswift

@ucswift ucswift commented Jun 30, 2026

Copy link
Copy Markdown
Member

Pull Request Description

This PR introduces a "Modern Notification Sounds" preference for Android users, allowing them to toggle between a new set of modern push notification sounds and the classic sounds.

Key changes:

  1. New modern sound assets: 20 new audio files (e.g., moderncallemergency.wav, moderncallhigh.wav, modernnotification.wav, modernmessage.wav) are now bundled with the app.

  2. User setting: A new toggle is added to the Settings screen (Android only) that lets users enable or disable modern notification sounds. The preference is persisted locally via MMKV and defaults to enabled.

  3. Push notification channel integration: The push notification service was refactored to use a declarative channel configuration. When modern sounds are enabled, each standard notification channel plays its corresponding modern sound—including channels (calls, notif, message) that were previously silent. When disabled, channels revert to their classic sounds (or silence for channels that originally had none).

  4. Channel refresh mechanism: Because Android locks a notification channel's sound at creation time, a new refreshNotificationChannelSounds() method deletes and recreates the affected channels whenever the user toggles the preference, ensuring the new sound takes effect immediately.

@Resgrid-Bot

This comment has been minimized.

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@ucswift, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 12 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 05dac4f5-1c6f-47e4-b798-cfaf6e9b8c70

📥 Commits

Reviewing files that changed from the base of the PR and between 503395e and b6b9773.

📒 Files selected for processing (4)
  • src/lib/hooks/__tests__/use-modern-notification-sounds.test.tsx
  • src/lib/hooks/use-modern-notification-sounds.tsx
  • src/services/__tests__/push-notification-channels.test.ts
  • src/services/push-notification.ts
📝 Walkthrough

Walkthrough

Adds an Android-only "Modern Notification Sounds" toggle, backed by a new MMKV-persisted preference hook (useModernNotificationSounds/getModernNotificationSoundsEnabled). The push notification service selects modern or classic channel sounds accordingly and exposes refreshNotificationChannelSounds() to recreate channels on toggle. Includes new sound assets, settings UI wiring, tests, and translations for all locales.

Changes

Modern Notification Sounds

Layer / File(s) Summary
Preference storage hook
src/lib/hooks/use-modern-notification-sounds.tsx, src/lib/hooks/index.tsx, src/lib/hooks/__tests__/use-modern-notification-sounds.test.tsx
New MMKV-backed getter and hook default to true when unset; hook exposes enabled state and setter; re-exported from hooks barrel; tested for default, persistence, and read-back.
Push notification channel sound selection
src/services/push-notification.ts, src/services/__tests__/push-notification.test.ts, src/services/__tests__/push-notification-channels.test.ts
Introduces typed NOTIFICATION_CHANNELS config with modern/classic sounds; setup picks sound based on the preference; new refreshNotificationChannelSounds() deletes and recreates channels with updated sound; both early-return on non-Android; tests cover enabled/disabled scenarios and mock the new delete API.
Settings UI toggle component
src/components/settings/modern-notification-sounds-item.tsx, src/components/settings/__tests__/modern-notification-sounds-item.test.tsx, src/app/(app)/settings.tsx, src/app/(app)/__tests__/settings.test.tsx
New Android-only ModernNotificationSoundsItem toggle component reads/sets the preference and triggers channel refresh on change; rendered in the settings Preferences section; tested for platform rendering and toggle behavior; settings test mock added.
Sound assets and translations
app.config.ts, src/translations/*.json
Adds modern sound WAV assets to the notifications plugin config; adds modern_notification_sounds/modern_notification_sounds_description keys to ar, de, en, es, fr, it, pl, sv, uk locales.

Sequence Diagram(s)

sequenceDiagram
  participant ModernNotificationSoundsItem
  participant useModernNotificationSounds
  participant pushNotificationService
  participant NotificationsAPI

  ModernNotificationSoundsItem->>useModernNotificationSounds: setModernNotificationSoundsEnabled(false)
  useModernNotificationSounds->>useModernNotificationSounds: persist boolean via MMKV
  ModernNotificationSoundsItem->>pushNotificationService: refreshNotificationChannelSounds()
  pushNotificationService->>pushNotificationService: getModernNotificationSoundsEnabled()
  loop for each standard channel
    pushNotificationService->>NotificationsAPI: deleteNotificationChannelAsync(channelId)
    pushNotificationService->>NotificationsAPI: setNotificationChannelAsync(channelId, selectedSound)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Poem

A switch, a hop, a sound reborn,
Modern chimes greet every morn. 🐰🔔
Channels deleted, then rebuilt anew,
Classic or modern — your call, not mine to do.
Across nine tongues the labels sing,
This bunny thumps approval for this little spring!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding modern push notification sounds support.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch develop

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
src/components/settings/modern-notification-sounds-item.tsx (1)

34-43: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Switch lacks an accessible name for screen readers.

The Switch has no accessibilityLabel/accessibilityRole tied to the setting's purpose; screen-reader users only get the visual Text sibling, which isn't programmatically associated with the control.

♿ Suggested fix
-          <Switch size="md" value={isModernNotificationSoundsEnabled} onValueChange={handleToggle} />
+          <Switch
+            size="md"
+            value={isModernNotificationSoundsEnabled}
+            onValueChange={handleToggle}
+            accessibilityLabel={t('settings.modern_notification_sounds')}
+          />

As per coding guidelines, "Ensure the app is accessible, following WCAG guidelines for mobile applications."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/settings/modern-notification-sounds-item.tsx` around lines 34
- 43, The `Switch` in `ModernNotificationSoundsItem` is missing a programmatic
accessible name, so screen readers cannot tell what setting it controls. Update
the `Switch` component to expose the setting purpose directly by adding an
appropriate accessibility label and role, using the existing translated text
from `t('settings.modern_notification_sounds')` as the name. Keep the change
localized to `ModernNotificationSoundsItem` and ensure the label remains
associated with the toggle even when the surrounding `Text` is not read
together.

Source: Coding guidelines

src/components/settings/__tests__/modern-notification-sounds-item.test.tsx (1)

31-57: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Mock component props typed as any.

The Switch, Text, View, and VStack mocks all type their props as any. As per coding guidelines, "Avoid using any; use precise types" applies to **/*.{ts,tsx} files.

🔧 Suggested fix
-  Switch: ({ value, onValueChange }: any) => {
+  Switch: ({ value, onValueChange }: { value: boolean; onValueChange: (v: boolean) => void }) => {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/settings/__tests__/modern-notification-sounds-item.test.tsx`
around lines 31 - 57, The mocked React components in the test file currently
type their props as any, which violates the no-any guideline for TypeScript
tests. Update the mock component signatures for Switch, Text, View, and VStack
to use precise prop types from their real components or minimal inline
interfaces for the specific props they consume, and keep the mock behavior in
the same jest.mock blocks so the test references remain unchanged.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/services/push-notification.ts`:
- Around line 55-62: The notification channel labels and descriptions in
NOTIFICATION_CHANNELS are hardcoded English text, so update the channel config
in push-notification.ts to use i18n via t() from react-i18next instead of
literals. Locate the NOTIFICATION_CHANNELS array and replace each user-visible
name/description with translation keys sourced from the locale files in
src/translations, keeping the channel ids and sound values unchanged.
- Around line 107-114: The standard notification channels in
push-notification.ts are only being updated via createNotificationChannel, so
existing Android channels keep their old sound values when
getModernNotificationSoundsEnabled() first defaults to true. Add a one-time
migration in the same flow that detects first-seen modern sounds and
deletes/recreates the standard channels (using the existing channel loop and
createNotificationChannel helper) before proceeding, so upgraded installs
actually receive the new sound configuration.
- Around line 145-162: `refreshNotificationChannelSounds()` currently catches
channel API failures, logs them, and then resolves, which hides refresh errors
from callers. Update the try/catch in `push-notification.ts` so failures are not
swallowed: after `logger.error(...)`, re-throw the caught error or return an
explicit failure result that the caller can inspect. Keep the existing
`useModernSounds`, `Notifications.deleteNotificationChannelAsync`, and
`createNotificationChannel` flow intact, but ensure the settings toggle’s caller
can detect and handle refresh failures.

---

Nitpick comments:
In `@src/components/settings/__tests__/modern-notification-sounds-item.test.tsx`:
- Around line 31-57: The mocked React components in the test file currently type
their props as any, which violates the no-any guideline for TypeScript tests.
Update the mock component signatures for Switch, Text, View, and VStack to use
precise prop types from their real components or minimal inline interfaces for
the specific props they consume, and keep the mock behavior in the same
jest.mock blocks so the test references remain unchanged.

In `@src/components/settings/modern-notification-sounds-item.tsx`:
- Around line 34-43: The `Switch` in `ModernNotificationSoundsItem` is missing a
programmatic accessible name, so screen readers cannot tell what setting it
controls. Update the `Switch` component to expose the setting purpose directly
by adding an appropriate accessibility label and role, using the existing
translated text from `t('settings.modern_notification_sounds')` as the name.
Keep the change localized to `ModernNotificationSoundsItem` and ensure the label
remains associated with the toggle even when the surrounding `Text` is not read
together.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: baf2ef0d-171b-4b1c-a0a3-a0c27c6e24a1

📥 Commits

Reviewing files that changed from the base of the PR and between 7759a3f and 503395e.

⛔ Files ignored due to path filters (20)
  • assets/audio/modernavailabilityalert.wav is excluded by !**/*.wav
  • assets/audio/moderncalendar.wav is excluded by !**/*.wav
  • assets/audio/moderncallclosed.wav is excluded by !**/*.wav
  • assets/audio/moderncallemergency.wav is excluded by !**/*.wav
  • assets/audio/moderncallhigh.wav is excluded by !**/*.wav
  • assets/audio/moderncalllow.wav is excluded by !**/*.wav
  • assets/audio/moderncallmedium.wav is excluded by !**/*.wav
  • assets/audio/moderncallupdated.wav is excluded by !**/*.wav
  • assets/audio/modernchat.wav is excluded by !**/*.wav
  • assets/audio/modernmessage.wav is excluded by !**/*.wav
  • assets/audio/modernnotification.wav is excluded by !**/*.wav
  • assets/audio/modernpersonnelstatus.wav is excluded by !**/*.wav
  • assets/audio/modernresourceorder.wav is excluded by !**/*.wav
  • assets/audio/modernshift.wav is excluded by !**/*.wav
  • assets/audio/modernstaffing.wav is excluded by !**/*.wav
  • assets/audio/moderntraining.wav is excluded by !**/*.wav
  • assets/audio/moderntroublealert.wav is excluded by !**/*.wav
  • assets/audio/modernunitnotice.wav is excluded by !**/*.wav
  • assets/audio/modernunitstatus.wav is excluded by !**/*.wav
  • assets/audio/modernweatheralert.wav is excluded by !**/*.wav
📒 Files selected for processing (20)
  • app.config.ts
  • src/app/(app)/__tests__/settings.test.tsx
  • src/app/(app)/settings.tsx
  • src/components/settings/__tests__/modern-notification-sounds-item.test.tsx
  • src/components/settings/modern-notification-sounds-item.tsx
  • src/lib/hooks/__tests__/use-modern-notification-sounds.test.tsx
  • src/lib/hooks/index.tsx
  • src/lib/hooks/use-modern-notification-sounds.tsx
  • src/services/__tests__/push-notification-channels.test.ts
  • src/services/__tests__/push-notification.test.ts
  • src/services/push-notification.ts
  • src/translations/ar.json
  • src/translations/de.json
  • src/translations/en.json
  • src/translations/es.json
  • src/translations/fr.json
  • src/translations/it.json
  • src/translations/pl.json
  • src/translations/sv.json
  • src/translations/uk.json

Comment on lines +55 to +62
const NOTIFICATION_CHANNELS: NotificationChannelConfig[] = [
{ id: 'calls', name: 'Generic Call', description: 'Generic Call', modernSound: 'modernnotification' },
{ id: '0', name: 'Emergency Call', description: 'Emergency Call', modernSound: 'moderncallemergency', classicSound: 'callemergency' },
{ id: '1', name: 'High Call', description: 'High Call', modernSound: 'moderncallhigh', classicSound: 'callhigh' },
{ id: '2', name: 'Medium Call', description: 'Medium Call', modernSound: 'moderncallmedium', classicSound: 'callmedium' },
{ id: '3', name: 'Low Call', description: 'Low Call', modernSound: 'moderncalllow', classicSound: 'calllow' },
{ id: 'notif', name: 'Notification', description: 'Notifications', modernSound: 'modernnotification', vibration: false },
{ id: 'message', name: 'Message', description: 'Messages', modernSound: 'modernmessage', vibration: false },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Localize the channel labels and descriptions.

These strings show up in Android’s notification settings, so hardcoded English here bypasses the locale files added in this PR. Please source them from i18n instead of embedding literals. As per coding guidelines, "All user-visible text must be wrapped in t() from react-i18next" and "Ensure all text is wrapped in t() from react-i18next for translations with the dictionary files stored in src/translations".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services/push-notification.ts` around lines 55 - 62, The notification
channel labels and descriptions in NOTIFICATION_CHANNELS are hardcoded English
text, so update the channel config in push-notification.ts to use i18n via t()
from react-i18next instead of literals. Locate the NOTIFICATION_CHANNELS array
and replace each user-visible name/description with translation keys sourced
from the locale files in src/translations, keeping the channel ids and sound
values unchanged.

Source: Coding guidelines

Comment thread src/services/push-notification.ts
Comment on lines +145 to +162
try {
const useModernSounds = getModernNotificationSoundsEnabled();

for (const channel of NOTIFICATION_CHANNELS) {
await Notifications.deleteNotificationChannelAsync(channel.id);
const sound = useModernSounds ? channel.modernSound : channel.classicSound;
await this.createNotificationChannel(channel.id, channel.name, channel.description, sound, channel.vibration ?? true);
}

logger.info({
message: 'Android notification channel sounds refreshed',
context: { useModernSounds },
});
} catch (error) {
logger.error({
message: 'Error refreshing Android notification channel sounds',
context: { error },
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Don’t swallow refresh failures.

refreshNotificationChannelSounds() logs and resolves on error. The settings toggle persists the preference before calling this method, so any channel API failure leaves MMKV and the actual Android channel sounds out of sync with no way to revert or notify the user. Re-throw here (or return an explicit failure result) so the caller can handle it. As per coding guidelines, "Handle errors gracefully and provide user feedback".

Minimal service-side change
     } catch (error) {
       logger.error({
         message: 'Error refreshing Android notification channel sounds',
         context: { error },
       });
+      throw error;
     }
📝 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
try {
const useModernSounds = getModernNotificationSoundsEnabled();
for (const channel of NOTIFICATION_CHANNELS) {
await Notifications.deleteNotificationChannelAsync(channel.id);
const sound = useModernSounds ? channel.modernSound : channel.classicSound;
await this.createNotificationChannel(channel.id, channel.name, channel.description, sound, channel.vibration ?? true);
}
logger.info({
message: 'Android notification channel sounds refreshed',
context: { useModernSounds },
});
} catch (error) {
logger.error({
message: 'Error refreshing Android notification channel sounds',
context: { error },
});
try {
const useModernSounds = getModernNotificationSoundsEnabled();
for (const channel of NOTIFICATION_CHANNELS) {
await Notifications.deleteNotificationChannelAsync(channel.id);
const sound = useModernSounds ? channel.modernSound : channel.classicSound;
await this.createNotificationChannel(channel.id, channel.name, channel.description, sound, channel.vibration ?? true);
}
logger.info({
message: 'Android notification channel sounds refreshed',
context: { useModernSounds },
});
} catch (error) {
logger.error({
message: 'Error refreshing Android notification channel sounds',
context: { error },
});
throw error;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services/push-notification.ts` around lines 145 - 162,
`refreshNotificationChannelSounds()` currently catches channel API failures,
logs them, and then resolves, which hides refresh errors from callers. Update
the try/catch in `push-notification.ts` so failures are not swallowed: after
`logger.error(...)`, re-throw the caught error or return an explicit failure
result that the caller can inspect. Keep the existing `useModernSounds`,
`Notifications.deleteNotificationChannelAsync`, and `createNotificationChannel`
flow intact, but ensure the settings toggle’s caller can detect and handle
refresh failures.

Source: Coding guidelines

@Resgrid-Bot

Resgrid-Bot commented Jun 30, 2026

Copy link
Copy Markdown

Kody Review Complete

Great news! 🎉
No issues were found that match your current review configurations.

Keep up the excellent work! 🚀

Kody Guide: Usage and Configuration
Interacting with Kody
  • Request a Review: Ask Kody to review your PR manually by adding a comment with the @kody start-review command at the root of your PR.

  • Validate Business Logic: Ask Kody to validate your code against business rules by adding a comment with the @kody -v business-logic command.

  • Provide Feedback: Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

Current Kody Configuration
Review Options

The following review options are enabled or disabled:

Options Enabled
Bug
Performance
Security
Business Logic

Access your configuration settings here.

@ucswift

ucswift commented Jun 30, 2026

Copy link
Copy Markdown
Member Author

Approve

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is approved.

@ucswift ucswift merged commit a6e28b6 into master Jun 30, 2026
13 of 14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants