From ca6924f5a99fe10597614e3b775e4293a1d68ced Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Thu, 18 Sep 2025 23:28:56 +0600 Subject: [PATCH] feat: show plugin source and set the only plugin as default if no plugin is there --- .../dialogs/link_open_permission_dialog.dart | 69 ++++ lib/components/markdown/markdown.dart | 60 +--- lib/l10n/app_en.arb | 3 +- lib/l10n/generated/app_localizations.dart | 6 + lib/l10n/generated/app_localizations_ar.dart | 3 + lib/l10n/generated/app_localizations_bn.dart | 3 + lib/l10n/generated/app_localizations_ca.dart | 3 + lib/l10n/generated/app_localizations_cs.dart | 3 + lib/l10n/generated/app_localizations_de.dart | 3 + lib/l10n/generated/app_localizations_en.dart | 3 + lib/l10n/generated/app_localizations_es.dart | 3 + lib/l10n/generated/app_localizations_eu.dart | 3 + lib/l10n/generated/app_localizations_fa.dart | 3 + lib/l10n/generated/app_localizations_fi.dart | 3 + lib/l10n/generated/app_localizations_fr.dart | 3 + lib/l10n/generated/app_localizations_hi.dart | 3 + lib/l10n/generated/app_localizations_id.dart | 3 + lib/l10n/generated/app_localizations_it.dart | 3 + lib/l10n/generated/app_localizations_ja.dart | 3 + lib/l10n/generated/app_localizations_ka.dart | 3 + lib/l10n/generated/app_localizations_ko.dart | 3 + lib/l10n/generated/app_localizations_ne.dart | 3 + lib/l10n/generated/app_localizations_nl.dart | 3 + lib/l10n/generated/app_localizations_pl.dart | 3 + lib/l10n/generated/app_localizations_pt.dart | 3 + lib/l10n/generated/app_localizations_ru.dart | 3 + lib/l10n/generated/app_localizations_ta.dart | 3 + lib/l10n/generated/app_localizations_th.dart | 3 + lib/l10n/generated/app_localizations_tl.dart | 3 + lib/l10n/generated/app_localizations_tr.dart | 3 + lib/l10n/generated/app_localizations_uk.dart | 3 + lib/l10n/generated/app_localizations_vi.dart | 3 + lib/l10n/generated/app_localizations_zh.dart | 3 + .../metadata_plugins/plugin_repository.dart | 300 ++++++++++-------- .../metadata_plugin_provider.dart | 13 + pubspec.lock | 2 +- pubspec.yaml | 1 + untranslated_messages.json | 115 ++++++- 38 files changed, 464 insertions(+), 192 deletions(-) create mode 100644 lib/components/dialogs/link_open_permission_dialog.dart diff --git a/lib/components/dialogs/link_open_permission_dialog.dart b/lib/components/dialogs/link_open_permission_dialog.dart new file mode 100644 index 00000000..a7212d0a --- /dev/null +++ b/lib/components/dialogs/link_open_permission_dialog.dart @@ -0,0 +1,69 @@ +import 'package:flutter/services.dart'; +import 'package:shadcn_flutter/shadcn_flutter.dart'; +import 'package:spotube/collections/spotube_icons.dart'; +import 'package:spotube/extensions/context.dart'; +import 'package:url_launcher/url_launcher_string.dart'; + +class LinkOpenPermissionDialog extends StatelessWidget { + final String? href; + const LinkOpenPermissionDialog({super.key, this.href}); + + @override + Widget build(BuildContext context) { + return ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 450), + child: AlertDialog( + title: Row( + spacing: 8, + children: [ + const Icon(SpotubeIcons.warning), + Text(context.l10n.open_link_in_browser), + ], + ), + content: Text.rich( + TextSpan( + children: [ + TextSpan( + text: + "${context.l10n.do_you_want_to_open_the_following_link}:\n", + ), + if (href != null) + TextSpan( + text: "$href\n\n", + style: const TextStyle(color: Colors.blue), + ), + TextSpan(text: context.l10n.unsafe_url_warning), + ], + ), + ), + actions: [ + Button.ghost( + onPressed: () => Navigator.of(context).pop(false), + child: Text(context.l10n.cancel), + ), + Button.ghost( + onPressed: () { + if (href != null) { + Clipboard.setData(ClipboardData(text: href!)); + } + Navigator.of(context).pop(false); + }, + child: Text(context.l10n.copy_link), + ), + Button.destructive( + onPressed: () { + if (href != null) { + launchUrlString( + href!, + mode: LaunchMode.externalApplication, + ); + } + Navigator.of(context).pop(true); + }, + child: Text(context.l10n.open), + ), + ], + ), + ); + } +} diff --git a/lib/components/markdown/markdown.dart b/lib/components/markdown/markdown.dart index 9ea2e77c..1fd4ac5b 100644 --- a/lib/components/markdown/markdown.dart +++ b/lib/components/markdown/markdown.dart @@ -1,9 +1,7 @@ import 'package:cached_network_image/cached_network_image.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_markdown_plus/flutter_markdown_plus.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart'; -import 'package:spotube/collections/spotube_icons.dart'; -import 'package:spotube/extensions/context.dart'; +import 'package:spotube/components/dialogs/link_open_permission_dialog.dart'; import 'package:url_launcher/url_launcher_string.dart'; class AppMarkdown extends StatelessWidget { @@ -28,61 +26,7 @@ class AppMarkdown extends StatelessWidget { final allowOpeningLink = await showDialog( context: context, builder: (context) { - return ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 450), - child: AlertDialog( - title: Row( - spacing: 8, - children: [ - const Icon(SpotubeIcons.warning), - Text(context.l10n.open_link_in_browser), - ], - ), - content: Text.rich( - TextSpan( - children: [ - TextSpan( - text: - "${context.l10n.do_you_want_to_open_the_following_link}:\n", - ), - if (href != null) - TextSpan( - text: "$href\n\n", - style: const TextStyle(color: Colors.blue), - ), - TextSpan(text: context.l10n.unsafe_url_warning), - ], - ), - ), - actions: [ - Button.ghost( - onPressed: () => Navigator.of(context).pop(false), - child: Text(context.l10n.cancel), - ), - Button.ghost( - onPressed: () { - if (href != null) { - Clipboard.setData(ClipboardData(text: href)); - } - Navigator.of(context).pop(false); - }, - child: Text(context.l10n.copy_link), - ), - Button.destructive( - onPressed: () { - if (href != null) { - launchUrlString( - href, - mode: LaunchMode.externalApplication, - ); - } - Navigator.of(context).pop(true); - }, - child: Text(context.l10n.open), - ), - ], - ), - ); + return LinkOpenPermissionDialog(href: href); }, ); diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 34b56489..de4a784b 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -461,5 +461,6 @@ "available_plugins": "Available plugins", "configure_your_own_metadata_plugin": "Configure your own playlist/album/artist/feed metadata provider", "audio_scrobblers": "Audio Scrobblers", - "scrobbling": "Scrobbling" + "scrobbling": "Scrobbling", + "source": "Source: " } diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index bf6f5211..79534e94 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -2930,6 +2930,12 @@ abstract class AppLocalizations { /// In en, this message translates to: /// **'Scrobbling'** String get scrobbling; + + /// No description provided for @source. + /// + /// In en, this message translates to: + /// **'Source: '** + String get source; } class _AppLocalizationsDelegate diff --git a/lib/l10n/generated/app_localizations_ar.dart b/lib/l10n/generated/app_localizations_ar.dart index b5734db6..e3d72f81 100644 --- a/lib/l10n/generated/app_localizations_ar.dart +++ b/lib/l10n/generated/app_localizations_ar.dart @@ -1537,4 +1537,7 @@ class AppLocalizationsAr extends AppLocalizations { @override String get scrobbling => 'التتبع'; + + @override + String get source => 'Source: '; } diff --git a/lib/l10n/generated/app_localizations_bn.dart b/lib/l10n/generated/app_localizations_bn.dart index 4503564e..cfa4d31a 100644 --- a/lib/l10n/generated/app_localizations_bn.dart +++ b/lib/l10n/generated/app_localizations_bn.dart @@ -1538,4 +1538,7 @@ class AppLocalizationsBn extends AppLocalizations { @override String get scrobbling => 'স্ক্রোব্বলিং'; + + @override + String get source => 'Source: '; } diff --git a/lib/l10n/generated/app_localizations_ca.dart b/lib/l10n/generated/app_localizations_ca.dart index 70899f04..458286dd 100644 --- a/lib/l10n/generated/app_localizations_ca.dart +++ b/lib/l10n/generated/app_localizations_ca.dart @@ -1548,4 +1548,7 @@ class AppLocalizationsCa extends AppLocalizations { @override String get scrobbling => 'Scrobbling'; + + @override + String get source => 'Source: '; } diff --git a/lib/l10n/generated/app_localizations_cs.dart b/lib/l10n/generated/app_localizations_cs.dart index 3cb620e0..6c89a2ab 100644 --- a/lib/l10n/generated/app_localizations_cs.dart +++ b/lib/l10n/generated/app_localizations_cs.dart @@ -1538,4 +1538,7 @@ class AppLocalizationsCs extends AppLocalizations { @override String get scrobbling => 'Scrobbling'; + + @override + String get source => 'Source: '; } diff --git a/lib/l10n/generated/app_localizations_de.dart b/lib/l10n/generated/app_localizations_de.dart index 75c858f5..1a79979c 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -1550,4 +1550,7 @@ class AppLocalizationsDe extends AppLocalizations { @override String get scrobbling => 'Scrobbling'; + + @override + String get source => 'Source: '; } diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index e6d4db1e..a9125e56 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -1536,4 +1536,7 @@ class AppLocalizationsEn extends AppLocalizations { @override String get scrobbling => 'Scrobbling'; + + @override + String get source => 'Source: '; } diff --git a/lib/l10n/generated/app_localizations_es.dart b/lib/l10n/generated/app_localizations_es.dart index f51f829c..ffa6eced 100644 --- a/lib/l10n/generated/app_localizations_es.dart +++ b/lib/l10n/generated/app_localizations_es.dart @@ -1551,4 +1551,7 @@ class AppLocalizationsEs extends AppLocalizations { @override String get scrobbling => 'Scrobbling'; + + @override + String get source => 'Source: '; } diff --git a/lib/l10n/generated/app_localizations_eu.dart b/lib/l10n/generated/app_localizations_eu.dart index 523f110f..b0a7be9a 100644 --- a/lib/l10n/generated/app_localizations_eu.dart +++ b/lib/l10n/generated/app_localizations_eu.dart @@ -1548,4 +1548,7 @@ class AppLocalizationsEu extends AppLocalizations { @override String get scrobbling => 'Scrobbling'; + + @override + String get source => 'Source: '; } diff --git a/lib/l10n/generated/app_localizations_fa.dart b/lib/l10n/generated/app_localizations_fa.dart index c63e723a..64f797f3 100644 --- a/lib/l10n/generated/app_localizations_fa.dart +++ b/lib/l10n/generated/app_localizations_fa.dart @@ -1536,4 +1536,7 @@ class AppLocalizationsFa extends AppLocalizations { @override String get scrobbling => 'اسکراب‌بلینگ'; + + @override + String get source => 'Source: '; } diff --git a/lib/l10n/generated/app_localizations_fi.dart b/lib/l10n/generated/app_localizations_fi.dart index e1ba7f5a..6f00104a 100644 --- a/lib/l10n/generated/app_localizations_fi.dart +++ b/lib/l10n/generated/app_localizations_fi.dart @@ -1536,4 +1536,7 @@ class AppLocalizationsFi extends AppLocalizations { @override String get scrobbling => 'Scrobbling'; + + @override + String get source => 'Source: '; } diff --git a/lib/l10n/generated/app_localizations_fr.dart b/lib/l10n/generated/app_localizations_fr.dart index 88350997..067e812d 100644 --- a/lib/l10n/generated/app_localizations_fr.dart +++ b/lib/l10n/generated/app_localizations_fr.dart @@ -1556,4 +1556,7 @@ class AppLocalizationsFr extends AppLocalizations { @override String get scrobbling => 'Scrobbling'; + + @override + String get source => 'Source: '; } diff --git a/lib/l10n/generated/app_localizations_hi.dart b/lib/l10n/generated/app_localizations_hi.dart index f3ba4802..0ca1641f 100644 --- a/lib/l10n/generated/app_localizations_hi.dart +++ b/lib/l10n/generated/app_localizations_hi.dart @@ -1542,4 +1542,7 @@ class AppLocalizationsHi extends AppLocalizations { @override String get scrobbling => 'स्क्रॉबलिंग'; + + @override + String get source => 'Source: '; } diff --git a/lib/l10n/generated/app_localizations_id.dart b/lib/l10n/generated/app_localizations_id.dart index c56f1ece..5545f306 100644 --- a/lib/l10n/generated/app_localizations_id.dart +++ b/lib/l10n/generated/app_localizations_id.dart @@ -1544,4 +1544,7 @@ class AppLocalizationsId extends AppLocalizations { @override String get scrobbling => 'Scrobbling'; + + @override + String get source => 'Source: '; } diff --git a/lib/l10n/generated/app_localizations_it.dart b/lib/l10n/generated/app_localizations_it.dart index dc8ed9cd..b130f9b4 100644 --- a/lib/l10n/generated/app_localizations_it.dart +++ b/lib/l10n/generated/app_localizations_it.dart @@ -1543,4 +1543,7 @@ class AppLocalizationsIt extends AppLocalizations { @override String get scrobbling => 'Scrobbling'; + + @override + String get source => 'Source: '; } diff --git a/lib/l10n/generated/app_localizations_ja.dart b/lib/l10n/generated/app_localizations_ja.dart index 7ce62161..104cf201 100644 --- a/lib/l10n/generated/app_localizations_ja.dart +++ b/lib/l10n/generated/app_localizations_ja.dart @@ -1507,4 +1507,7 @@ class AppLocalizationsJa extends AppLocalizations { @override String get scrobbling => 'Scrobbling'; + + @override + String get source => 'Source: '; } diff --git a/lib/l10n/generated/app_localizations_ka.dart b/lib/l10n/generated/app_localizations_ka.dart index a28bd02d..e44e9aa7 100644 --- a/lib/l10n/generated/app_localizations_ka.dart +++ b/lib/l10n/generated/app_localizations_ka.dart @@ -1545,4 +1545,7 @@ class AppLocalizationsKa extends AppLocalizations { @override String get scrobbling => 'სქრობლინგი'; + + @override + String get source => 'Source: '; } diff --git a/lib/l10n/generated/app_localizations_ko.dart b/lib/l10n/generated/app_localizations_ko.dart index 40104b52..aac88524 100644 --- a/lib/l10n/generated/app_localizations_ko.dart +++ b/lib/l10n/generated/app_localizations_ko.dart @@ -1511,4 +1511,7 @@ class AppLocalizationsKo extends AppLocalizations { @override String get scrobbling => '스크로블링'; + + @override + String get source => 'Source: '; } diff --git a/lib/l10n/generated/app_localizations_ne.dart b/lib/l10n/generated/app_localizations_ne.dart index 18d155fe..726a8eee 100644 --- a/lib/l10n/generated/app_localizations_ne.dart +++ b/lib/l10n/generated/app_localizations_ne.dart @@ -1548,4 +1548,7 @@ class AppLocalizationsNe extends AppLocalizations { @override String get scrobbling => 'स्क्रब्बलिंग'; + + @override + String get source => 'Source: '; } diff --git a/lib/l10n/generated/app_localizations_nl.dart b/lib/l10n/generated/app_localizations_nl.dart index 3074e958..d0a64253 100644 --- a/lib/l10n/generated/app_localizations_nl.dart +++ b/lib/l10n/generated/app_localizations_nl.dart @@ -1542,4 +1542,7 @@ class AppLocalizationsNl extends AppLocalizations { @override String get scrobbling => 'Scrobbling'; + + @override + String get source => 'Source: '; } diff --git a/lib/l10n/generated/app_localizations_pl.dart b/lib/l10n/generated/app_localizations_pl.dart index 969204da..8d29354e 100644 --- a/lib/l10n/generated/app_localizations_pl.dart +++ b/lib/l10n/generated/app_localizations_pl.dart @@ -1544,4 +1544,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get scrobbling => 'Scrobbling'; + + @override + String get source => 'Source: '; } diff --git a/lib/l10n/generated/app_localizations_pt.dart b/lib/l10n/generated/app_localizations_pt.dart index 35d9881d..10a036af 100644 --- a/lib/l10n/generated/app_localizations_pt.dart +++ b/lib/l10n/generated/app_localizations_pt.dart @@ -1541,4 +1541,7 @@ class AppLocalizationsPt extends AppLocalizations { @override String get scrobbling => 'Scrobbling'; + + @override + String get source => 'Source: '; } diff --git a/lib/l10n/generated/app_localizations_ru.dart b/lib/l10n/generated/app_localizations_ru.dart index e4cd090b..d0d6903d 100644 --- a/lib/l10n/generated/app_localizations_ru.dart +++ b/lib/l10n/generated/app_localizations_ru.dart @@ -1544,4 +1544,7 @@ class AppLocalizationsRu extends AppLocalizations { @override String get scrobbling => 'Скробблинг'; + + @override + String get source => 'Source: '; } diff --git a/lib/l10n/generated/app_localizations_ta.dart b/lib/l10n/generated/app_localizations_ta.dart index 0a131edd..20f89003 100644 --- a/lib/l10n/generated/app_localizations_ta.dart +++ b/lib/l10n/generated/app_localizations_ta.dart @@ -1550,4 +1550,7 @@ class AppLocalizationsTa extends AppLocalizations { @override String get scrobbling => 'ஸ்க்ரோப்ளிங்'; + + @override + String get source => 'Source: '; } diff --git a/lib/l10n/generated/app_localizations_th.dart b/lib/l10n/generated/app_localizations_th.dart index 85230bfd..98ae0ad4 100644 --- a/lib/l10n/generated/app_localizations_th.dart +++ b/lib/l10n/generated/app_localizations_th.dart @@ -1533,4 +1533,7 @@ class AppLocalizationsTh extends AppLocalizations { @override String get scrobbling => 'Scrobbling'; + + @override + String get source => 'Source: '; } diff --git a/lib/l10n/generated/app_localizations_tl.dart b/lib/l10n/generated/app_localizations_tl.dart index 361a7bf0..0b9b79d5 100644 --- a/lib/l10n/generated/app_localizations_tl.dart +++ b/lib/l10n/generated/app_localizations_tl.dart @@ -1551,4 +1551,7 @@ class AppLocalizationsTl extends AppLocalizations { @override String get scrobbling => 'Scrobbling'; + + @override + String get source => 'Source: '; } diff --git a/lib/l10n/generated/app_localizations_tr.dart b/lib/l10n/generated/app_localizations_tr.dart index 4dc65bbc..e5a36ba1 100644 --- a/lib/l10n/generated/app_localizations_tr.dart +++ b/lib/l10n/generated/app_localizations_tr.dart @@ -1544,4 +1544,7 @@ class AppLocalizationsTr extends AppLocalizations { @override String get scrobbling => 'Scrobbling'; + + @override + String get source => 'Source: '; } diff --git a/lib/l10n/generated/app_localizations_uk.dart b/lib/l10n/generated/app_localizations_uk.dart index 35a18d55..f0254780 100644 --- a/lib/l10n/generated/app_localizations_uk.dart +++ b/lib/l10n/generated/app_localizations_uk.dart @@ -1540,4 +1540,7 @@ class AppLocalizationsUk extends AppLocalizations { @override String get scrobbling => 'Скроблінг'; + + @override + String get source => 'Source: '; } diff --git a/lib/l10n/generated/app_localizations_vi.dart b/lib/l10n/generated/app_localizations_vi.dart index 6015931e..aa73fbdc 100644 --- a/lib/l10n/generated/app_localizations_vi.dart +++ b/lib/l10n/generated/app_localizations_vi.dart @@ -1546,4 +1546,7 @@ class AppLocalizationsVi extends AppLocalizations { @override String get scrobbling => 'Scrobbling'; + + @override + String get source => 'Source: '; } diff --git a/lib/l10n/generated/app_localizations_zh.dart b/lib/l10n/generated/app_localizations_zh.dart index e42b6994..d3b25f71 100644 --- a/lib/l10n/generated/app_localizations_zh.dart +++ b/lib/l10n/generated/app_localizations_zh.dart @@ -1500,6 +1500,9 @@ class AppLocalizationsZh extends AppLocalizations { @override String get scrobbling => 'Scrobbling'; + + @override + String get source => 'Source: '; } /// The translations for Chinese, as used in Taiwan (`zh_TW`). diff --git a/lib/modules/metadata_plugins/plugin_repository.dart b/lib/modules/metadata_plugins/plugin_repository.dart index f140b9ee..295aed53 100644 --- a/lib/modules/metadata_plugins/plugin_repository.dart +++ b/lib/modules/metadata_plugins/plugin_repository.dart @@ -1,3 +1,4 @@ +import 'package:flutter/gestures.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart'; @@ -8,6 +9,7 @@ import 'package:spotube/extensions/context.dart'; import 'package:spotube/models/metadata/metadata.dart'; import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart'; import 'package:url_launcher/url_launcher_string.dart'; +import 'package:change_case/change_case.dart'; class MetadataPluginRepositoryItem extends HookConsumerWidget { final MetadataPluginRepository pluginRepo; @@ -26,144 +28,180 @@ class MetadataPluginRepositoryItem extends HookConsumerWidget { final isInstalling = useState(false); return Card( - child: Basic( - title: Text( - "${pluginRepo.owner == "KRTirtho" ? "" : "${pluginRepo.owner}/"}${pluginRepo.name}"), - subtitle: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - spacing: 8, - children: [ - Text(pluginRepo.description), - Row( - spacing: 8, - children: [ - if (pluginRepo.owner == "KRTirtho") ...[ - PrimaryBadge( - leading: Icon(SpotubeIcons.done), - child: Text(context.l10n.official), - ), - SecondaryBadge( - leading: host == "github.com" - ? const Icon(SpotubeIcons.github) - : null, - child: Text(host), - onPressed: () { - launchUrlString(pluginRepo.repoUrl); - }, - ), - ] else ...[ - Text(context.l10n.author_name(pluginRepo.owner)), - DestructiveBadge( - leading: const Icon(SpotubeIcons.warning), - child: Text(context.l10n.third_party), - ) - ] - ], + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + spacing: 8, + children: [ + Basic( + title: Text( + pluginRepo.name.startsWith("spotube-plugin") + ? pluginRepo.name + .replaceFirst("spotube-plugin-", "") + .trim() + .toCapitalCase() + : pluginRepo.name.toCapitalCase(), ), - ], - ), - trailing: Button.primary( - enabled: !isInstalling.value, - onPressed: () async { - try { - isInstalling.value = true; - final pluginConfig = await pluginsNotifier - .downloadAndCachePlugin(pluginRepo.repoUrl); + subtitle: Text(pluginRepo.description), + trailing: Button.primary( + enabled: !isInstalling.value, + onPressed: () async { + try { + isInstalling.value = true; + final pluginConfig = await pluginsNotifier + .downloadAndCachePlugin(pluginRepo.repoUrl); - if (!context.mounted) return; - final isOfficialPlugin = pluginRepo.owner == "KRTirtho"; + if (!context.mounted) return; + final isOfficialPlugin = pluginRepo.owner == "KRTirtho"; - final isAllowed = isOfficialPlugin - ? true - : await showDialog( - context: context, - builder: (context) { - final pluginAbilities = pluginConfig.apis - .map( - (e) => context.l10n.can_access_name_api(e.name)) - .join("\n\n"); + final isAllowed = isOfficialPlugin + ? true + : await showDialog( + context: context, + builder: (context) { + final pluginAbilities = pluginConfig.apis + .map((e) => + context.l10n.can_access_name_api(e.name)) + .join("\n\n"); - return AlertDialog( - title: Text( - context.l10n.do_you_want_to_install_this_plugin), - content: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(context.l10n.third_party_plugin_warning), - const Gap(8), - FutureBuilder( - future: - pluginsNotifier.getLogoPath(pluginConfig), - builder: (context, snapshot) { - return Basic( - leading: snapshot.hasData - ? Image.file( - snapshot.data!, - width: 36, - height: 36, - ) - : Container( - height: 36, - width: 36, - alignment: Alignment.center, - decoration: BoxDecoration( - color: context - .theme.colorScheme.secondary, - borderRadius: - BorderRadius.circular(8), - ), - child: - const Icon(SpotubeIcons.plugin), - ), - title: Text(pluginConfig.name), - subtitle: Text(pluginConfig.description), - ); - }, + return AlertDialog( + title: Text(context + .l10n.do_you_want_to_install_this_plugin), + content: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(context.l10n.third_party_plugin_warning), + const Gap(8), + FutureBuilder( + future: pluginsNotifier + .getLogoPath(pluginConfig), + builder: (context, snapshot) { + return Basic( + leading: snapshot.hasData + ? Image.file( + snapshot.data!, + width: 36, + height: 36, + ) + : Container( + height: 36, + width: 36, + alignment: Alignment.center, + decoration: BoxDecoration( + color: context.theme + .colorScheme.secondary, + borderRadius: + BorderRadius.circular(8), + ), + child: const Icon( + SpotubeIcons.plugin), + ), + title: Text(pluginConfig.name), + subtitle: + Text(pluginConfig.description), + ); + }, + ), + const Gap(8), + AppMarkdown( + data: + "**${context.l10n.author}**: ${pluginConfig.author}\n\n" + "**${context.l10n.repository}**: [${pluginConfig.repository ?? 'N/A'}](${pluginConfig.repository})\n\n\n\n" + "${context.l10n.this_plugin_can_do_following}:\n\n" + "$pluginAbilities", + ), + ], ), - const Gap(8), - AppMarkdown( - data: - "**${context.l10n.author}**: ${pluginConfig.author}\n\n" - "**${context.l10n.repository}**: [${pluginConfig.repository ?? 'N/A'}](${pluginConfig.repository})\n\n\n\n" - "${context.l10n.this_plugin_can_do_following}:\n\n" - "$pluginAbilities", - ), - ], - ), - actions: [ - Button.secondary( - onPressed: () { - Navigator.of(context).pop(false); - }, - child: Text(context.l10n.decline), - ), - Button.primary( - onPressed: () { - Navigator.of(context).pop(true); - }, - child: Text(context.l10n.accept), - ), - ], + actions: [ + Button.secondary( + onPressed: () { + Navigator.of(context).pop(false); + }, + child: Text(context.l10n.decline), + ), + Button.primary( + onPressed: () { + Navigator.of(context).pop(true); + }, + child: Text(context.l10n.accept), + ), + ], + ); + }, ); - }, - ); - if (isAllowed != true) return; - await pluginsNotifier.addPlugin(pluginConfig); - } finally { - if (context.mounted) { - isInstalling.value = false; - } - } - }, - leading: isInstalling.value - ? const CircularProgressIndicator() - : const Icon(SpotubeIcons.add), - child: Text(context.l10n.install), - ), + if (isAllowed != true) return; + await pluginsNotifier.addPlugin(pluginConfig); + } finally { + if (context.mounted) { + isInstalling.value = false; + } + } + }, + leading: isInstalling.value + ? SizedBox.square( + dimension: 20, + child: CircularProgressIndicator( + color: context.theme.colorScheme.primaryForeground, + ), + ) + : const Icon(SpotubeIcons.add), + child: Text(context.l10n.install), + ), + ), + if (pluginRepo.owner != "KRTirtho") + Text.rich( + TextSpan( + children: [ + TextSpan(text: context.l10n.source), + TextSpan( + text: pluginRepo.repoUrl.replaceAll("https://", ""), + style: const TextStyle( + color: Colors.blue, + decoration: TextDecoration.underline, + ), + recognizer: TapGestureRecognizer() + ..onTap = () async { + launchUrlString(pluginRepo.repoUrl); + }, + ), + ], + ), + style: context.theme.typography.xSmall, + ), + Wrap( + spacing: 8, + runSpacing: 8, + children: [ + if (pluginRepo.owner == "KRTirtho") + PrimaryBadge( + leading: const Icon(SpotubeIcons.done), + child: Text(context.l10n.official), + ) + else ...[ + Text( + context.l10n.author_name(pluginRepo.owner), + style: context.theme.typography.xSmall, + ), + DestructiveBadge( + leading: const Icon(SpotubeIcons.warning), + child: Text(context.l10n.third_party), + ), + ], + SecondaryBadge( + leading: host == "github.com" + ? const Icon(SpotubeIcons.github) + : null, + child: Text(host), + onPressed: () { + launchUrlString(pluginRepo.repoUrl); + }, + ), + ], + ), + ], ), ); } diff --git a/lib/provider/metadata_plugin/metadata_plugin_provider.dart b/lib/provider/metadata_plugin/metadata_plugin_provider.dart index b61c0255..881c0113 100644 --- a/lib/provider/metadata_plugin/metadata_plugin_provider.dart +++ b/lib/provider/metadata_plugin/metadata_plugin_provider.dart @@ -350,6 +350,8 @@ class MetadataPluginNotifier extends AsyncNotifier { abilities: plugin.abilities.map((e) => e.name).toList(), pluginApiVersion: Value(plugin.pluginApiVersion), repository: Value(plugin.repository), + // Setting the very first plugin as the default plugin + selected: Value(state.valueOrNull?.plugins.isEmpty ?? true), ), ); } @@ -362,6 +364,17 @@ class MetadataPluginNotifier extends AsyncNotifier { } await database.metadataPluginsTable.deleteWhere((tbl) => tbl.name.equals(plugin.name) & tbl.author.equals(plugin.author)); + + // Same here, if the removed plugin is the default plugin + // set the first available plugin as the default plugin + // only when there is 1 remaining plugin + if (state.valueOrNull?.defaultPluginConfig == plugin) { + final remainingPlugins = + state.valueOrNull?.plugins.where((p) => p != plugin) ?? []; + if (remainingPlugins.length == 1) { + await setDefaultPlugin(remainingPlugins.first); + } + } } Future updatePlugin( diff --git a/pubspec.lock b/pubspec.lock index a6720c55..4d900033 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -315,7 +315,7 @@ packages: source: hosted version: "1.3.1" change_case: - dependency: transitive + dependency: "direct main" description: name: change_case sha256: f4e08feaa845e75e4f5ad2b0e15f24813d7ea6c27e7b78252f0c17f752cf1157 diff --git a/pubspec.yaml b/pubspec.yaml index c3044aad..9d3c1a3c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -163,6 +163,7 @@ dependencies: get_it: ^8.0.3 flutter_markdown_plus: ^1.0.3 pub_semver: ^2.2.0 + change_case: ^1.1.0 dev_dependencies: build_runner: ^2.4.13 diff --git a/untranslated_messages.json b/untranslated_messages.json index af89bb78..08dfdf16 100644 --- a/untranslated_messages.json +++ b/untranslated_messages.json @@ -1,5 +1,118 @@ { + "ar": [ + "source" + ], + + "bn": [ + "source" + ], + + "ca": [ + "source" + ], + + "cs": [ + "source" + ], + + "de": [ + "source" + ], + + "es": [ + "source" + ], + + "eu": [ + "source" + ], + + "fa": [ + "source" + ], + + "fi": [ + "source" + ], + + "fr": [ + "source" + ], + + "hi": [ + "source" + ], + + "id": [ + "source" + ], + + "it": [ + "source" + ], + + "ja": [ + "source" + ], + + "ka": [ + "source" + ], + + "ko": [ + "source" + ], + + "ne": [ + "source" + ], + "nl": [ - "audio_source" + "audio_source", + "source" + ], + + "pl": [ + "source" + ], + + "pt": [ + "source" + ], + + "ru": [ + "source" + ], + + "ta": [ + "source" + ], + + "th": [ + "source" + ], + + "tl": [ + "source" + ], + + "tr": [ + "source" + ], + + "uk": [ + "source" + ], + + "vi": [ + "source" + ], + + "zh": [ + "source" + ], + + "zh_TW": [ + "source" ] }