A framework for building convergent cross-platform Nextcloud clients using Flutter.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

197 lines
6.2 KiB

part of '../neon_news.dart';
class NewsArticlePage extends StatefulWidget {
const NewsArticlePage({
required this.bloc,
required this.articlesBloc,
required this.useWebView,
this.bodyData,
this.url,
super.key,
}) : assert(useWebView || bodyData != null, 'bodyData has to be set when not using a WebView'),
assert(!useWebView || url != null, 'url has to be set when using a WebView');
final NewsArticleBloc bloc;
final NewsArticlesBloc articlesBloc;
final bool useWebView;
final String? bodyData;
final String? url;
@override
State<NewsArticlePage> createState() => _NewsArticlePageState();
}
class _NewsArticlePageState extends State<NewsArticlePage> {
WebViewController? _webviewController;
Timer? _markAsReadTimer;
@override
void initState() {
super.initState();
widget.bloc.errors.listen((final error) {
NeonException.showSnackbar(context, error);
});
WidgetsBinding.instance.addPostFrameCallback((final _) async {
if (Provider.of<NeonPlatform>(context, listen: false).canUseWakelock) {
await Wakelock.enable();
}
});
if (widget.useWebView) {
_webviewController = WebViewController()
// ignore: discarded_futures
..setJavaScriptMode(JavaScriptMode.unrestricted)
// ignore: discarded_futures
..setNavigationDelegate(
NavigationDelegate(
onPageFinished: (final _) async {
await _startMarkAsReadTimer();
},
),
)
// ignore: discarded_futures
..loadRequest(Uri.parse(widget.url!));
} else {
unawaited(_startMarkAsReadTimer());
}
}
@override
void dispose() {
_cancelMarkAsReadTimer();
super.dispose();
}
Future _startMarkAsReadTimer() async {
if (await widget.bloc.unread.first) {
if (widget.articlesBloc.options.articleDisableMarkAsReadTimeoutOption.value) {
widget.bloc.markArticleAsRead();
} else {
_markAsReadTimer = Timer(const Duration(seconds: 3), () async {
if (await widget.bloc.unread.first) {
widget.bloc.markArticleAsRead();
}
});
}
}
}
void _cancelMarkAsReadTimer() {
if (_markAsReadTimer != null) {
_markAsReadTimer!.cancel();
_markAsReadTimer = null;
}
}
Future<String> _getURL() async {
if (_webviewController != null) {
return (await _webviewController!.currentUrl())!;
}
return widget.url!;
}
@override
Widget build(final BuildContext context) => BackButtonListener(
onBackButtonPressed: () async {
if (_webviewController != null && await _webviewController!.canGoBack()) {
await _webviewController!.goBack();
return true;
}
if (mounted && Provider.of<NeonPlatform>(context, listen: false).canUseWakelock) {
await Wakelock.disable();
}
return false;
},
child: Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
actions: [
StreamBuilder<bool>(
stream: widget.bloc.starred,
builder: (final context, final starredSnapshot) {
final starred = starredSnapshot.data ?? false;
return IconButton(
onPressed: () async {
if (starred) {
widget.bloc.unstarArticle();
} else {
widget.bloc.starArticle();
}
},
tooltip: starred
? AppLocalizations.of(context).newsArticleUnstar
: AppLocalizations.of(context).newsArticleStar,
icon: Icon(starred ? Icons.star : Icons.star_outline),
);
},
),
StreamBuilder<bool>(
stream: widget.bloc.unread,
builder: (final context, final unreadSnapshot) {
final unread = unreadSnapshot.data ?? false;
return IconButton(
onPressed: () async {
if (unread) {
widget.bloc.markArticleAsRead();
} else {
widget.bloc.markArticleAsUnread();
}
},
tooltip: unread
? AppLocalizations.of(context).newsArticleMarkRead
: AppLocalizations.of(context).newsArticleMarkUnread,
icon: Icon(unread ? MdiIcons.email : MdiIcons.emailMarkAsUnread),
);
},
),
if (widget.url != null) ...[
IconButton(
onPressed: () async {
await launchUrlString(
await _getURL(),
mode: LaunchMode.externalApplication,
);
},
tooltip: AppLocalizations.of(context).newsArticleOpenLink,
icon: const Icon(Icons.open_in_new),
),
IconButton(
onPressed: () async {
await Share.share(await _getURL());
},
tooltip: AppLocalizations.of(context).newsArticleShare,
icon: const Icon(Icons.share),
),
],
],
),
body: widget.useWebView
? WebViewWidget(
controller: _webviewController!,
)
: SingleChildScrollView(
padding: const EdgeInsets.all(10),
child: Html(
data: widget.bodyData,
onLinkTap: (
final url,
final attributes,
final element,
) async {
if (url != null) {
await launchUrlString(
url,
mode: LaunchMode.externalApplication,
);
}
},
),
),
),
);
}