Trainees choose which kinds of notification they receive. The screen is at /notification-preferences, opened from the profile tab. The code is src/features/notification-preferences.

Endpoint

Both calls use /v1/trainee/notification-preferences.
One PATCH changes one group.

Groups

NotificationPreferenceGroup has six values. The screen maps each to an icon and two strings in GROUP_COPY. The screen filters the server list to keys it has copy for:
So a group added on the server later is hidden by older app builds instead of crashing them. Adding a group to the app means adding it to the union type, to GROUP_COPY, and to all six locale files. The order on screen is the order the server returns.

Locked groups

A group with locked: true renders a disabled switch. The app does not decide which groups are locked. It only honours the flag.

Saving

useSaveNotificationPreference() is an optimistic mutation.
  1. onMutate cancels in-flight fetches for ['notification-preferences'] and flips the group in the cache, so the switch moves at once.
  2. The mutation has a fixed scope id, so several quick toggles are sent one after another, in order.
  3. onSettled checks queryClient.isMutating for the same mutation key. If another save is still queued it does nothing. Otherwise it invalidates ['notification-preferences'] and ['notification-configs'].
  4. On error the screen shows the notificationPrefsSaveFailed toast. The invalidation in onSettled then restores the real server state.
Invalidating ['notification-configs'] matters for the WORKOUT_REMINDERS group. Those reminders are scheduled on the device by useWorkoutReminders, which reads its on and off state from that query. See Notifications.

Push permission card

Group switches mean nothing if the operating system blocks notifications. usePushPermission() wraps getPushPermissionState() in a query with staleTime: 0 and refetches every time the app returns to the foreground. When permission is readable and not granted, the screen shows a card above the list with the pushDisabledBanner text and one button: The card does not hide the group list. The trainee can still set preferences while push is off.

Screen states

Pull to refresh refetches both the preferences and the permission state, and replays the entrance animation through useReplayAnimation. Each switch sets accessibilityLabel to the group title and accessibilityHint to its hint.

What is stored where

Nothing is stored on the device. Preferences live on the server and are fetched fresh on each visit.