diff --git a/__tests__/UserAgent.test.ts b/__tests__/UserAgent.test.ts new file mode 100644 index 0000000..dc7e677 --- /dev/null +++ b/__tests__/UserAgent.test.ts @@ -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}`); + }); +}); diff --git a/src/app/Home/index.tsx b/src/app/Home/index.tsx index 4535819..d68782c 100644 --- a/src/app/Home/index.tsx +++ b/src/app/Home/index.tsx @@ -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'; @@ -185,6 +186,7 @@ export default function HomeScreen() { title: DEFAULT_NAME, artist: currentShow || 'Live Radio', artwork: require('../../../assets/cover.png'), + userAgent: getUserAgent(), }); } diff --git a/src/services/ArchiveService.ts b/src/services/ArchiveService.ts index 7ada9c5..d4c91f5 100644 --- a/src/services/ArchiveService.ts +++ b/src/services/ArchiveService.ts @@ -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; @@ -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 @@ -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 diff --git a/src/services/AudioPreviewService.ts b/src/services/AudioPreviewService.ts index b3ab2c4..b221c28 100644 --- a/src/services/AudioPreviewService.ts +++ b/src/services/AudioPreviewService.ts @@ -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; @@ -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 diff --git a/src/utils/UserAgent.ts b/src/utils/UserAgent.ts new file mode 100644 index 0000000..9ffca66 --- /dev/null +++ b/src/utils/UserAgent.ts @@ -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})`; + } +} diff --git a/tsconfig.json b/tsconfig.json index 52b8a38..60e1069 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,8 @@ { "extends": "@react-native/typescript-config", "compilerOptions": { + "resolveJsonModule": true, + "esModuleInterop": true, "baseUrl": ".", "paths": { "@components/*": ["./src/components/*"],