Added "URLs in file (like OPML)" import

This commit is contained in:
Imran Remtulla
2023-02-24 21:54:27 -05:00
parent dae5a67652
commit 728dafcc28
8 changed files with 124 additions and 81 deletions

View File

@@ -217,6 +217,7 @@
"releaseDateAsVersionExplanation": "Diese Option sollte nur für Apps verwendet werden, bei denen die Versionserkennung nicht korrekt funktioniert, aber ein Veröffentlichungsdatum verfügbar ist.", "releaseDateAsVersionExplanation": "Diese Option sollte nur für Apps verwendet werden, bei denen die Versionserkennung nicht korrekt funktioniert, aber ein Veröffentlichungsdatum verfügbar ist.",
"changes": "Änderungen", "changes": "Änderungen",
"releaseDate": "Veröffentlichungsdatum", "releaseDate": "Veröffentlichungsdatum",
"importFromURLsInFile": "Import from URLs in File (like OPML)",
"removeAppQuestion": { "removeAppQuestion": {
"one": "App entfernen?", "one": "App entfernen?",
"other": "App entfernen?" "other": "App entfernen?"

View File

@@ -217,6 +217,7 @@
"releaseDateAsVersionExplanation": "This option should only be used for Apps where version detection does not work correctly, but a release date is available.", "releaseDateAsVersionExplanation": "This option should only be used for Apps where version detection does not work correctly, but a release date is available.",
"changes": "Changes", "changes": "Changes",
"releaseDate": "Release Date", "releaseDate": "Release Date",
"importFromURLsInFile": "Import from URLs in File (like OPML)",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Remove App?", "one": "Remove App?",
"other": "Remove Apps?" "other": "Remove Apps?"

View File

@@ -217,6 +217,7 @@
"releaseDateAsVersionExplanation": "This option should only be used for Apps where version detection does not work correctly, but a release date is available.", "releaseDateAsVersionExplanation": "This option should only be used for Apps where version detection does not work correctly, but a release date is available.",
"changes": "Changes", "changes": "Changes",
"releaseDate": "Release Date", "releaseDate": "Release Date",
"importFromURLsInFile": "Import from URLs in File (like OPML)",
"removeAppQuestion": { "removeAppQuestion": {
"one": "برنامه حذف شود؟", "one": "برنامه حذف شود؟",
"other": "برنامه ها حذف شوند؟" "other": "برنامه ها حذف شوند؟"

View File

@@ -216,6 +216,7 @@
"releaseDateAsVersionExplanation": "Ezt a beállítást csak olyan alkalmazásoknál szabad használni, ahol a verzió érzékelése nem működik megfelelően, de elérhető a kiadás dátuma.", "releaseDateAsVersionExplanation": "Ezt a beállítást csak olyan alkalmazásoknál szabad használni, ahol a verzió érzékelése nem működik megfelelően, de elérhető a kiadás dátuma.",
"changes": "Változtatások", "changes": "Változtatások",
"releaseDate": "Kiadás dátuma", "releaseDate": "Kiadás dátuma",
"importFromURLsInFile": "Import from URLs in File (like OPML)",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Eltávolítja az alkalmazást?", "one": "Eltávolítja az alkalmazást?",
"other": "Eltávolítja az alkalmazást?" "other": "Eltávolítja az alkalmazást?"

View File

@@ -217,6 +217,7 @@
"releaseDateAsVersionExplanation": "Questa opzione dovrebbe essere usata solo per le App in cui il rilevamento della versione non funziona correttamente, ma è disponibile una data di rilascio.", "releaseDateAsVersionExplanation": "Questa opzione dovrebbe essere usata solo per le App in cui il rilevamento della versione non funziona correttamente, ma è disponibile una data di rilascio.",
"changes": "Novità", "changes": "Novità",
"releaseDate": "Data di rilascio", "releaseDate": "Data di rilascio",
"importFromURLsInFile": "Import from URLs in File (like OPML)",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Rimuovere l'App?", "one": "Rimuovere l'App?",
"other": "Rimuovere le App?" "other": "Rimuovere le App?"

View File

@@ -217,6 +217,7 @@
"releaseDateAsVersionExplanation": "このオプションは、バージョン検出が正しく機能しないアプリで、リリース日が利用可能な場合にのみ使用する必要があります。", "releaseDateAsVersionExplanation": "このオプションは、バージョン検出が正しく機能しないアプリで、リリース日が利用可能な場合にのみ使用する必要があります。",
"changes": "変更点", "changes": "変更点",
"releaseDate": "リリース日", "releaseDate": "リリース日",
"importFromURLsInFile": "Import from URLs in File (like OPML)",
"removeAppQuestion": { "removeAppQuestion": {
"one": "アプリを削除しますか?", "one": "アプリを削除しますか?",
"other": "アプリを削除しますか?" "other": "アプリを削除しますか?"

View File

@@ -217,6 +217,7 @@
"releaseDateAsVersionExplanation": "This option should only be used for Apps where version detection does not work correctly, but a release date is available.", "releaseDateAsVersionExplanation": "This option should only be used for Apps where version detection does not work correctly, but a release date is available.",
"changes": "Changes", "changes": "Changes",
"releaseDate": "Release Date", "releaseDate": "Release Date",
"importFromURLsInFile": "Import from URLs in File (like OPML)",
"removeAppQuestion": { "removeAppQuestion": {
"one": "删除应用?", "one": "删除应用?",
"other": "删除应用?" "other": "删除应用?"

View File

@@ -41,6 +41,66 @@ class _ImportExportPageState extends State<ImportExportPage> {
), ),
); );
urlListImport({String? initValue, bool overrideInitValid = false}) {
showDialog<Map<String, dynamic>?>(
context: context,
builder: (BuildContext ctx) {
return GeneratedFormModal(
initValid: overrideInitValid,
title: tr('importFromURLList'),
items: [
[
GeneratedFormTextField('appURLList',
defaultValue: initValue ?? '',
label: tr('appURLList'),
max: 7,
additionalValidators: [
(dynamic value) {
if (value != null && value.isNotEmpty) {
var lines = value.trim().split('\n');
for (int i = 0; i < lines.length; i++) {
try {
sourceProvider.getSource(lines[i]);
} catch (e) {
return '${tr('line')} ${i + 1}: $e';
}
}
}
return null;
}
])
]
],
);
}).then((values) {
if (values != null) {
var urls = (values['appURLList'] as String).split('\n');
setState(() {
importInProgress = true;
});
appsProvider.addAppsByURL(urls).then((errors) {
if (errors.isEmpty) {
showError(tr('importedX', args: [plural('apps', urls.length)]),
context);
} else {
showDialog(
context: context,
builder: (BuildContext ctx) {
return ImportErrorDialog(
urlsLength: urls.length, errors: errors);
});
}
}).catchError((e) {
showError(e, context);
}).whenComplete(() {
setState(() {
importInProgress = false;
});
});
}
});
}
return Scaffold( return Scaffold(
backgroundColor: Theme.of(context).colorScheme.surface, backgroundColor: Theme.of(context).colorScheme.surface,
body: CustomScrollView(slivers: <Widget>[ body: CustomScrollView(slivers: <Widget>[
@@ -150,88 +210,60 @@ class _ImportExportPageState extends State<ImportExportPage> {
], ],
) )
else else
const Divider( Column(
height: 32, children: [
), const Divider(
TextButton( height: 32,
onPressed: importInProgress ),
? null TextButton(
: () { onPressed: importInProgress
showDialog<Map<String, dynamic>?>( ? null
context: context, : () {
builder: (BuildContext ctx) { urlListImport();
return GeneratedFormModal( },
title: tr('importFromURLList'), child: Text(
items: [ tr('importFromURLList'),
[ )),
GeneratedFormTextField( const SizedBox(height: 8),
'appURLList', TextButton(
label: tr('appURLList'), onPressed: importInProgress
max: 7, ? null
additionalValidators: [ : () {
(dynamic value) { FilePicker.platform
if (value != null && .pickFiles()
value.isNotEmpty) { .then((result) {
var lines = value if (result != null) {
.trim() urlListImport(
.split('\n'); overrideInitValid: true,
for (int i = 0; initValue:
i < lines.length; RegExp('https?://[^"]+')
i++) { .allMatches(File(result
try { .files
sourceProvider .single
.getSource( .path!)
lines[i]); .readAsStringSync())
} catch (e) { .map((e) =>
return '${tr('line')} ${i + 1}: $e'; e.input.substring(
} e.start, e.end))
} .toSet()
} .toList()
return null; .where((url) {
} try {
]) sourceProvider
] .getSource(url);
], return true;
); } catch (e) {
}).then((values) { return false;
if (values != null) { }
var urls = }).join('\n'));
(values['appURLList'] as String) }
.split('\n');
setState(() {
importInProgress = true;
});
appsProvider
.addAppsByURL(urls)
.then((errors) {
if (errors.isEmpty) {
showError(
tr('importedX', args: [
plural('apps', urls.length)
]),
context);
} else {
showDialog(
context: context,
builder: (BuildContext ctx) {
return ImportErrorDialog(
urlsLength: urls.length,
errors: errors);
});
}
}).catchError((e) {
showError(e, context);
}).whenComplete(() {
setState(() {
importInProgress = false;
}); });
}); },
} child: Text(
}); tr('importFromURLsInFile'),
}, )),
child: Text( ],
tr('importFromURLList'), ),
)),
...sourceProvider.sources ...sourceProvider.sources
.where((element) => element.canSearch) .where((element) => element.canSearch)
.map((source) => Column( .map((source) => Column(
@@ -280,6 +312,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
if (urlsWithDescriptions if (urlsWithDescriptions
.isNotEmpty) { .isNotEmpty) {
var selectedUrls = var selectedUrls =
// ignore: use_build_context_synchronously
await showDialog< await showDialog<
List< List<
String>?>( String>?>(
@@ -314,6 +347,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
]), ]),
context); context);
} else { } else {
// ignore: use_build_context_synchronously
showDialog( showDialog(
context: context, context: context,
builder: builder:
@@ -391,6 +425,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
e.toString()) e.toString())
.toList()); .toList());
var selectedUrls = var selectedUrls =
// ignore: use_build_context_synchronously
await showDialog< await showDialog<
List<String>?>( List<String>?>(
context: context, context: context,
@@ -418,6 +453,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
]), ]),
context); context);
} else { } else {
// ignore: use_build_context_synchronously
showDialog( showDialog(
context: context, context: context,
builder: builder: