feat: add recent access history, artist screen redesign, and extension improvements

Recent Access History:
- Quick access to recently visited artists, albums, playlists, and tracks
- Tap search bar to show recent access list
- Stays visible after keyboard dismiss, exit with back button
- Persists across app restarts (SharedPreferences)
- X button to remove items, Clear All button for all

Artist Screen Redesign:
- Full-width header image with gradient overlay
- Monthly listeners display with compact notation
- Popular section with top 5 tracks and download status
- Extension artists skip Spotify/Deezer fetch (no rate limit errors)

Go Backend:
- GetArtistWithExtensionJSON now returns top_tracks, header_image, listeners

Bug Fixes:
- Search bar unfocus when tapping outside
- Keyboard not appearing on Settings navigation return
- Recent access artist navigation uses correct screen for extensions
- Extension artist screen correctly parses and forwards top tracks

Localization:
- Added recentPlaylistInfo, errorGeneric strings
- Multi-language support via Crowdin

Extensions:
- YT Music: v1.5.0 (top_tracks in getArtist)
- Spotify Web: v1.6.0
This commit is contained in:
zarzet
2026-01-17 04:29:39 +07:00
parent 9eac6e6e56
commit fc8cfb05d0
29 changed files with 2014 additions and 240 deletions
+48
View File
@@ -995,6 +995,18 @@ abstract class AppLocalizations {
/// **'{count, plural, =1{1 release} other{{count} releases}}'**
String artistReleases(int count);
/// Section header for popular/top tracks
///
/// In en, this message translates to:
/// **'Popular'**
String get artistPopular;
/// Monthly listener count display
///
/// In en, this message translates to:
/// **'{count} monthly listeners'**
String artistMonthlyListeners(String count);
/// Track metadata screen title
///
/// In en, this message translates to:
@@ -3598,6 +3610,42 @@ abstract class AppLocalizations {
/// In en, this message translates to:
/// **'Utility Functions'**
String get utilityFunctions;
/// Recent access item type - artist
///
/// In en, this message translates to:
/// **'Artist'**
String get recentTypeArtist;
/// Recent access item type - album
///
/// In en, this message translates to:
/// **'Album'**
String get recentTypeAlbum;
/// Recent access item type - song/track
///
/// In en, this message translates to:
/// **'Song'**
String get recentTypeSong;
/// Recent access item type - playlist
///
/// In en, this message translates to:
/// **'Playlist'**
String get recentTypePlaylist;
/// Snackbar message when tapping playlist in recent access
///
/// In en, this message translates to:
/// **'Playlist: {name}'**
String recentPlaylistInfo(String name);
/// Generic error message format
///
/// In en, this message translates to:
/// **'Error: {message}'**
String errorGeneric(String message);
}
class _AppLocalizationsDelegate
+30
View File
@@ -513,6 +513,14 @@ class AppLocalizationsDe extends AppLocalizations {
return '$_temp0';
}
@override
String get artistPopular => 'Popular';
@override
String artistMonthlyListeners(String count) {
return '$count monthly listeners';
}
@override
String get trackMetadataTitle => 'Track Info';
@@ -1976,4 +1984,26 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get utilityFunctions => 'Utility Functions';
@override
String get recentTypeArtist => 'Artist';
@override
String get recentTypeAlbum => 'Album';
@override
String get recentTypeSong => 'Song';
@override
String get recentTypePlaylist => 'Playlist';
@override
String recentPlaylistInfo(String name) {
return 'Playlist: $name';
}
@override
String errorGeneric(String message) {
return 'Error: $message';
}
}
+30
View File
@@ -513,6 +513,14 @@ class AppLocalizationsEn extends AppLocalizations {
return '$_temp0';
}
@override
String get artistPopular => 'Popular';
@override
String artistMonthlyListeners(String count) {
return '$count monthly listeners';
}
@override
String get trackMetadataTitle => 'Track Info';
@@ -1976,4 +1984,26 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get utilityFunctions => 'Utility Functions';
@override
String get recentTypeArtist => 'Artist';
@override
String get recentTypeAlbum => 'Album';
@override
String get recentTypeSong => 'Song';
@override
String get recentTypePlaylist => 'Playlist';
@override
String recentPlaylistInfo(String name) {
return 'Playlist: $name';
}
@override
String errorGeneric(String message) {
return 'Error: $message';
}
}
+30
View File
@@ -513,6 +513,14 @@ class AppLocalizationsEs extends AppLocalizations {
return '$_temp0';
}
@override
String get artistPopular => 'Popular';
@override
String artistMonthlyListeners(String count) {
return '$count monthly listeners';
}
@override
String get trackMetadataTitle => 'Track Info';
@@ -1976,4 +1984,26 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get utilityFunctions => 'Utility Functions';
@override
String get recentTypeArtist => 'Artist';
@override
String get recentTypeAlbum => 'Album';
@override
String get recentTypeSong => 'Song';
@override
String get recentTypePlaylist => 'Playlist';
@override
String recentPlaylistInfo(String name) {
return 'Playlist: $name';
}
@override
String errorGeneric(String message) {
return 'Error: $message';
}
}
+30
View File
@@ -513,6 +513,14 @@ class AppLocalizationsFr extends AppLocalizations {
return '$_temp0';
}
@override
String get artistPopular => 'Popular';
@override
String artistMonthlyListeners(String count) {
return '$count monthly listeners';
}
@override
String get trackMetadataTitle => 'Track Info';
@@ -1976,4 +1984,26 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get utilityFunctions => 'Utility Functions';
@override
String get recentTypeArtist => 'Artist';
@override
String get recentTypeAlbum => 'Album';
@override
String get recentTypeSong => 'Song';
@override
String get recentTypePlaylist => 'Playlist';
@override
String recentPlaylistInfo(String name) {
return 'Playlist: $name';
}
@override
String errorGeneric(String message) {
return 'Error: $message';
}
}
+30
View File
@@ -513,6 +513,14 @@ class AppLocalizationsHi extends AppLocalizations {
return '$_temp0';
}
@override
String get artistPopular => 'Popular';
@override
String artistMonthlyListeners(String count) {
return '$count monthly listeners';
}
@override
String get trackMetadataTitle => 'Track Info';
@@ -1976,4 +1984,26 @@ class AppLocalizationsHi extends AppLocalizations {
@override
String get utilityFunctions => 'Utility Functions';
@override
String get recentTypeArtist => 'Artist';
@override
String get recentTypeAlbum => 'Album';
@override
String get recentTypeSong => 'Song';
@override
String get recentTypePlaylist => 'Playlist';
@override
String recentPlaylistInfo(String name) {
return 'Playlist: $name';
}
@override
String errorGeneric(String message) {
return 'Error: $message';
}
}
+30
View File
@@ -518,6 +518,14 @@ class AppLocalizationsId extends AppLocalizations {
return '$_temp0';
}
@override
String get artistPopular => 'Populer';
@override
String artistMonthlyListeners(String count) {
return '$count pendengar bulanan';
}
@override
String get trackMetadataTitle => 'Info Lagu';
@@ -1989,4 +1997,26 @@ class AppLocalizationsId extends AppLocalizations {
@override
String get utilityFunctions => 'Fungsi Utilitas';
@override
String get recentTypeArtist => 'Artis';
@override
String get recentTypeAlbum => 'Album';
@override
String get recentTypeSong => 'Lagu';
@override
String get recentTypePlaylist => 'Playlist';
@override
String recentPlaylistInfo(String name) {
return 'Playlist: $name';
}
@override
String errorGeneric(String message) {
return 'Error: $message';
}
}
+30
View File
@@ -513,6 +513,14 @@ class AppLocalizationsJa extends AppLocalizations {
return '$_temp0';
}
@override
String get artistPopular => 'Popular';
@override
String artistMonthlyListeners(String count) {
return '$count monthly listeners';
}
@override
String get trackMetadataTitle => 'Track Info';
@@ -1976,4 +1984,26 @@ class AppLocalizationsJa extends AppLocalizations {
@override
String get utilityFunctions => 'Utility Functions';
@override
String get recentTypeArtist => 'Artist';
@override
String get recentTypeAlbum => 'Album';
@override
String get recentTypeSong => 'Song';
@override
String get recentTypePlaylist => 'Playlist';
@override
String recentPlaylistInfo(String name) {
return 'Playlist: $name';
}
@override
String errorGeneric(String message) {
return 'Error: $message';
}
}
+30
View File
@@ -513,6 +513,14 @@ class AppLocalizationsKo extends AppLocalizations {
return '$_temp0';
}
@override
String get artistPopular => 'Popular';
@override
String artistMonthlyListeners(String count) {
return '$count monthly listeners';
}
@override
String get trackMetadataTitle => 'Track Info';
@@ -1976,4 +1984,26 @@ class AppLocalizationsKo extends AppLocalizations {
@override
String get utilityFunctions => 'Utility Functions';
@override
String get recentTypeArtist => 'Artist';
@override
String get recentTypeAlbum => 'Album';
@override
String get recentTypeSong => 'Song';
@override
String get recentTypePlaylist => 'Playlist';
@override
String recentPlaylistInfo(String name) {
return 'Playlist: $name';
}
@override
String errorGeneric(String message) {
return 'Error: $message';
}
}
+30
View File
@@ -513,6 +513,14 @@ class AppLocalizationsNl extends AppLocalizations {
return '$_temp0';
}
@override
String get artistPopular => 'Popular';
@override
String artistMonthlyListeners(String count) {
return '$count monthly listeners';
}
@override
String get trackMetadataTitle => 'Track Info';
@@ -1976,4 +1984,26 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get utilityFunctions => 'Utility Functions';
@override
String get recentTypeArtist => 'Artist';
@override
String get recentTypeAlbum => 'Album';
@override
String get recentTypeSong => 'Song';
@override
String get recentTypePlaylist => 'Playlist';
@override
String recentPlaylistInfo(String name) {
return 'Playlist: $name';
}
@override
String errorGeneric(String message) {
return 'Error: $message';
}
}
+30
View File
@@ -513,6 +513,14 @@ class AppLocalizationsPt extends AppLocalizations {
return '$_temp0';
}
@override
String get artistPopular => 'Popular';
@override
String artistMonthlyListeners(String count) {
return '$count monthly listeners';
}
@override
String get trackMetadataTitle => 'Track Info';
@@ -1976,4 +1984,26 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get utilityFunctions => 'Utility Functions';
@override
String get recentTypeArtist => 'Artist';
@override
String get recentTypeAlbum => 'Album';
@override
String get recentTypeSong => 'Song';
@override
String get recentTypePlaylist => 'Playlist';
@override
String recentPlaylistInfo(String name) {
return 'Playlist: $name';
}
@override
String errorGeneric(String message) {
return 'Error: $message';
}
}
+30
View File
@@ -513,6 +513,14 @@ class AppLocalizationsRu extends AppLocalizations {
return '$_temp0';
}
@override
String get artistPopular => 'Popular';
@override
String artistMonthlyListeners(String count) {
return '$count monthly listeners';
}
@override
String get trackMetadataTitle => 'Track Info';
@@ -1976,4 +1984,26 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get utilityFunctions => 'Utility Functions';
@override
String get recentTypeArtist => 'Artist';
@override
String get recentTypeAlbum => 'Album';
@override
String get recentTypeSong => 'Song';
@override
String get recentTypePlaylist => 'Playlist';
@override
String recentPlaylistInfo(String name) {
return 'Playlist: $name';
}
@override
String errorGeneric(String message) {
return 'Error: $message';
}
}
+30
View File
@@ -513,6 +513,14 @@ class AppLocalizationsZh extends AppLocalizations {
return '$_temp0';
}
@override
String get artistPopular => 'Popular';
@override
String artistMonthlyListeners(String count) {
return '$count monthly listeners';
}
@override
String get trackMetadataTitle => 'Track Info';
@@ -1976,6 +1984,28 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get utilityFunctions => 'Utility Functions';
@override
String get recentTypeArtist => 'Artist';
@override
String get recentTypeAlbum => 'Album';
@override
String get recentTypeSong => 'Song';
@override
String get recentTypePlaylist => 'Playlist';
@override
String recentPlaylistInfo(String name) {
return 'Playlist: $name';
}
@override
String errorGeneric(String message) {
return 'Error: $message';
}
}
/// The translations for Chinese, as used in Taiwan (`zh_TW`).
+34 -1
View File
@@ -362,6 +362,15 @@
"count": {"type": "int"}
}
},
"artistPopular": "Popular",
"@artistPopular": {"description": "Section header for popular/top tracks"},
"artistMonthlyListeners": "{count} monthly listeners",
"@artistMonthlyListeners": {
"description": "Monthly listener count display",
"placeholders": {
"count": {"type": "String", "description": "Formatted listener count"}
}
},
"trackMetadataTitle": "Track Info",
"@trackMetadataTitle": {"description": "Track metadata screen title"},
@@ -1459,5 +1468,29 @@
"@downloadedAlbumSelectToDelete": {"description": "Placeholder when nothing selected"},
"utilityFunctions": "Utility Functions",
"@utilityFunctions": {"description": "Extension capability - utility functions"}
"@utilityFunctions": {"description": "Extension capability - utility functions"},
"recentTypeArtist": "Artist",
"@recentTypeArtist": {"description": "Recent access item type - artist"},
"recentTypeAlbum": "Album",
"@recentTypeAlbum": {"description": "Recent access item type - album"},
"recentTypeSong": "Song",
"@recentTypeSong": {"description": "Recent access item type - song/track"},
"recentTypePlaylist": "Playlist",
"@recentTypePlaylist": {"description": "Recent access item type - playlist"},
"recentPlaylistInfo": "Playlist: {name}",
"@recentPlaylistInfo": {
"description": "Snackbar message when tapping playlist in recent access",
"placeholders": {
"name": {"type": "String", "description": "Playlist name"}
}
},
"errorGeneric": "Error: {message}",
"@errorGeneric": {
"description": "Generic error message format",
"placeholders": {
"message": {"type": "String", "description": "Error message"}
}
}
}
+11 -1
View File
@@ -324,6 +324,8 @@
"artistReleases": "{count, plural, =1{1 rilis} other{{count} rilis}}",
"artistCompilations": "Kompilasi",
"artistPopular": "Populer",
"artistMonthlyListeners": "{count} pendengar bulanan",
"tracksHeader": "Lagu",
"downloadAllCount": "Unduh Semua ({count})",
@@ -667,5 +669,13 @@
"folderOrganizationByAlbum": "Berdasarkan Album",
"folderOrganizationByAlbumSubtitle": "Folder terpisah untuk setiap album",
"folderOrganizationByArtistAlbum": "Berdasarkan Artis & Album",
"folderOrganizationByArtistAlbumSubtitle": "Folder bersarang untuk artis dan album"
"folderOrganizationByArtistAlbumSubtitle": "Folder bersarang untuk artis dan album",
"recentTypeArtist": "Artis",
"recentTypeAlbum": "Album",
"recentTypeSong": "Lagu",
"recentTypePlaylist": "Playlist",
"recentPlaylistInfo": "Playlist: {name}",
"errorGeneric": "Error: {message}"
}
+24
View File
@@ -0,0 +1,24 @@
// GENERATED FILE - DO NOT EDIT
// Generated by: dart run tool/check_translations.dart 70
// Only languages with >= 70% translation completion are included.
// Translation is measured by comparing VALUES (not just key existence).
//
// To regenerate, run: dart run tool/check_translations.dart 70
import 'package:flutter/widgets.dart';
/// Minimum translation completion threshold used to filter languages.
const int translationThreshold = 70;
/// List of locales that meet the translation threshold.
/// Only these languages will be available in the app.
const List<Locale> filteredSupportedLocales = <Locale>[
Locale('en'),
Locale('id'),
];
/// Set of locale codes for quick lookup.
const Set<String> filteredLocaleCodes = <String>{
'en',
'id',
};