mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-08-18 08:57:13 +02:00
feat: polish search empty state and share caching
This commit is contained in:
@@ -1404,6 +1404,12 @@ abstract class AppLocalizations {
|
||||
/// **'No tracks found'**
|
||||
String get errorNoTracksFound;
|
||||
|
||||
/// Subtitle shown under the empty search result state on the home screen
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Try another keyword'**
|
||||
String get searchEmptyResultSubtitle;
|
||||
|
||||
/// Error title - URL not handled by any extension or service
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
@@ -7040,7 +7046,7 @@ abstract class AppLocalizations {
|
||||
/// Title and tooltip for finding the current collection in other services
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Open in other services'**
|
||||
/// **'Open in Other Services'**
|
||||
String get openInOtherServices;
|
||||
|
||||
/// Empty state when no extensions can be searched for cross-service links
|
||||
@@ -7058,7 +7064,7 @@ abstract class AppLocalizations {
|
||||
/// Tooltip for copying a cross-service link
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Copy link'**
|
||||
/// **'Copy Link'**
|
||||
String get shareSheetCopyLink;
|
||||
|
||||
/// Snackbar after copying a cross-service link
|
||||
@@ -7066,12 +7072,6 @@ abstract class AppLocalizations {
|
||||
/// In en, this message translates to:
|
||||
/// **'{service} link copied'**
|
||||
String shareSheetLinkCopied(Object service);
|
||||
|
||||
/// Tooltip for opening a cross-service link inside the app
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Open'**
|
||||
String get shareSheetOpen;
|
||||
}
|
||||
|
||||
class _AppLocalizationsDelegate
|
||||
|
||||
@@ -754,6 +754,9 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
@override
|
||||
String get errorNoTracksFound => 'Keine Titel gefunden';
|
||||
|
||||
@override
|
||||
String get searchEmptyResultSubtitle => 'Try another keyword';
|
||||
|
||||
@override
|
||||
String get errorUrlNotRecognized => 'Link wurde nicht erkannt';
|
||||
|
||||
@@ -4256,7 +4259,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
String get dialogDisableAndClear => 'Turn off and clear';
|
||||
|
||||
@override
|
||||
String get openInOtherServices => 'Open in other services';
|
||||
String get openInOtherServices => 'Open in Other Services';
|
||||
|
||||
@override
|
||||
String get shareSheetNoExtensions => 'No other compatible services';
|
||||
@@ -4265,13 +4268,10 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
String get shareSheetNotFound => 'Not found';
|
||||
|
||||
@override
|
||||
String get shareSheetCopyLink => 'Copy link';
|
||||
String get shareSheetCopyLink => 'Copy Link';
|
||||
|
||||
@override
|
||||
String shareSheetLinkCopied(Object service) {
|
||||
return '$service link copied';
|
||||
}
|
||||
|
||||
@override
|
||||
String get shareSheetOpen => 'Open';
|
||||
}
|
||||
|
||||
@@ -742,6 +742,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
@override
|
||||
String get errorNoTracksFound => 'No tracks found';
|
||||
|
||||
@override
|
||||
String get searchEmptyResultSubtitle => 'Try another keyword';
|
||||
|
||||
@override
|
||||
String get errorUrlNotRecognized => 'Link not recognized';
|
||||
|
||||
@@ -4227,7 +4230,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
String get dialogDisableAndClear => 'Turn off and clear';
|
||||
|
||||
@override
|
||||
String get openInOtherServices => 'Open in other services';
|
||||
String get openInOtherServices => 'Open in Other Services';
|
||||
|
||||
@override
|
||||
String get shareSheetNoExtensions => 'No other compatible services';
|
||||
@@ -4236,13 +4239,10 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
String get shareSheetNotFound => 'Not found';
|
||||
|
||||
@override
|
||||
String get shareSheetCopyLink => 'Copy link';
|
||||
String get shareSheetCopyLink => 'Copy Link';
|
||||
|
||||
@override
|
||||
String shareSheetLinkCopied(Object service) {
|
||||
return '$service link copied';
|
||||
}
|
||||
|
||||
@override
|
||||
String get shareSheetOpen => 'Open';
|
||||
}
|
||||
|
||||
@@ -742,6 +742,9 @@ class AppLocalizationsEs extends AppLocalizations {
|
||||
@override
|
||||
String get errorNoTracksFound => 'No tracks found';
|
||||
|
||||
@override
|
||||
String get searchEmptyResultSubtitle => 'Try another keyword';
|
||||
|
||||
@override
|
||||
String get errorUrlNotRecognized => 'Link not recognized';
|
||||
|
||||
@@ -4221,7 +4224,7 @@ class AppLocalizationsEs extends AppLocalizations {
|
||||
String get dialogDisableAndClear => 'Turn off and clear';
|
||||
|
||||
@override
|
||||
String get openInOtherServices => 'Open in other services';
|
||||
String get openInOtherServices => 'Open in Other Services';
|
||||
|
||||
@override
|
||||
String get shareSheetNoExtensions => 'No other compatible services';
|
||||
@@ -4230,15 +4233,12 @@ class AppLocalizationsEs extends AppLocalizations {
|
||||
String get shareSheetNotFound => 'Not found';
|
||||
|
||||
@override
|
||||
String get shareSheetCopyLink => 'Copy link';
|
||||
String get shareSheetCopyLink => 'Copy Link';
|
||||
|
||||
@override
|
||||
String shareSheetLinkCopied(Object service) {
|
||||
return '$service link copied';
|
||||
}
|
||||
|
||||
@override
|
||||
String get shareSheetOpen => 'Open';
|
||||
}
|
||||
|
||||
/// The translations for Spanish Castilian, as used in Spain (`es_ES`).
|
||||
|
||||
@@ -745,6 +745,9 @@ class AppLocalizationsFr extends AppLocalizations {
|
||||
@override
|
||||
String get errorNoTracksFound => 'No tracks found';
|
||||
|
||||
@override
|
||||
String get searchEmptyResultSubtitle => 'Try another keyword';
|
||||
|
||||
@override
|
||||
String get errorUrlNotRecognized => 'Link not recognized';
|
||||
|
||||
@@ -4225,7 +4228,7 @@ class AppLocalizationsFr extends AppLocalizations {
|
||||
String get dialogDisableAndClear => 'Turn off and clear';
|
||||
|
||||
@override
|
||||
String get openInOtherServices => 'Open in other services';
|
||||
String get openInOtherServices => 'Open in Other Services';
|
||||
|
||||
@override
|
||||
String get shareSheetNoExtensions => 'No other compatible services';
|
||||
@@ -4234,13 +4237,10 @@ class AppLocalizationsFr extends AppLocalizations {
|
||||
String get shareSheetNotFound => 'Not found';
|
||||
|
||||
@override
|
||||
String get shareSheetCopyLink => 'Copy link';
|
||||
String get shareSheetCopyLink => 'Copy Link';
|
||||
|
||||
@override
|
||||
String shareSheetLinkCopied(Object service) {
|
||||
return '$service link copied';
|
||||
}
|
||||
|
||||
@override
|
||||
String get shareSheetOpen => 'Open';
|
||||
}
|
||||
|
||||
@@ -742,6 +742,9 @@ class AppLocalizationsHi extends AppLocalizations {
|
||||
@override
|
||||
String get errorNoTracksFound => 'No tracks found';
|
||||
|
||||
@override
|
||||
String get searchEmptyResultSubtitle => 'Try another keyword';
|
||||
|
||||
@override
|
||||
String get errorUrlNotRecognized => 'Link not recognized';
|
||||
|
||||
@@ -4222,7 +4225,7 @@ class AppLocalizationsHi extends AppLocalizations {
|
||||
String get dialogDisableAndClear => 'Turn off and clear';
|
||||
|
||||
@override
|
||||
String get openInOtherServices => 'Open in other services';
|
||||
String get openInOtherServices => 'Open in Other Services';
|
||||
|
||||
@override
|
||||
String get shareSheetNoExtensions => 'No other compatible services';
|
||||
@@ -4231,13 +4234,10 @@ class AppLocalizationsHi extends AppLocalizations {
|
||||
String get shareSheetNotFound => 'Not found';
|
||||
|
||||
@override
|
||||
String get shareSheetCopyLink => 'Copy link';
|
||||
String get shareSheetCopyLink => 'Copy Link';
|
||||
|
||||
@override
|
||||
String shareSheetLinkCopied(Object service) {
|
||||
return '$service link copied';
|
||||
}
|
||||
|
||||
@override
|
||||
String get shareSheetOpen => 'Open';
|
||||
}
|
||||
|
||||
@@ -745,6 +745,9 @@ class AppLocalizationsId extends AppLocalizations {
|
||||
@override
|
||||
String get errorNoTracksFound => 'Tidak ada lagu ditemukan';
|
||||
|
||||
@override
|
||||
String get searchEmptyResultSubtitle => 'Coba kata kunci lain';
|
||||
|
||||
@override
|
||||
String get errorUrlNotRecognized => 'Tautan tidak dikenali';
|
||||
|
||||
@@ -4213,7 +4216,7 @@ class AppLocalizationsId extends AppLocalizations {
|
||||
String get dialogDisableAndClear => 'Matikan dan hapus';
|
||||
|
||||
@override
|
||||
String get openInOtherServices => 'Buka di layanan lain';
|
||||
String get openInOtherServices => 'Buka di Layanan Lain';
|
||||
|
||||
@override
|
||||
String get shareSheetNoExtensions => 'Tidak ada layanan lain yang kompatibel';
|
||||
@@ -4222,13 +4225,10 @@ class AppLocalizationsId extends AppLocalizations {
|
||||
String get shareSheetNotFound => 'Tidak ditemukan';
|
||||
|
||||
@override
|
||||
String get shareSheetCopyLink => 'Salin tautan';
|
||||
String get shareSheetCopyLink => 'Salin Tautan';
|
||||
|
||||
@override
|
||||
String shareSheetLinkCopied(Object service) {
|
||||
return 'Tautan $service disalin';
|
||||
}
|
||||
|
||||
@override
|
||||
String get shareSheetOpen => 'Buka';
|
||||
}
|
||||
|
||||
@@ -737,6 +737,9 @@ class AppLocalizationsJa extends AppLocalizations {
|
||||
@override
|
||||
String get errorNoTracksFound => 'トラックがありません';
|
||||
|
||||
@override
|
||||
String get searchEmptyResultSubtitle => 'Try another keyword';
|
||||
|
||||
@override
|
||||
String get errorUrlNotRecognized => 'Link not recognized';
|
||||
|
||||
@@ -4209,7 +4212,7 @@ class AppLocalizationsJa extends AppLocalizations {
|
||||
String get dialogDisableAndClear => 'Turn off and clear';
|
||||
|
||||
@override
|
||||
String get openInOtherServices => 'Open in other services';
|
||||
String get openInOtherServices => 'Open in Other Services';
|
||||
|
||||
@override
|
||||
String get shareSheetNoExtensions => 'No other compatible services';
|
||||
@@ -4218,13 +4221,10 @@ class AppLocalizationsJa extends AppLocalizations {
|
||||
String get shareSheetNotFound => 'Not found';
|
||||
|
||||
@override
|
||||
String get shareSheetCopyLink => 'Copy link';
|
||||
String get shareSheetCopyLink => 'Copy Link';
|
||||
|
||||
@override
|
||||
String shareSheetLinkCopied(Object service) {
|
||||
return '$service link copied';
|
||||
}
|
||||
|
||||
@override
|
||||
String get shareSheetOpen => 'Open';
|
||||
}
|
||||
|
||||
@@ -724,6 +724,9 @@ class AppLocalizationsKo extends AppLocalizations {
|
||||
@override
|
||||
String get errorNoTracksFound => '트랙을 찾을 수 없습니다';
|
||||
|
||||
@override
|
||||
String get searchEmptyResultSubtitle => 'Try another keyword';
|
||||
|
||||
@override
|
||||
String get errorUrlNotRecognized => 'Link not recognized';
|
||||
|
||||
@@ -4202,7 +4205,7 @@ class AppLocalizationsKo extends AppLocalizations {
|
||||
String get dialogDisableAndClear => 'Turn off and clear';
|
||||
|
||||
@override
|
||||
String get openInOtherServices => 'Open in other services';
|
||||
String get openInOtherServices => 'Open in Other Services';
|
||||
|
||||
@override
|
||||
String get shareSheetNoExtensions => 'No other compatible services';
|
||||
@@ -4211,13 +4214,10 @@ class AppLocalizationsKo extends AppLocalizations {
|
||||
String get shareSheetNotFound => 'Not found';
|
||||
|
||||
@override
|
||||
String get shareSheetCopyLink => 'Copy link';
|
||||
String get shareSheetCopyLink => 'Copy Link';
|
||||
|
||||
@override
|
||||
String shareSheetLinkCopied(Object service) {
|
||||
return '$service link copied';
|
||||
}
|
||||
|
||||
@override
|
||||
String get shareSheetOpen => 'Open';
|
||||
}
|
||||
|
||||
@@ -742,6 +742,9 @@ class AppLocalizationsNl extends AppLocalizations {
|
||||
@override
|
||||
String get errorNoTracksFound => 'No tracks found';
|
||||
|
||||
@override
|
||||
String get searchEmptyResultSubtitle => 'Try another keyword';
|
||||
|
||||
@override
|
||||
String get errorUrlNotRecognized => 'Link not recognized';
|
||||
|
||||
@@ -4222,7 +4225,7 @@ class AppLocalizationsNl extends AppLocalizations {
|
||||
String get dialogDisableAndClear => 'Turn off and clear';
|
||||
|
||||
@override
|
||||
String get openInOtherServices => 'Open in other services';
|
||||
String get openInOtherServices => 'Open in Other Services';
|
||||
|
||||
@override
|
||||
String get shareSheetNoExtensions => 'No other compatible services';
|
||||
@@ -4231,13 +4234,10 @@ class AppLocalizationsNl extends AppLocalizations {
|
||||
String get shareSheetNotFound => 'Not found';
|
||||
|
||||
@override
|
||||
String get shareSheetCopyLink => 'Copy link';
|
||||
String get shareSheetCopyLink => 'Copy Link';
|
||||
|
||||
@override
|
||||
String shareSheetLinkCopied(Object service) {
|
||||
return '$service link copied';
|
||||
}
|
||||
|
||||
@override
|
||||
String get shareSheetOpen => 'Open';
|
||||
}
|
||||
|
||||
@@ -742,6 +742,9 @@ class AppLocalizationsPt extends AppLocalizations {
|
||||
@override
|
||||
String get errorNoTracksFound => 'No tracks found';
|
||||
|
||||
@override
|
||||
String get searchEmptyResultSubtitle => 'Try another keyword';
|
||||
|
||||
@override
|
||||
String get errorUrlNotRecognized => 'Link not recognized';
|
||||
|
||||
@@ -4221,7 +4224,7 @@ class AppLocalizationsPt extends AppLocalizations {
|
||||
String get dialogDisableAndClear => 'Turn off and clear';
|
||||
|
||||
@override
|
||||
String get openInOtherServices => 'Open in other services';
|
||||
String get openInOtherServices => 'Open in Other Services';
|
||||
|
||||
@override
|
||||
String get shareSheetNoExtensions => 'No other compatible services';
|
||||
@@ -4230,15 +4233,12 @@ class AppLocalizationsPt extends AppLocalizations {
|
||||
String get shareSheetNotFound => 'Not found';
|
||||
|
||||
@override
|
||||
String get shareSheetCopyLink => 'Copy link';
|
||||
String get shareSheetCopyLink => 'Copy Link';
|
||||
|
||||
@override
|
||||
String shareSheetLinkCopied(Object service) {
|
||||
return '$service link copied';
|
||||
}
|
||||
|
||||
@override
|
||||
String get shareSheetOpen => 'Open';
|
||||
}
|
||||
|
||||
/// The translations for Portuguese, as used in Portugal (`pt_PT`).
|
||||
|
||||
@@ -755,6 +755,9 @@ class AppLocalizationsRu extends AppLocalizations {
|
||||
@override
|
||||
String get errorNoTracksFound => 'Треки не найдены';
|
||||
|
||||
@override
|
||||
String get searchEmptyResultSubtitle => 'Try another keyword';
|
||||
|
||||
@override
|
||||
String get errorUrlNotRecognized => 'Ссылка не распознана';
|
||||
|
||||
@@ -4281,7 +4284,7 @@ class AppLocalizationsRu extends AppLocalizations {
|
||||
String get dialogDisableAndClear => 'Turn off and clear';
|
||||
|
||||
@override
|
||||
String get openInOtherServices => 'Open in other services';
|
||||
String get openInOtherServices => 'Open in Other Services';
|
||||
|
||||
@override
|
||||
String get shareSheetNoExtensions => 'No other compatible services';
|
||||
@@ -4290,13 +4293,10 @@ class AppLocalizationsRu extends AppLocalizations {
|
||||
String get shareSheetNotFound => 'Not found';
|
||||
|
||||
@override
|
||||
String get shareSheetCopyLink => 'Copy link';
|
||||
String get shareSheetCopyLink => 'Copy Link';
|
||||
|
||||
@override
|
||||
String shareSheetLinkCopied(Object service) {
|
||||
return '$service link copied';
|
||||
}
|
||||
|
||||
@override
|
||||
String get shareSheetOpen => 'Open';
|
||||
}
|
||||
|
||||
@@ -750,6 +750,9 @@ class AppLocalizationsTr extends AppLocalizations {
|
||||
@override
|
||||
String get errorNoTracksFound => 'Parça bulunamadı';
|
||||
|
||||
@override
|
||||
String get searchEmptyResultSubtitle => 'Try another keyword';
|
||||
|
||||
@override
|
||||
String get errorUrlNotRecognized => 'Bağlantı tanınamadı';
|
||||
|
||||
@@ -4248,7 +4251,7 @@ class AppLocalizationsTr extends AppLocalizations {
|
||||
String get dialogDisableAndClear => 'Turn off and clear';
|
||||
|
||||
@override
|
||||
String get openInOtherServices => 'Open in other services';
|
||||
String get openInOtherServices => 'Open in Other Services';
|
||||
|
||||
@override
|
||||
String get shareSheetNoExtensions => 'No other compatible services';
|
||||
@@ -4257,13 +4260,10 @@ class AppLocalizationsTr extends AppLocalizations {
|
||||
String get shareSheetNotFound => 'Not found';
|
||||
|
||||
@override
|
||||
String get shareSheetCopyLink => 'Copy link';
|
||||
String get shareSheetCopyLink => 'Copy Link';
|
||||
|
||||
@override
|
||||
String shareSheetLinkCopied(Object service) {
|
||||
return '$service link copied';
|
||||
}
|
||||
|
||||
@override
|
||||
String get shareSheetOpen => 'Open';
|
||||
}
|
||||
|
||||
@@ -755,6 +755,9 @@ class AppLocalizationsUk extends AppLocalizations {
|
||||
@override
|
||||
String get errorNoTracksFound => 'Треків не знайдено';
|
||||
|
||||
@override
|
||||
String get searchEmptyResultSubtitle => 'Try another keyword';
|
||||
|
||||
@override
|
||||
String get errorUrlNotRecognized => 'Посилання не розпізнано';
|
||||
|
||||
@@ -4281,7 +4284,7 @@ class AppLocalizationsUk extends AppLocalizations {
|
||||
String get dialogDisableAndClear => 'Turn off and clear';
|
||||
|
||||
@override
|
||||
String get openInOtherServices => 'Open in other services';
|
||||
String get openInOtherServices => 'Open in Other Services';
|
||||
|
||||
@override
|
||||
String get shareSheetNoExtensions => 'No other compatible services';
|
||||
@@ -4290,13 +4293,10 @@ class AppLocalizationsUk extends AppLocalizations {
|
||||
String get shareSheetNotFound => 'Not found';
|
||||
|
||||
@override
|
||||
String get shareSheetCopyLink => 'Copy link';
|
||||
String get shareSheetCopyLink => 'Copy Link';
|
||||
|
||||
@override
|
||||
String shareSheetLinkCopied(Object service) {
|
||||
return '$service link copied';
|
||||
}
|
||||
|
||||
@override
|
||||
String get shareSheetOpen => 'Open';
|
||||
}
|
||||
|
||||
@@ -742,6 +742,9 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||
@override
|
||||
String get errorNoTracksFound => 'No tracks found';
|
||||
|
||||
@override
|
||||
String get searchEmptyResultSubtitle => 'Try another keyword';
|
||||
|
||||
@override
|
||||
String get errorUrlNotRecognized => 'Link not recognized';
|
||||
|
||||
@@ -4221,7 +4224,7 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||
String get dialogDisableAndClear => 'Turn off and clear';
|
||||
|
||||
@override
|
||||
String get openInOtherServices => 'Open in other services';
|
||||
String get openInOtherServices => 'Open in Other Services';
|
||||
|
||||
@override
|
||||
String get shareSheetNoExtensions => 'No other compatible services';
|
||||
@@ -4230,15 +4233,12 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||
String get shareSheetNotFound => 'Not found';
|
||||
|
||||
@override
|
||||
String get shareSheetCopyLink => 'Copy link';
|
||||
String get shareSheetCopyLink => 'Copy Link';
|
||||
|
||||
@override
|
||||
String shareSheetLinkCopied(Object service) {
|
||||
return '$service link copied';
|
||||
}
|
||||
|
||||
@override
|
||||
String get shareSheetOpen => 'Open';
|
||||
}
|
||||
|
||||
/// The translations for Chinese, as used in China (`zh_CN`).
|
||||
|
||||
@@ -961,6 +961,10 @@
|
||||
"@errorNoTracksFound": {
|
||||
"description": "Error - search returned no results"
|
||||
},
|
||||
"searchEmptyResultSubtitle": "Try another keyword",
|
||||
"@searchEmptyResultSubtitle": {
|
||||
"description": "Subtitle shown under the empty search result state on the home screen"
|
||||
},
|
||||
"errorUrlNotRecognized": "Link not recognized",
|
||||
"@errorUrlNotRecognized": {
|
||||
"description": "Error title - URL not handled by any extension or service"
|
||||
@@ -5528,7 +5532,7 @@
|
||||
"@dialogDisableAndClear": {
|
||||
"description": "Confirmation action to disable download history and clear existing entries"
|
||||
},
|
||||
"openInOtherServices": "Open in other services",
|
||||
"openInOtherServices": "Open in Other Services",
|
||||
"@openInOtherServices": {
|
||||
"description": "Title and tooltip for finding the current collection in other services"
|
||||
},
|
||||
@@ -5540,7 +5544,7 @@
|
||||
"@shareSheetNotFound": {
|
||||
"description": "Cross-service share sheet row subtitle when a service has no match"
|
||||
},
|
||||
"shareSheetCopyLink": "Copy link",
|
||||
"shareSheetCopyLink": "Copy Link",
|
||||
"@shareSheetCopyLink": {
|
||||
"description": "Tooltip for copying a cross-service link"
|
||||
},
|
||||
@@ -5550,9 +5554,5 @@
|
||||
"placeholders": {
|
||||
"service": {}
|
||||
}
|
||||
},
|
||||
"shareSheetOpen": "Open",
|
||||
"@shareSheetOpen": {
|
||||
"description": "Tooltip for opening a cross-service link inside the app"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -905,6 +905,10 @@
|
||||
"@errorNoTracksFound": {
|
||||
"description": "Error - search returned no results"
|
||||
},
|
||||
"searchEmptyResultSubtitle": "Coba kata kunci lain",
|
||||
"@searchEmptyResultSubtitle": {
|
||||
"description": "Subtitle shown under the empty search result state on the home screen"
|
||||
},
|
||||
"errorUrlNotRecognized": "Tautan tidak dikenali",
|
||||
"@errorUrlNotRecognized": {
|
||||
"description": "Error title - URL not handled by any extension or service"
|
||||
@@ -4656,7 +4660,7 @@
|
||||
"@dialogDisableAndClear": {
|
||||
"description": "Confirmation action to disable download history and clear existing entries"
|
||||
},
|
||||
"openInOtherServices": "Buka di layanan lain",
|
||||
"openInOtherServices": "Buka di Layanan Lain",
|
||||
"@openInOtherServices": {
|
||||
"description": "Title and tooltip for finding the current collection in other services"
|
||||
},
|
||||
@@ -4668,7 +4672,7 @@
|
||||
"@shareSheetNotFound": {
|
||||
"description": "Cross-service share sheet row subtitle when a service has no match"
|
||||
},
|
||||
"shareSheetCopyLink": "Salin tautan",
|
||||
"shareSheetCopyLink": "Salin Tautan",
|
||||
"@shareSheetCopyLink": {
|
||||
"description": "Tooltip for copying a cross-service link"
|
||||
},
|
||||
@@ -4678,9 +4682,5 @@
|
||||
"placeholders": {
|
||||
"service": {}
|
||||
}
|
||||
},
|
||||
"shareSheetOpen": "Buka",
|
||||
"@shareSheetOpen": {
|
||||
"description": "Tooltip for opening a cross-service link inside the app"
|
||||
}
|
||||
}
|
||||
|
||||
+116
-10
@@ -46,6 +46,8 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
final _urlController = TextEditingController();
|
||||
final FocusNode _searchFocusNode = FocusNode();
|
||||
String? _lastSearchQuery;
|
||||
String? _activeSearchInput;
|
||||
bool _isResettingSearchSurface = false;
|
||||
late final ProviderSubscription<TrackState> _trackStateSub;
|
||||
late final ProviderSubscription<bool> _extensionInitSub;
|
||||
late final ProviderSubscription<bool> _homeFeedExtSub;
|
||||
@@ -557,9 +559,18 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
|
||||
if (text.isEmpty) {
|
||||
_liveSearchDebounce?.cancel();
|
||||
_activeSearchInput = null;
|
||||
_lastSearchQuery = null;
|
||||
if (!_isResettingSearchSurface) {
|
||||
_resetSearchSurface(clearText: false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (_activeSearchInput != null && _activeSearchInput != text) {
|
||||
_activeSearchInput = null;
|
||||
}
|
||||
|
||||
if (_isLiveSearchEnabled() && text.length >= _minLiveSearchChars) {
|
||||
if (text.startsWith('http') || text.startsWith('spotify:')) return;
|
||||
|
||||
@@ -638,10 +649,17 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
|
||||
final searchKey =
|
||||
'${searchProvider ?? 'default'}:$query:${selectedFilter ?? 'all'}';
|
||||
if (_lastSearchQuery == searchKey) return;
|
||||
if (_lastSearchQuery == searchKey) {
|
||||
_activeSearchInput = query;
|
||||
ref.read(trackProvider.notifier).setSearchText(query.trim().isNotEmpty);
|
||||
if (mounted) setState(() {});
|
||||
return;
|
||||
}
|
||||
_lastSearchQuery = searchKey;
|
||||
_activeSearchInput = query;
|
||||
_searchSortOption = _SearchSortOption.defaultOrder;
|
||||
_invalidateSearchSortCaches();
|
||||
ref.read(trackProvider.notifier).setSearchText(query.trim().isNotEmpty);
|
||||
|
||||
final isExtensionEnabled =
|
||||
searchProvider != null &&
|
||||
@@ -686,12 +704,26 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
}
|
||||
|
||||
Future<void> _clearAndRefresh() async {
|
||||
_liveSearchDebounce?.cancel();
|
||||
_pendingLiveSearchQuery = null;
|
||||
_urlController.clear();
|
||||
_searchFocusNode.unfocus();
|
||||
_lastSearchQuery = null;
|
||||
ref.read(trackProvider.notifier).clear();
|
||||
_resetSearchSurface();
|
||||
}
|
||||
|
||||
void _resetSearchSurface({bool clearText = true}) {
|
||||
if (_isResettingSearchSurface) return;
|
||||
_isResettingSearchSurface = true;
|
||||
try {
|
||||
_liveSearchDebounce?.cancel();
|
||||
_pendingLiveSearchQuery = null;
|
||||
_lastSearchQuery = null;
|
||||
_activeSearchInput = null;
|
||||
FocusManager.instance.primaryFocus?.unfocus();
|
||||
if (clearText && _urlController.text.isNotEmpty) {
|
||||
_urlController.clear();
|
||||
}
|
||||
ref.read(trackProvider.notifier).clear();
|
||||
if (mounted) setState(() {});
|
||||
} finally {
|
||||
_isResettingSearchSurface = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _fetchMetadata() async {
|
||||
@@ -1114,6 +1146,7 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
),
|
||||
);
|
||||
final isLoading = ref.watch(trackProvider.select((s) => s.isLoading));
|
||||
final searchError = ref.watch(trackProvider.select((s) => s.error));
|
||||
final hasSearchedBefore = ref.watch(
|
||||
settingsProvider.select((s) => s.hasSearchedBefore),
|
||||
);
|
||||
@@ -1153,6 +1186,18 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
final isSearchFocused = _searchFocusNode.hasFocus;
|
||||
final hasShortSearchInput =
|
||||
hasSearchInput && searchText.length < _minLiveSearchChars;
|
||||
final hasSearchError = hasSearchInput && searchError != null;
|
||||
final hasActiveSearchSurface =
|
||||
hasSearchInput &&
|
||||
(_activeSearchInput == searchText ||
|
||||
hasActualResults ||
|
||||
isLoading ||
|
||||
hasSearchError);
|
||||
final showEmptySearchResult =
|
||||
hasActiveSearchSurface &&
|
||||
!hasActualResults &&
|
||||
!isLoading &&
|
||||
searchError == null;
|
||||
final isShowingRecentAccess = ref.watch(
|
||||
trackProvider.select((s) => s.isShowingRecentAccess),
|
||||
);
|
||||
@@ -1166,7 +1211,11 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
final recentModeRequested = isShowingRecentAccess || isSearchFocused;
|
||||
final showRecentAccess =
|
||||
recentModeRequested &&
|
||||
(!hasSearchInput || hasShortSearchInput || !hasActualResults) &&
|
||||
(!hasSearchInput ||
|
||||
hasShortSearchInput ||
|
||||
(!hasActualResults &&
|
||||
!hasSearchError &&
|
||||
!hasActiveSearchSurface)) &&
|
||||
!isLoading;
|
||||
final isSearchProviderLoading =
|
||||
!extensionReadiness.isInitialized && extensionReadiness.error == null;
|
||||
@@ -1180,6 +1229,7 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
final showExplore =
|
||||
!hasActualResults &&
|
||||
!isLoading &&
|
||||
!hasActiveSearchSurface &&
|
||||
!showRecentAccess &&
|
||||
!homeFeedDisabled &&
|
||||
(hasHomeFeedExtension || hasExploreContent) &&
|
||||
@@ -1299,7 +1349,9 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
),
|
||||
),
|
||||
|
||||
if (hasActualResults && !showRecentAccess)
|
||||
if (hasActiveSearchSurface &&
|
||||
!showRecentAccess &&
|
||||
!showEmptySearchResult)
|
||||
Consumer(
|
||||
builder: (context, ref, _) {
|
||||
final currentSearchProvider = ref.watch(
|
||||
@@ -1466,7 +1518,8 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
(searchAlbums != null && searchAlbums.isNotEmpty) ||
|
||||
(searchPlaylists != null && searchPlaylists.isNotEmpty) ||
|
||||
isLoading ||
|
||||
error != null;
|
||||
error != null ||
|
||||
hasActiveSearchSurface;
|
||||
|
||||
return SliverMainAxisGroup(
|
||||
slivers: _buildSearchResults(
|
||||
@@ -1478,6 +1531,7 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
error: error,
|
||||
colorScheme: colorScheme,
|
||||
hasResults: hasResults,
|
||||
showEmptySearchResult: showEmptySearchResult,
|
||||
searchExtensionId: searchExtensionId,
|
||||
showLocalLibraryIndicator: showLocalLibraryIndicator,
|
||||
thumbnailSizesByExtensionId: thumbnailSizesByExtensionId,
|
||||
@@ -2611,6 +2665,47 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEmptySearchResultWidget(ColorScheme colorScheme) {
|
||||
return ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 340),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
width: 86,
|
||||
height: 86,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: colorScheme.surfaceContainerHighest,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.manage_search,
|
||||
size: 46,
|
||||
color: colorScheme.primary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
context.l10n.errorNoTracksFound,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||
color: colorScheme.onSurface,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
context.l10n.searchEmptyResultSubtitle,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: colorScheme.onSurfaceVariant.withValues(alpha: 0.7),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _sortOptionLabel(_SearchSortOption option) {
|
||||
switch (option) {
|
||||
case _SearchSortOption.defaultOrder:
|
||||
@@ -2888,6 +2983,7 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
required String? error,
|
||||
required ColorScheme colorScheme,
|
||||
required bool hasResults,
|
||||
required bool showEmptySearchResult,
|
||||
required String? searchExtensionId,
|
||||
required bool showLocalLibraryIndicator,
|
||||
required Map<String, (double, double)> thumbnailSizesByExtensionId,
|
||||
@@ -2934,6 +3030,16 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
child: LinearProgressIndicator(),
|
||||
),
|
||||
),
|
||||
if (showEmptySearchResult && !hasActualData)
|
||||
SliverFillRemaining(
|
||||
hasScrollBody: false,
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(24, 24, 24, 96),
|
||||
child: _buildEmptySearchResultWidget(colorScheme),
|
||||
),
|
||||
),
|
||||
),
|
||||
];
|
||||
|
||||
bool sortButtonShown = false;
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:spotiflac_android/l10n/l10n.dart';
|
||||
import 'package:spotiflac_android/providers/extension_provider.dart';
|
||||
import 'package:spotiflac_android/services/cross_extension_share_service.dart';
|
||||
import 'package:spotiflac_android/services/share_intent_service.dart';
|
||||
|
||||
class CrossExtensionShareSheet extends StatefulWidget {
|
||||
class CrossExtensionShareSheet extends ConsumerStatefulWidget {
|
||||
final String name;
|
||||
final String artists;
|
||||
final String type;
|
||||
@@ -44,11 +48,12 @@ class CrossExtensionShareSheet extends StatefulWidget {
|
||||
}
|
||||
|
||||
@override
|
||||
State<CrossExtensionShareSheet> createState() =>
|
||||
ConsumerState<CrossExtensionShareSheet> createState() =>
|
||||
_CrossExtensionShareSheetState();
|
||||
}
|
||||
|
||||
class _CrossExtensionShareSheetState extends State<CrossExtensionShareSheet> {
|
||||
class _CrossExtensionShareSheetState
|
||||
extends ConsumerState<CrossExtensionShareSheet> {
|
||||
late final Future<List<CrossExtensionShareResult>> _future;
|
||||
|
||||
@override
|
||||
@@ -71,6 +76,18 @@ class _CrossExtensionShareSheetState extends State<CrossExtensionShareSheet> {
|
||||
});
|
||||
}
|
||||
|
||||
String? _iconPathFor(String extensionId) {
|
||||
if (extensionId.isEmpty) return null;
|
||||
final extensions = ref.read(extensionProvider).extensions;
|
||||
for (final ext in extensions) {
|
||||
if (ext.id == extensionId) {
|
||||
final path = ext.iconPath;
|
||||
return (path != null && path.isNotEmpty) ? path : null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
@@ -86,25 +103,23 @@ class _CrossExtensionShareSheetState extends State<CrossExtensionShareSheet> {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 12, bottom: 8),
|
||||
child: Container(
|
||||
width: 40,
|
||||
height: 4,
|
||||
decoration: BoxDecoration(
|
||||
color: colorScheme.onSurfaceVariant.withValues(alpha: 0.4),
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
),
|
||||
child: Container(
|
||||
width: 40,
|
||||
height: 4,
|
||||
decoration: BoxDecoration(
|
||||
color: colorScheme.onSurfaceVariant.withValues(alpha: 0.4),
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(24, 8, 24, 4),
|
||||
padding: const EdgeInsets.fromLTRB(24, 16, 24, 4),
|
||||
child: Text(
|
||||
context.l10n.openInOtherServices,
|
||||
style: textTheme.titleLarge?.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
style: textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -147,14 +162,16 @@ class _CrossExtensionShareSheetState extends State<CrossExtensionShareSheet> {
|
||||
);
|
||||
}
|
||||
|
||||
return ListView.separated(
|
||||
return ListView.builder(
|
||||
shrinkWrap: true,
|
||||
padding: const EdgeInsets.fromLTRB(12, 4, 12, 16),
|
||||
padding: const EdgeInsets.only(bottom: 16, top: 4),
|
||||
itemBuilder: (context, index) {
|
||||
return _CrossExtensionShareTile(result: results[index]);
|
||||
final result = results[index];
|
||||
return _CrossExtensionShareTile(
|
||||
result: result,
|
||||
iconPath: _iconPathFor(result.extensionId),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (_, _) =>
|
||||
const Divider(height: 1, indent: 72),
|
||||
itemCount: results.length,
|
||||
);
|
||||
},
|
||||
@@ -169,33 +186,28 @@ class _CrossExtensionShareSheetState extends State<CrossExtensionShareSheet> {
|
||||
|
||||
class _CrossExtensionShareTile extends StatelessWidget {
|
||||
final CrossExtensionShareResult result;
|
||||
final String? iconPath;
|
||||
|
||||
const _CrossExtensionShareTile({required this.result});
|
||||
const _CrossExtensionShareTile({required this.result, this.iconPath});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final textTheme = Theme.of(context).textTheme;
|
||||
final url = result.found ? result.url : null;
|
||||
final hasUrl = url != null && url.isNotEmpty;
|
||||
final url = result.url;
|
||||
final hasUrl = result.found && url != null && url.isNotEmpty;
|
||||
|
||||
return ListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
|
||||
final tile = ListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24, vertical: 4),
|
||||
leading: Container(
|
||||
width: 44,
|
||||
height: 44,
|
||||
decoration: BoxDecoration(
|
||||
color: hasUrl
|
||||
? colorScheme.primaryContainer
|
||||
: colorScheme.surfaceContainerHighest,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
hasUrl ? Icons.link_rounded : Icons.link_off_rounded,
|
||||
color: hasUrl
|
||||
? colorScheme.onPrimaryContainer
|
||||
: colorScheme.onSurfaceVariant,
|
||||
color: colorScheme.surfaceContainerHighest,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: _buildIcon(colorScheme),
|
||||
),
|
||||
title: Text(
|
||||
result.displayName,
|
||||
@@ -210,39 +222,53 @@ class _CrossExtensionShareTile extends StatelessWidget {
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: textTheme.bodySmall?.copyWith(
|
||||
color: hasUrl ? colorScheme.primary : colorScheme.onSurfaceVariant,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
trailing: hasUrl
|
||||
? Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton(
|
||||
tooltip: context.l10n.shareSheetCopyLink,
|
||||
icon: const Icon(Icons.copy_rounded, size: 20),
|
||||
onPressed: () {
|
||||
Clipboard.setData(ClipboardData(text: url));
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
context.l10n.shareSheetLinkCopied(result.displayName),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
tooltip: context.l10n.shareSheetOpen,
|
||||
icon: const Icon(Icons.open_in_new_rounded, size: 20),
|
||||
color: colorScheme.primary,
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
ShareIntentService().injectUrl(url);
|
||||
},
|
||||
),
|
||||
],
|
||||
? IconButton(
|
||||
tooltip: context.l10n.shareSheetCopyLink,
|
||||
icon: const Icon(Icons.copy_rounded, size: 20),
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
onPressed: () {
|
||||
Clipboard.setData(ClipboardData(text: url));
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
context.l10n.shareSheetLinkCopied(result.displayName),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
: null,
|
||||
onTap: hasUrl
|
||||
? () {
|
||||
Navigator.pop(context);
|
||||
ShareIntentService().injectUrl(url);
|
||||
}
|
||||
: null,
|
||||
);
|
||||
|
||||
if (hasUrl) return tile;
|
||||
return Opacity(opacity: 0.5, child: tile);
|
||||
}
|
||||
|
||||
Widget _buildIcon(ColorScheme colorScheme) {
|
||||
final fallbackIcon = Icon(
|
||||
Icons.extension_rounded,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
);
|
||||
|
||||
final path = iconPath;
|
||||
if (path == null) return fallbackIcon;
|
||||
|
||||
return Image.file(
|
||||
File(path),
|
||||
width: 44,
|
||||
height: 44,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (_, _, _) => fallbackIcon,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user