From 19f69a6090e9fdfa84a13845bbfb7b6f0162a62b Mon Sep 17 00:00:00 2001 From: zarzet Date: Fri, 10 Jul 2026 04:39:35 +0700 Subject: [PATCH] refactor: dispatch on MusicServices constants instead of raw service ids Service ids ('deezer', 'tidal', 'qobuz', 'spotify', 'amazon', 'local') were compared as raw string literals in every dispatch chain. Introduce MusicServices constants and use them at the comparison/switch sites so a typo becomes a compile error instead of a silently dead branch. --- lib/constants/music_services.dart | 15 ++++++++++++ .../lyrics_provider_priority_page.dart | 5 ++-- lib/screens/track_metadata_actions.dart | 12 +++++----- lib/screens/track_metadata_cards.dart | 24 +++++++++---------- lib/screens/track_metadata_screen.dart | 21 ++++++++-------- lib/utils/provider_resource_ids.dart | 10 ++++---- 6 files changed, 53 insertions(+), 34 deletions(-) create mode 100644 lib/constants/music_services.dart diff --git a/lib/constants/music_services.dart b/lib/constants/music_services.dart new file mode 100644 index 00000000..4851c397 --- /dev/null +++ b/lib/constants/music_services.dart @@ -0,0 +1,15 @@ +/// Canonical service / provider identifiers shared across the app. +/// +/// These values must stay in sync with the provider ids reported by the Go +/// backend and by extensions; use them instead of raw string literals when +/// comparing or dispatching on a service id. +abstract final class MusicServices { + static const spotify = 'spotify'; + static const deezer = 'deezer'; + static const tidal = 'tidal'; + static const qobuz = 'qobuz'; + static const amazon = 'amazon'; + + /// Pseudo-service for tracks that only exist in the local library. + static const local = 'local'; +} diff --git a/lib/screens/settings/lyrics_provider_priority_page.dart b/lib/screens/settings/lyrics_provider_priority_page.dart index f91ad0f4..2137b004 100644 --- a/lib/screens/settings/lyrics_provider_priority_page.dart +++ b/lib/screens/settings/lyrics_provider_priority_page.dart @@ -4,6 +4,7 @@ import 'package:spotiflac_android/l10n/l10n.dart'; import 'package:spotiflac_android/providers/settings_provider.dart'; import 'package:spotiflac_android/widgets/priority_settings_scaffold.dart'; import 'package:spotiflac_android/widgets/settings_group.dart'; +import 'package:spotiflac_android/constants/music_services.dart'; class LyricsProviderPriorityPage extends ConsumerStatefulWidget { const LyricsProviderPriorityPage({super.key}); @@ -215,13 +216,13 @@ class _LyricsProviderPriorityPageState description: context.l10n.lyricsProviderQqMusicDesc, icon: Icons.queue_music, ); - case 'spotify': + case MusicServices.spotify: return _LyricsProviderInfo( name: 'Spotify', description: context.l10n.lyricsProviderExtensionDesc, icon: Icons.graphic_eq, ); - case 'deezer': + case MusicServices.deezer: return _LyricsProviderInfo( name: 'Deezer', description: context.l10n.lyricsProviderExtensionDesc, diff --git a/lib/screens/track_metadata_actions.dart b/lib/screens/track_metadata_actions.dart index bff11828..71a9ac77 100644 --- a/lib/screens/track_metadata_actions.dart +++ b/lib/screens/track_metadata_actions.dart @@ -272,11 +272,11 @@ extension _TrackMetadataFileActions on _TrackMetadataScreenState { IconData _getServiceIcon(String service) { switch (service.toLowerCase()) { - case 'tidal': + case MusicServices.tidal: return Icons.waves; - case 'qobuz': + case MusicServices.qobuz: return Icons.album; - case 'amazon': + case MusicServices.amazon: return Icons.shopping_cart; default: return Icons.cloud_download; @@ -285,11 +285,11 @@ extension _TrackMetadataFileActions on _TrackMetadataScreenState { Color _getServiceColor(String service, ColorScheme colorScheme) { switch (service.toLowerCase()) { - case 'tidal': + case MusicServices.tidal: return const Color(0xFF0077B5); - case 'qobuz': + case MusicServices.qobuz: return const Color(0xFF0052CC); - case 'amazon': + case MusicServices.amazon: return const Color(0xFFFF9900); default: return colorScheme.primary; diff --git a/lib/screens/track_metadata_cards.dart b/lib/screens/track_metadata_cards.dart index 5d3cfbb0..11898e54 100644 --- a/lib/screens/track_metadata_cards.dart +++ b/lib/screens/track_metadata_cards.dart @@ -368,15 +368,15 @@ extension _TrackMetadataCards on _TrackMetadataScreenState { fallbackService: _service.toLowerCase(), ); String buttonLabel; - if (openService == 'deezer') { + if (openService == MusicServices.deezer) { buttonLabel = context.l10n.trackOpenInDeezer; - } else if (openService == 'amazon') { + } else if (openService == MusicServices.amazon) { buttonLabel = context.l10n.trackOpenInService( 'Amazon Music', ); - } else if (openService == 'tidal') { + } else if (openService == MusicServices.tidal) { buttonLabel = context.l10n.trackOpenInService('Tidal'); - } else if (openService == 'qobuz') { + } else if (openService == MusicServices.qobuz) { buttonLabel = context.l10n.trackOpenInService('Qobuz'); } else { buttonLabel = context.l10n.trackOpenInSpotify; @@ -417,19 +417,19 @@ extension _TrackMetadataCards on _TrackMetadataScreenState { Uri? appUri; String serviceName; - if (openService == 'deezer') { + if (openService == MusicServices.deezer) { webUrl = 'https://www.deezer.com/track/$rawId'; appUri = Uri.parse('deezer://www.deezer.com/track/$rawId'); serviceName = 'Deezer'; - } else if (openService == 'amazon') { + } else if (openService == MusicServices.amazon) { webUrl = 'https://music.amazon.com/search/$rawId'; appUri = Uri.parse('amznm://search/$rawId'); serviceName = 'Amazon Music'; - } else if (openService == 'tidal') { + } else if (openService == MusicServices.tidal) { webUrl = 'https://listen.tidal.com/track/$rawId'; appUri = Uri.parse('tidal://track/$rawId'); serviceName = 'Tidal'; - } else if (openService == 'qobuz') { + } else if (openService == MusicServices.qobuz) { webUrl = 'https://play.qobuz.com/track/$rawId'; appUri = Uri.parse('qobuz://track/$rawId'); serviceName = 'Qobuz'; @@ -518,13 +518,13 @@ extension _TrackMetadataCards on _TrackMetadataScreenState { final cleanId = _displayServiceTrackId(_spotifyId!); String idLabel; switch (idService) { - case 'deezer': + case MusicServices.deezer: idLabel = 'Deezer ID'; - case 'amazon': + case MusicServices.amazon: idLabel = 'Amazon ASIN'; - case 'tidal': + case MusicServices.tidal: idLabel = 'Tidal ID'; - case 'qobuz': + case MusicServices.qobuz: idLabel = 'Qobuz ID'; default: idLabel = 'Spotify ID'; diff --git a/lib/screens/track_metadata_screen.dart b/lib/screens/track_metadata_screen.dart index b60490c3..185db9e1 100644 --- a/lib/screens/track_metadata_screen.dart +++ b/lib/screens/track_metadata_screen.dart @@ -30,6 +30,7 @@ import 'package:spotiflac_android/utils/nav_bar_inset.dart'; import 'package:spotiflac_android/widgets/audio_analysis_widget.dart'; import 'package:spotiflac_android/widgets/cached_cover_image.dart'; import 'package:spotiflac_android/widgets/settings_group.dart'; +import 'package:spotiflac_android/constants/music_services.dart'; part 'track_metadata_edit_sheet.dart'; part 'track_metadata_cards.dart'; @@ -787,7 +788,7 @@ class _TrackMetadataScreenState extends ConsumerState { String? get _localCoverPath => _isLocalItem ? _localLibraryItem!.coverPath : null; String? get _spotifyId => _isLocalItem ? null : _downloadItem!.spotifyId; - String get _service => _isLocalItem ? 'local' : _downloadItem!.service; + String get _service => _isLocalItem ? MusicServices.local : _downloadItem!.service; DateTime get _addedAt { if (_isLocalItem) { final modTime = _localLibraryItem!.fileModTime; @@ -915,19 +916,19 @@ class _TrackMetadataScreenState extends ConsumerState { if (raw.isEmpty) return fallbackService; final spotifyTrackIdPattern = RegExp(r'^[A-Za-z0-9]{22}$'); - if (raw.startsWith('deezer:')) return 'deezer'; - if (raw.startsWith('tidal:')) return 'tidal'; - if (raw.startsWith('qobuz:')) return 'qobuz'; - if (raw.startsWith('spotify:')) return 'spotify'; - if (spotifyTrackIdPattern.hasMatch(raw)) return 'spotify'; + if (raw.startsWith('deezer:')) return MusicServices.deezer; + if (raw.startsWith('tidal:')) return MusicServices.tidal; + if (raw.startsWith('qobuz:')) return MusicServices.qobuz; + if (raw.startsWith('spotify:')) return MusicServices.spotify; + if (spotifyTrackIdPattern.hasMatch(raw)) return MusicServices.spotify; final uri = Uri.tryParse(raw); if (uri != null) { final host = uri.host.toLowerCase(); - if (host.contains('spotify.com')) return 'spotify'; - if (host.contains('deezer.com')) return 'deezer'; - if (host.contains('tidal.com')) return 'tidal'; - if (host.contains('qobuz.com')) return 'qobuz'; + if (host.contains('spotify.com')) return MusicServices.spotify; + if (host.contains('deezer.com')) return MusicServices.deezer; + if (host.contains('tidal.com')) return MusicServices.tidal; + if (host.contains('qobuz.com')) return MusicServices.qobuz; } return fallbackService; diff --git a/lib/utils/provider_resource_ids.dart b/lib/utils/provider_resource_ids.dart index 546d6b87..6644afc8 100644 --- a/lib/utils/provider_resource_ids.dart +++ b/lib/utils/provider_resource_ids.dart @@ -1,10 +1,12 @@ +import 'package:spotiflac_android/constants/music_services.dart'; + /// Maps a legacy prefixed resource id (e.g. "deezer:123") to its provider id, /// or null when the value carries no known provider prefix. String? legacyProviderIdFromResourceId(String value) { - if (value.startsWith('deezer:')) return 'deezer'; - if (value.startsWith('qobuz:')) return 'qobuz'; - if (value.startsWith('tidal:')) return 'tidal'; - if (value.startsWith('spotify:')) return 'spotify'; + if (value.startsWith('deezer:')) return MusicServices.deezer; + if (value.startsWith('qobuz:')) return MusicServices.qobuz; + if (value.startsWith('tidal:')) return MusicServices.tidal; + if (value.startsWith('spotify:')) return MusicServices.spotify; return null; }