From bcb45673821184a806c582d80ed3242f2652bac6 Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Fri, 24 Nov 2023 17:59:59 -0500 Subject: [PATCH] More search options (#1107) --- assets/translations/bs.json | 2 +- assets/translations/cs.json | 2 +- assets/translations/de.json | 2 +- assets/translations/en.json | 2 +- assets/translations/es.json | 2 +- assets/translations/fa.json | 2 +- assets/translations/fr.json | 2 +- assets/translations/hu.json | 2 +- assets/translations/it.json | 2 +- assets/translations/ja.json | 2 +- assets/translations/nl.json | 2 +- assets/translations/pl.json | 2 +- assets/translations/pt.json | 2 +- assets/translations/ru.json | 2 +- assets/translations/sv.json | 2 +- assets/translations/tr.json | 2 +- assets/translations/vi.json | 2 +- assets/translations/zh.json | 2 +- lib/pages/add_app.dart | 59 +++++--- lib/pages/import_export.dart | 282 ++++++++++++++++++++--------------- 20 files changed, 221 insertions(+), 156 deletions(-) diff --git a/assets/translations/bs.json b/assets/translations/bs.json index f76a4a1..863f91d 100644 --- a/assets/translations/bs.json +++ b/assets/translations/bs.json @@ -223,7 +223,7 @@ "moveNonInstalledAppsToBottom": "Premjesti neinstalirane aplikacije na dno prikaza aplikacija", "gitlabPATLabel": "GitLab token za lični pristup\n(Omogućava pretraživanje i bolje otkrivanje APK-a)", "about": "O nama", - "requiresCredentialsInSettings": "Za ovo su potrebni dodatni akreditivi (u Postavkama)", + "requiresCredentialsInSettings": "{}: Za ovo su potrebni dodatni akreditivi (u Postavkama)", "checkOnStart": "Provjerite ima li novosti pri pokretanju", "tryInferAppIdFromCode": "Pokušati otkriti ID aplikacije iz izvornog koda", "removeOnExternalUninstall": "Automatski ukloni eksterno deinstalirane aplikacije", diff --git a/assets/translations/cs.json b/assets/translations/cs.json index 613a348..0ec6c30 100644 --- a/assets/translations/cs.json +++ b/assets/translations/cs.json @@ -223,7 +223,7 @@ "moveNonInstalledAppsToBottom": "Přesunout nenainstalované aplikace na konec zobrazení Aplikace", "gitlabPATLabel": "GitLab Personal Access Token\n(Umožňuje vyhledávání a lepší zjišťování APK)", "about": "About", - "requiresCredentialsInSettings": "Vyžaduje další pověření (v nastavení)", + "requiresCredentialsInSettings": "{}: Vyžaduje další pověření (v nastavení)", "checkOnStart": "Zkontrolovat jednou při spuštění", "tryInferAppIdFromCode": "Pokusit se určit ID aplikace ze zdrojového kódu", "removeOnExternalUninstall": "Automaticky odstranit externě odinstalované aplikace", diff --git a/assets/translations/de.json b/assets/translations/de.json index f0352ec..f7ae62a 100644 --- a/assets/translations/de.json +++ b/assets/translations/de.json @@ -223,7 +223,7 @@ "moveNonInstalledAppsToBottom": "Nicht installierte Apps ans Ende der Apps Ansicht verschieben", "gitlabPATLabel": "GitLab Personal Access Token\n(Aktiviert Suche und bessere APK Entdeckung)", "about": "Über", - "requiresCredentialsInSettings": "Benötigt zusätzliche Anmeldedaten (in den Einstellungen)", + "requiresCredentialsInSettings": "{}: Benötigt zusätzliche Anmeldedaten (in den Einstellungen)", "checkOnStart": "Überprüfe einmalig beim Start", "tryInferAppIdFromCode": "Versuche, die App-ID aus dem Quellcode zu ermitteln", "removeOnExternalUninstall": "Automatisches Entfernen von extern deinstallierten Apps", diff --git a/assets/translations/en.json b/assets/translations/en.json index ed85d8d..acff0d4 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -223,7 +223,7 @@ "moveNonInstalledAppsToBottom": "Move non-installed Apps to bottom of Apps view", "gitlabPATLabel": "GitLab Personal Access Token\n(Enables Search and Better APK Discovery)", "about": "About", - "requiresCredentialsInSettings": "This needs additional credentials (in Settings)", + "requiresCredentialsInSettings": "{} needs additional credentials (in Settings)", "checkOnStart": "Check for updates on startup", "tryInferAppIdFromCode": "Try inferring App ID from source code", "removeOnExternalUninstall": "Automatically remove externally uninstalled Apps", diff --git a/assets/translations/es.json b/assets/translations/es.json index 9ffd926..7792f93 100644 --- a/assets/translations/es.json +++ b/assets/translations/es.json @@ -223,7 +223,7 @@ "moveNonInstalledAppsToBottom": "Move non-installed Apps to bottom of Apps view", "gitlabPATLabel": "GitLab Personal Access Token\n(Enables Search and Better APK Discovery)", "about": "About", - "requiresCredentialsInSettings": "This needs additional credentials (in Settings)", + "requiresCredentialsInSettings": "{}: This needs additional credentials (in Settings)", "checkOnStart": "Check for updates on startup", "tryInferAppIdFromCode": "Try inferring App ID from source code", "removeOnExternalUninstall": "Automatically remove externally uninstalled Apps", diff --git a/assets/translations/fa.json b/assets/translations/fa.json index 1563556..a3eb511 100644 --- a/assets/translations/fa.json +++ b/assets/translations/fa.json @@ -223,7 +223,7 @@ "moveNonInstalledAppsToBottom": "برنامه های نصب نشده را به نمای پایین برنامه ها منتقل کنید", "gitlabPATLabel": "رمز دسترسی شخصی GitLab\n(جستجو و کشف بهتر APK را فعال میکند)", "about": "درباره", - "requiresCredentialsInSettings": "این به اعتبارنامه های اضافی نیاز دارد (در تنظیمات)", + "requiresCredentialsInSettings": "{}: این به اعتبارنامه های اضافی نیاز دارد (در تنظیمات)", "checkOnStart": "بررسی در شروع", "tryInferAppIdFromCode": "شناسه برنامه را از کد منبع استنباط کنید", "removeOnExternalUninstall": "حذف خودکار برنامه های حذف نصب شده خارجی", diff --git a/assets/translations/fr.json b/assets/translations/fr.json index eaab8c0..7b65f5d 100644 --- a/assets/translations/fr.json +++ b/assets/translations/fr.json @@ -223,7 +223,7 @@ "moveNonInstalledAppsToBottom": "Move non-installed Apps to bottom of Apps view", "gitlabPATLabel": "GitLab Personal Access Token\n(Enables Search and Better APK Discovery)", "about": "About", - "requiresCredentialsInSettings": "This needs additional credentials (in Settings)", + "requiresCredentialsInSettings": "{}: This needs additional credentials (in Settings)", "checkOnStart": "Check for updates on startup", "tryInferAppIdFromCode": "Try inferring App ID from source code", "removeOnExternalUninstall": "Automatically remove externally uninstalled Apps", diff --git a/assets/translations/hu.json b/assets/translations/hu.json index e1258dd..da95328 100644 --- a/assets/translations/hu.json +++ b/assets/translations/hu.json @@ -223,7 +223,7 @@ "moveNonInstalledAppsToBottom": "Helyezze át a nem telepített appokat az App nézet aljára", "gitlabPATLabel": "GitLab Personal Access Token\n(Engedélyezi a Keresést és jobb APK felfedezés)", "about": "Rólunk", - "requiresCredentialsInSettings": "Ehhez további hitelesítő adatokra van szükség (a Beállításokban)", + "requiresCredentialsInSettings": "{}: Ehhez további hitelesítő adatokra van szükség (a Beállításokban)", "checkOnStart": "Egyszer az alkalmazás indításakor is", "tryInferAppIdFromCode": "Próbálja kikövetkeztetni az app azonosítót a forráskódból", "removeOnExternalUninstall": "A külsőleg eltávolított appok auto. eltávolítása", diff --git a/assets/translations/it.json b/assets/translations/it.json index 277c1aa..4c2eebd 100644 --- a/assets/translations/it.json +++ b/assets/translations/it.json @@ -223,7 +223,7 @@ "moveNonInstalledAppsToBottom": "Sposta le app non installate in fondo alla lista", "gitlabPATLabel": "GitLab Personal Access Token\n(attiva la ricerca e migliora la rilevazione di apk)", "about": "Informazioni", - "requiresCredentialsInSettings": "Servono credenziali aggiuntive (in Impostazioni)", + "requiresCredentialsInSettings": "{}: Servono credenziali aggiuntive (in Impostazioni)", "checkOnStart": "Controlla una volta all'avvio", "tryInferAppIdFromCode": "Prova a dedurre l'ID dell'app dal codice sorgente", "removeOnExternalUninstall": "Rimuovi automaticamente app disinstallate esternamente", diff --git a/assets/translations/ja.json b/assets/translations/ja.json index ff7cdda..55a8252 100644 --- a/assets/translations/ja.json +++ b/assets/translations/ja.json @@ -223,7 +223,7 @@ "moveNonInstalledAppsToBottom": "未インストールのアプリをアプリ一覧の下部に移動させる", "gitlabPATLabel": "GitLab パーソナルアクセストークン\n(検索とより良いAPK検出の有効化)", "about": "概要", - "requiresCredentialsInSettings": "これには追加の認証が必要です (設定にて)", + "requiresCredentialsInSettings": "{}: これには追加の認証が必要です (設定にて)", "checkOnStart": "起動時にアップデートを確認する", "tryInferAppIdFromCode": "ソースコードからApp IDを推測する", "removeOnExternalUninstall": "外部でアンインストールされたアプリを自動的に削除する", diff --git a/assets/translations/nl.json b/assets/translations/nl.json index 9b6addc..6e7c846 100644 --- a/assets/translations/nl.json +++ b/assets/translations/nl.json @@ -223,7 +223,7 @@ "moveNonInstalledAppsToBottom": "Verplaats niet-geïnstalleerde apps naar de onderkant van de apps-weergave", "gitlabPATLabel": "GitLab Personal Access Token\n(Maakt het mogelijk beter te zoeken naar APK's)", "about": "Over", - "requiresCredentialsInSettings": "Dit vereist aanvullende referenties (in Instellingen)", + "requiresCredentialsInSettings": "{}: Dit vereist aanvullende referenties (in Instellingen)", "checkOnStart": "Controleren op updates bij opstarten", "tryInferAppIdFromCode": "Probeer de app-ID af te leiden uit de broncode", "removeOnExternalUninstall": "Automatisch extern verwijderde apps verwijderen", diff --git a/assets/translations/pl.json b/assets/translations/pl.json index a06fd38..cd6d26f 100644 --- a/assets/translations/pl.json +++ b/assets/translations/pl.json @@ -223,7 +223,7 @@ "moveNonInstalledAppsToBottom": "Przenieś niezainstalowane aplikacje na dół widoku aplikacji", "gitlabPATLabel": "Osobisty token dostępu GitLab\n(Umożliwia wyszukiwanie i lepsze wykrywanie APK)", "about": "Więcej informacji", - "requiresCredentialsInSettings": "Wymaga to dodatkowych poświadczeń (w Ustawieniach)", + "requiresCredentialsInSettings": "{}: Wymaga to dodatkowych poświadczeń (w Ustawieniach)", "checkOnStart": "Sprawdź aktualizacje przy uruchomieniu", "tryInferAppIdFromCode": "Spróbuj wywnioskować identyfikator aplikacji z kodu źródłowego", "removeOnExternalUninstall": "Automatyczne usuń odinstalowane zewnętrznie aplikacje", diff --git a/assets/translations/pt.json b/assets/translations/pt.json index b828ee0..25345a8 100644 --- a/assets/translations/pt.json +++ b/assets/translations/pt.json @@ -223,7 +223,7 @@ "moveNonInstalledAppsToBottom": "Mover Apps não instalados para o fundo da visão de Apps", "gitlabPATLabel": "Token de Acceso Pessoal do Gitlab\n(Ativa Pesquisa e Melhor Descoberta de APKs)", "about": "Sobre", - "requiresCredentialsInSettings": "Isso requer credenciais adicionais (em Configurações)", + "requiresCredentialsInSettings": "{}: Isso requer credenciais adicionais (em Configurações)", "checkOnStart": "Checar por atualizações ao iniciar ", "tryInferAppIdFromCode": "Tente inferir o ID do App pelo código fonte", "removeOnExternalUninstall": "Remover automaticamente Apps desinstalados externamente", diff --git a/assets/translations/ru.json b/assets/translations/ru.json index 1dcb20d..4b91bc4 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -223,7 +223,7 @@ "moveNonInstalledAppsToBottom": "Отображать неустановленные приложения внизу списка", "gitlabPATLabel": "Персональный токен доступа GitLab\n(включает поиск и улучшает обнаружение APK)", "about": "Описание", - "requiresCredentialsInSettings": "Для этого требуются дополнительные учетные данные (в настройках)", + "requiresCredentialsInSettings": "{}: Для этого требуются дополнительные учетные данные (в настройках)", "checkOnStart": "Проверять наличие обновлений при запуске", "tryInferAppIdFromCode": "Попытаться определить ID приложения из исходного кода", "removeOnExternalUninstall": "Автоматически убирать из списка удаленные извне приложения", diff --git a/assets/translations/sv.json b/assets/translations/sv.json index 06aeabb..4f680ef 100644 --- a/assets/translations/sv.json +++ b/assets/translations/sv.json @@ -223,7 +223,7 @@ "moveNonInstalledAppsToBottom": "Move non-installed Apps to bottom of Apps view", "gitlabPATLabel": "GitLab Personal Access Token\n(Enables Search and Better APK Discovery)", "about": "Om", - "requiresCredentialsInSettings": "This needs additional credentials (in Settings)", + "requiresCredentialsInSettings": "{}: This needs additional credentials (in Settings)", "checkOnStart": "Kolla efter uppdateringar vid start", "tryInferAppIdFromCode": "Try inferring App ID from source code", "removeOnExternalUninstall": "Automatically remove externally uninstalled Apps", diff --git a/assets/translations/tr.json b/assets/translations/tr.json index 04dc7ff..724a08a 100644 --- a/assets/translations/tr.json +++ b/assets/translations/tr.json @@ -223,7 +223,7 @@ "moveNonInstalledAppsToBottom": "Yüklenmemiş Uygulamaları Uygulamalar Görünümünün Altına Taşı", "gitlabPATLabel": "GitLab Kişisel Erişim Belirteci\n(Arama ve Daha İyi APK Keşfi İçin)", "about": "Hakkında", - "requiresCredentialsInSettings": "Bu, ek kimlik bilgilerine ihtiyaç duyar (Ayarlar'da)", + "requiresCredentialsInSettings": "{}: Bu, ek kimlik bilgilerine ihtiyaç duyar (Ayarlar'da)", "checkOnStart": "Başlangıçta güncellemeleri kontrol et", "tryInferAppIdFromCode": "Uygulama kimliğini kaynak kodundan çıkarma girişimi", "removeOnExternalUninstall": "Harici kaldırmada otomatik olarak kaldırılan uygulamalar", diff --git a/assets/translations/vi.json b/assets/translations/vi.json index 4f13aa9..50a78e7 100644 --- a/assets/translations/vi.json +++ b/assets/translations/vi.json @@ -223,7 +223,7 @@ "moveNonInstalledAppsToBottom": "Di chuyển Ứng dụng chưa được cài đặt xuống cuối chế độ xem Ứng dụng", "gitlabPATLabel": "Mã thông báo truy cập cá nhân GitLab\n(Cho phép tìm kiếm và khám phá APK tốt hơn)", "about": "Giới thiệu", - "requiresCredentialsInSettings": "Điều này cần thông tin xác thực bổ sung (trong Cài đặt)", + "requiresCredentialsInSettings": "{}: Điều này cần thông tin xác thực bổ sung (trong Cài đặt)", "checkOnStart": "Kiểm tra các bản cập nhật khi khởi động", "tryInferAppIdFromCode": "Thử suy ra ID ứng dụng từ mã nguồn", "removeOnExternalUninstall": "Tự động xóa ứng dụng đã gỡ cài đặt bên ngoài", diff --git a/assets/translations/zh.json b/assets/translations/zh.json index bf031fd..48dec33 100644 --- a/assets/translations/zh.json +++ b/assets/translations/zh.json @@ -223,7 +223,7 @@ "moveNonInstalledAppsToBottom": "将未安装应用置底", "gitlabPATLabel": "GitLab 个人访问令牌(启用搜索功能并增强 APK 发现)", "about": "相关文档", - "requiresCredentialsInSettings": "此功能需要额外的凭据(在“设置”中添加)", + "requiresCredentialsInSettings": "{}: 此功能需要额外的凭据(在“设置”中添加)", "checkOnStart": "启动时进行一次检查", "tryInferAppIdFromCode": "尝试从源代码推断应用 ID", "removeOnExternalUninstall": "自动删除已卸载的外部应用", diff --git a/lib/pages/add_app.dart b/lib/pages/add_app.dart index 08f3024..c578a6e 100644 --- a/lib/pages/add_app.dart +++ b/lib/pages/add_app.dart @@ -254,13 +254,30 @@ class _AddAppPageState extends State { ], ); - runSearch() async { + runSearch({bool filtered = true}) async { setState(() { searching = true; }); + var sourceStrings = >{}; + sourceProvider.sources + .where((e) => e.canSearch && !e.excludeFromMassSearch) + .forEach((s) { + sourceStrings[s.name] = [s.name]; + }); try { + var searchSources = await showDialog?>( + context: context, + builder: (BuildContext ctx) { + return SelectionModal( + entries: sourceStrings, + selectedByDefault: true, + onlyOneSelectionAllowed: false, + titlesAreLinks: false, + ); + }) ?? + []; var results = await Future.wait(sourceProvider.sources - .where((e) => e.canSearch && !e.excludeFromMassSearch) + .where((e) => searchSources.contains(e.name)) .map((e) async { try { return await e.search(searchQuery); @@ -268,6 +285,8 @@ class _AddAppPageState extends State { if (err is! CredsNeededError) { rethrow; } else { + err.unexpected = true; + showError(err, context); return >{}; } } @@ -297,8 +316,8 @@ class _AddAppPageState extends State { : await showDialog?>( context: context, builder: (BuildContext ctx) { - return UrlSelectionModal( - urlsWithDescriptions: res, + return SelectionModal( + entries: res, selectedByDefault: false, onlyOneSelectionAllowed: true, ); @@ -470,23 +489,21 @@ class _AddAppPageState extends State { const SizedBox( height: 16, ), - ...sourceProvider.sources - .map((e) => GestureDetector( - onTap: e.host != null - ? () { - launchUrlString('https://${e.host}', - mode: LaunchMode.externalApplication); - } - : null, - child: Text( - '${e.name}${e.enforceTrackOnly ? ' ${tr('trackOnlyInBrackets')}' : ''}${e.canSearch ? ' ${tr('searchableInBrackets')}' : ''}', - style: TextStyle( - decoration: e.host != null - ? TextDecoration.underline - : TextDecoration.none, - fontStyle: FontStyle.italic), - ))) - + ...sourceProvider.sources.map((e) => GestureDetector( + onTap: e.host != null + ? () { + launchUrlString('https://${e.host}', + mode: LaunchMode.externalApplication); + } + : null, + child: Text( + '${e.name}${e.enforceTrackOnly ? ' ${tr('trackOnlyInBrackets')}' : ''}${e.canSearch ? ' ${tr('searchableInBrackets')}' : ''}', + style: TextStyle( + decoration: e.host != null + ? TextDecoration.underline + : TextDecoration.none, + fontStyle: FontStyle.italic), + ))) ]); return Scaffold( diff --git a/lib/pages/import_export.dart b/lib/pages/import_export.dart index 8f76e80..032180f 100644 --- a/lib/pages/import_export.dart +++ b/lib/pages/import_export.dart @@ -208,8 +208,8 @@ class _ImportExportPageState extends State { await showDialog?>( context: context, builder: (BuildContext ctx) { - return UrlSelectionModal( - urlsWithDescriptions: urlsWithDescriptions, + return SelectionModal( + entries: urlsWithDescriptions, selectedByDefault: false, ); }); @@ -269,8 +269,7 @@ class _ImportExportPageState extends State { await showDialog?>( context: context, builder: (BuildContext ctx) { - return UrlSelectionModal( - urlsWithDescriptions: urlsWithDescriptions); + return SelectionModal(entries: urlsWithDescriptions); }); if (selectedUrls != null) { var errors = await appsProvider.addAppsByURL(selectedUrls); @@ -300,6 +299,11 @@ class _ImportExportPageState extends State { }); } + var sourceStrings = >{}; + sourceProvider.sources.where((e) => e.canSearch).forEach((s) { + sourceStrings[s.name] = [s.name]; + }); + return Scaffold( backgroundColor: Theme.of(context).colorScheme.surface, body: CustomScrollView(slivers: [ @@ -409,6 +413,49 @@ class _ImportExportPageState extends State { const Divider( height: 32, ), + Row( + children: [ + Expanded( + child: TextButton( + onPressed: importInProgress + ? null + : () async { + var searchSourceName = + await showDialog< + List?>( + context: context, + builder: + (BuildContext + ctx) { + return SelectionModal( + entries: + sourceStrings, + selectedByDefault: + false, + onlyOneSelectionAllowed: + true, + titlesAreLinks: + false, + ); + }) ?? + []; + var searchSource = + sourceProvider.sources + .where((e) => + searchSourceName + .contains( + e.name)) + .toList(); + if (searchSource.isNotEmpty) { + runSourceSearch( + searchSource[0]); + } + }, + child: Text(tr('searchX', + args: [tr('source')])))), + ], + ), + const SizedBox(height: 8), TextButton( onPressed: importInProgress ? null : urlListImport, @@ -424,39 +471,19 @@ class _ImportExportPageState extends State { )), ], ), - ...sourceProvider.sources - .where((element) => element.canSearch) - .map((source) => Column( - crossAxisAlignment: - CrossAxisAlignment.stretch, - children: [ - const SizedBox(height: 8), - TextButton( - onPressed: importInProgress - ? null - : () { - runSourceSearch(source); - }, - child: Text( - tr('searchX', args: [source.name]))) - ])) - , - ...sourceProvider.massUrlSources - .map((source) => Column( - crossAxisAlignment: - CrossAxisAlignment.stretch, - children: [ - const SizedBox(height: 8), - TextButton( - onPressed: importInProgress - ? null - : () { - runMassSourceImport(source); - }, - child: Text( - tr('importX', args: [source.name]))) - ])) - , + ...sourceProvider.massUrlSources.map((source) => Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const SizedBox(height: 8), + TextButton( + onPressed: importInProgress + ? null + : () { + runMassSourceImport(source); + }, + child: Text( + tr('importX', args: [source.name]))) + ])), const Spacer(), const Divider( height: 32, @@ -532,38 +559,40 @@ class _ImportErrorDialogState extends State { } // ignore: must_be_immutable -class UrlSelectionModal extends StatefulWidget { - UrlSelectionModal( +class SelectionModal extends StatefulWidget { + SelectionModal( {super.key, - required this.urlsWithDescriptions, + required this.entries, this.selectedByDefault = true, - this.onlyOneSelectionAllowed = false}); + this.onlyOneSelectionAllowed = false, + this.titlesAreLinks = true}); - Map> urlsWithDescriptions; + Map> entries; bool selectedByDefault; bool onlyOneSelectionAllowed; + bool titlesAreLinks; @override - State createState() => _UrlSelectionModalState(); + State createState() => _SelectionModalState(); } -class _UrlSelectionModalState extends State { - Map>, bool> urlWithDescriptionSelections = {}; +class _SelectionModalState extends State { + Map>, bool> entrySelections = {}; @override void initState() { super.initState(); - for (var url in widget.urlsWithDescriptions.entries) { - urlWithDescriptionSelections.putIfAbsent(url, + for (var url in widget.entries.entries) { + entrySelections.putIfAbsent(url, () => widget.selectedByDefault && !widget.onlyOneSelectionAllowed); } if (widget.selectedByDefault && widget.onlyOneSelectionAllowed) { - selectOnlyOne(widget.urlsWithDescriptions.entries.first.key); + selectOnlyOne(widget.entries.entries.first.key); } } selectOnlyOne(String url) { - for (var uwd in urlWithDescriptionSelections.keys) { - urlWithDescriptionSelections[uwd] = uwd.key == url; + for (var e in entrySelections.keys) { + entrySelections[e] = e.key == url; } } @@ -571,73 +600,88 @@ class _UrlSelectionModalState extends State { Widget build(BuildContext context) { return AlertDialog( scrollable: true, - title: Text( - widget.onlyOneSelectionAllowed ? tr('selectURL') : tr('selectURLs')), + title: Text(tr('select')), content: Column(children: [ - ...urlWithDescriptionSelections.keys.map((urlWithD) { + ...entrySelections.keys.map((entry) { selectThis(bool? value) { setState(() { value ??= false; if (value! && widget.onlyOneSelectionAllowed) { - selectOnlyOne(urlWithD.key); + selectOnlyOne(entry.key); } else { - urlWithDescriptionSelections[urlWithD] = value!; + entrySelections[entry] = value!; } }); } var urlLink = GestureDetector( - onTap: () { - launchUrlString(urlWithD.key, - mode: LaunchMode.externalApplication); - }, + onTap: !widget.titlesAreLinks + ? null + : () { + launchUrlString(entry.key, + mode: LaunchMode.externalApplication); + }, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - urlWithD.value[0], - style: const TextStyle( - decoration: TextDecoration.underline, + entry.value.isEmpty ? entry.key : entry.value[0], + style: TextStyle( + decoration: widget.titlesAreLinks + ? TextDecoration.underline + : null, fontWeight: FontWeight.bold), textAlign: TextAlign.start, ), - Text( - Uri.parse(urlWithD.key).host, - style: const TextStyle( - decoration: TextDecoration.underline, fontSize: 12), - ) + if (widget.titlesAreLinks) + Text( + Uri.parse(entry.key).host, + style: const TextStyle( + decoration: TextDecoration.underline, fontSize: 12), + ) ], )); - var descriptionText = Text( - urlWithD.value[1].length > 128 - ? '${urlWithD.value[1].substring(0, 128)}...' - : urlWithD.value[1], - style: const TextStyle(fontStyle: FontStyle.italic, fontSize: 12), - ); + var descriptionText = entry.value.length <= 1 + ? const SizedBox.shrink() + : Text( + entry.value[1].length > 128 + ? '${entry.value[1].substring(0, 128)}...' + : entry.value[1], + style: const TextStyle( + fontStyle: FontStyle.italic, fontSize: 12), + ); - var selectedUrlsWithDs = urlWithDescriptionSelections.entries - .where((e) => e.value) - .toList(); + var selectedEntries = + entrySelections.entries.where((e) => e.value).toList(); var singleSelectTile = ListTile( - title: urlLink, - subtitle: GestureDetector( - onTap: () { - setState(() { - selectOnlyOne(urlWithD.key); - }); - }, - child: descriptionText, - ), - leading: Radio( - value: urlWithD.key, - groupValue: selectedUrlsWithDs.isEmpty + title: GestureDetector( + onTap: widget.titlesAreLinks ? null - : selectedUrlsWithDs.first.key.key, + : () { + selectThis(!(entrySelections[entry] ?? false)); + }, + child: urlLink, + ), + subtitle: entry.value.length <= 1 + ? null + : GestureDetector( + onTap: () { + setState(() { + selectOnlyOne(entry.key); + }); + }, + child: descriptionText, + ), + leading: Radio( + value: entry.key, + groupValue: selectedEntries.isEmpty + ? null + : selectedEntries.first.key.key, onChanged: (value) { setState(() { - selectOnlyOne(urlWithD.key); + selectOnlyOne(entry.key); }); }, ), @@ -645,7 +689,7 @@ class _UrlSelectionModalState extends State { var multiSelectTile = Row(children: [ Checkbox( - value: urlWithDescriptionSelections[urlWithD], + value: entrySelections[entry], onChanged: (value) { selectThis(value); }), @@ -657,20 +701,30 @@ class _UrlSelectionModalState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ - const SizedBox( - height: 8, + SizedBox( + height: entry.value.length <= 1 ? 16 : 8, ), - urlLink, GestureDetector( - onTap: () { - selectThis( - !(urlWithDescriptionSelections[urlWithD] ?? false)); - }, - child: descriptionText, + onTap: widget.titlesAreLinks + ? null + : () { + selectThis(!(entrySelections[entry] ?? false)); + }, + child: urlLink, ), - const SizedBox( - height: 8, - ) + entry.value.length <= 1 + ? const SizedBox.shrink() + : GestureDetector( + onTap: () { + selectThis(!(entrySelections[entry] ?? false)); + }, + child: descriptionText, + ), + entry.value.length <= 1 + ? const SizedBox.shrink() + : const SizedBox( + height: 8, + ) ], )) ]); @@ -687,24 +741,18 @@ class _UrlSelectionModalState extends State { }, child: Text(tr('cancel'))), TextButton( - onPressed: - urlWithDescriptionSelections.values.where((b) => b).isEmpty - ? null - : () { - Navigator.of(context).pop(urlWithDescriptionSelections - .entries - .where((entry) => entry.value) - .map((e) => e.key.key) - .toList()); - }, + onPressed: entrySelections.values.where((b) => b).isEmpty + ? null + : () { + Navigator.of(context).pop(entrySelections.entries + .where((entry) => entry.value) + .map((e) => e.key.key) + .toList()); + }, child: Text(widget.onlyOneSelectionAllowed ? tr('pick') : tr('importX', args: [ - plural( - 'url', - urlWithDescriptionSelections.values - .where((b) => b) - .length) + plural('url', entrySelections.values.where((b) => b).length) ]))) ], );