Skip to content
51 changes: 51 additions & 0 deletions __tests__/UserAgent.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Platform } from 'react-native';
import packageJson from '../package.json';
import { getUserAgent } from '../src/utils/UserAgent';

// Mock Platform from react-native
jest.mock('react-native', () => ({
Platform: {
OS: 'ios',
Version: '17.0',
},
}));

const version = packageJson.version;

describe('getUserAgent', () => {
afterEach(() => {
jest.clearAllMocks();
});

test('should return iOS user agent when platform is iOS', () => {
Platform.OS = 'ios';
Platform.Version = '17.0';

const userAgent = getUserAgent();

expect(userAgent).toBe(`WMBRApp/${version} (iPhone; iOS 17.0)`);
});

test('should return Android user agent when platform is Android', () => {
Platform.OS = 'android';
Platform.Version = 33;

const userAgent = getUserAgent();

expect(userAgent).toBe(`WMBRApp/${version} (Android; SDK 33)`);
});

test('should include correct SDK version for Android', () => {
Platform.OS = 'android';
Platform.Version = 30;

const userAgent = getUserAgent();

expect(userAgent).toContain('SDK 30');
});

test('should always include app version in user agent', () => {
const iosUserAgent = getUserAgent();
expect(iosUserAgent).toContain(`WMBRApp/${version}`);
});
});
2 changes: 2 additions & 0 deletions src/app/Home/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { WmbrRouteName } from '@customTypes/Navigation';
import { DEFAULT_NAME } from '@customTypes/Playlist';
import { COLORS, CORE_COLORS } from '@utils/Colors';
import { formatArchiveDate } from '@utils/DateTime';
import { getUserAgent } from '@utils/UserAgent';

import HomeNowPlaying from './HomeNowPlaying';

Expand Down Expand Up @@ -185,6 +186,7 @@ export default function HomeScreen() {
title: DEFAULT_NAME,
artist: currentShow || 'Live Radio',
artwork: require('../../../assets/cover.png'),
userAgent: getUserAgent(),
});
}

Expand Down
3 changes: 3 additions & 0 deletions src/services/ArchiveService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import TrackPlayer, { Track } from 'react-native-track-player';
import { Show, Archive } from '@customTypes/RecentlyPlayed';
import { debugLog, debugError } from '@utils/Debug';
import { DEFAULT_NAME } from '@customTypes/Playlist';
import { getUserAgent } from '@utils/UserAgent';

export interface ArchivePlaybackState {
isPlayingArchive: boolean;
Expand Down Expand Up @@ -59,6 +60,7 @@ export class ArchiveService {
title: `${show.name} - Archive`,
artist: `${DEFAULT_NAME} - ${archive.date}`,
artwork: require('../../assets/cover.png'),
userAgent: getUserAgent(),
};

// Add and play archive
Expand Down Expand Up @@ -95,6 +97,7 @@ export class ArchiveService {
title: DEFAULT_NAME,
artist: currentShowTitle || 'Live Radio',
artwork: require('../../assets/cover.png'),
userAgent: getUserAgent(),
};

// Add and play live stream
Expand Down
2 changes: 2 additions & 0 deletions src/services/AudioPreviewService.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import TrackPlayer, { Track, State, Event } from 'react-native-track-player';
import { debugError } from '@utils/Debug';
import { getUserAgent } from '@utils/UserAgent';

export interface PreviewState {
isPlaying: boolean;
Expand Down Expand Up @@ -141,6 +142,7 @@ export class AudioPreviewService {
url: url,
title: 'Preview',
artist: 'Apple Music Preview',
userAgent: getUserAgent(),
};

// Reset the queue with just the preview track
Expand Down
22 changes: 22 additions & 0 deletions src/utils/UserAgent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Platform } from 'react-native';
import packageJson from '../../package.json';

/**
* Generates a platform-specific user agent string for HTTP requests.
* This helps servers identify iOS vs Android clients.
*
* Format: WMBRApp/{version} ({platform}; {os})
* Examples:
* - "WMBRApp/0.0.1 (iOS; iPhone)"
* - "WMBRApp/0.0.1 (Android; SDK 33)"
*/
export function getUserAgent(): string {
const version = packageJson.version;
const osVersion = Platform.Version;

if (Platform.OS === 'ios') {
return `WMBRApp/${version} (iPhone; iOS ${osVersion})`;
} else {
return `WMBRApp/${version} (Android; SDK ${osVersion})`;
}
}
2 changes: 2 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
{
"extends": "@react-native/typescript-config",
"compilerOptions": {
"resolveJsonModule": true,
"esModuleInterop": true,
"baseUrl": ".",
"paths": {
"@components/*": ["./src/components/*"],
Expand Down