Files
feeddeck/app/lib/widgets/source/add/add_source_rss.dart
Rico Berger db9363e7af [rss] Allow Users to Provide a Website URL (#72)
Users can now provide the URL of a website instead of the url of a RSS
feed via the input field for the RSS source.

This is possible because we are now trying to get and parse the RSS feed
for the provided url as usual, but if this operation fails, we try to
parse the text as html, so that we can check if it contains a
"<link type="application/rss+xml" href="RSS_FEED_URL">" tag. If this is
the case we are using this value to try to get and parse the RSS feed
again. If it fails again we are returning an error as before.
2023-11-24 23:11:50 +01:00

133 lines
3.7 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:provider/provider.dart';
import 'package:feeddeck/models/column.dart';
import 'package:feeddeck/models/source.dart';
import 'package:feeddeck/repositories/app_repository.dart';
import 'package:feeddeck/utils/api_exception.dart';
import 'package:feeddeck/utils/constants.dart';
import 'package:feeddeck/utils/openurl.dart';
import 'package:feeddeck/widgets/source/add/add_source_form.dart';
const _helpText = '''
The RSS source can be used to follow all your RSS feeds. You have to provide
the url for the RSS feed, e.g. `https://www.tagesschau.de/xml/rss2/`.
If you do not know the url of the RSS feed you can also provide the url of the
website you want to add and we try to find a RSS feed for you, e.g.
`https://www.tagesschau.de`.
''';
/// The [AddSourceRSS] widget is used to display the form to add a new RSS
/// source.
class AddSourceRSS extends StatefulWidget {
const AddSourceRSS({
super.key,
required this.column,
});
final FDColumn column;
@override
State<AddSourceRSS> createState() => _AddSourceRSSState();
}
class _AddSourceRSSState extends State<AddSourceRSS> {
final _formKey = GlobalKey<FormState>();
final _rssController = TextEditingController();
bool _isLoading = false;
String _error = '';
/// [_addSource] adds a new RSS source. To add a new RSS source the user must
/// provide the URL of the RSS feed via the [_rssController].
///
/// If a user provides the RSS feed for a YouTube channeld, Medium page or a
/// Reddit we have some special parsing in the backend to convert the the
/// provided url to the correct source type.
Future<void> _addSource() async {
setState(() {
_isLoading = true;
_error = '';
});
try {
AppRepository app = Provider.of<AppRepository>(context, listen: false);
await app.addSource(
widget.column.id,
FDSourceType.rss,
FDSourceOptions(
rss: _rssController.text,
),
);
setState(() {
_isLoading = false;
_error = '';
});
if (mounted) {
Navigator.of(context).pop();
}
} on ApiException catch (err) {
setState(() {
_isLoading = false;
_error = 'Failed to add source: ${err.message}';
});
} catch (err) {
setState(() {
_isLoading = false;
_error = 'Failed to add source: ${err.toString()}';
});
}
}
@override
void dispose() {
_rssController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AddSourceForm(
onTap: _addSource,
isLoading: _isLoading,
error: _error,
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MarkdownBody(
selectable: true,
data: _helpText,
onTapLink: (text, href, title) {
try {
if (href != null) {
openUrl(href);
}
} catch (_) {}
},
),
const SizedBox(
height: Constants.spacingMiddle,
),
TextFormField(
controller: _rssController,
keyboardType: TextInputType.text,
autocorrect: false,
enableSuggestions: true,
maxLines: 1,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'RSS Feed',
),
onFieldSubmitted: (value) => _addSource(),
),
],
),
),
);
}
}