♻️ Refactored news, mixed feed and call
This commit is contained in:
		
							
								
								
									
										158
									
								
								lib/screens/feed/feed_detail.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								lib/screens/feed/feed_detail.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,158 @@ | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:gap/gap.dart'; | ||||
| import 'package:html/parser.dart'; | ||||
| import 'package:html2md/html2md.dart' as html2md; | ||||
| import 'package:provider/provider.dart'; | ||||
| import 'package:relative_time/relative_time.dart'; | ||||
| import 'package:styled_widget/styled_widget.dart'; | ||||
| import 'package:surface/providers/sn_network.dart'; | ||||
| import 'package:surface/types/news.dart'; | ||||
| import 'package:surface/widgets/dialog.dart'; | ||||
| import 'package:surface/widgets/markdown_content.dart'; | ||||
| import 'package:surface/widgets/navigation/app_scaffold.dart'; | ||||
| import 'package:flutter_inappwebview/flutter_inappwebview.dart'; | ||||
| import 'package:url_launcher/url_launcher_string.dart'; | ||||
|  | ||||
| class ReaderPageScreen extends StatefulWidget { | ||||
|   final String id; | ||||
|  | ||||
|   const ReaderPageScreen({super.key, required this.id}); | ||||
|  | ||||
|   @override | ||||
|   State<ReaderPageScreen> createState() => _ReaderPageScreenState(); | ||||
| } | ||||
|  | ||||
| class _ReaderPageScreenState extends State<ReaderPageScreen> { | ||||
|   SnSubscriptionItem? _article; | ||||
|  | ||||
|   Future<void> _fetchArticle() async { | ||||
|     try { | ||||
|       final sn = context.read<SnNetworkProvider>(); | ||||
|       final resp = await sn.client.get('/cgi/re/subscriptions/${widget.id}'); | ||||
|       _article = SnSubscriptionItem.fromJson(resp.data); | ||||
|     } catch (err) { | ||||
|       if (!mounted) return; | ||||
|       context.showErrorDialog(err).then((_) { | ||||
|         if (!mounted) return; | ||||
|         Navigator.pop(context); | ||||
|       }); | ||||
|     } finally { | ||||
|       setState(() {}); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
|     _fetchArticle(); | ||||
|   } | ||||
|  | ||||
|   bool _isReadingFromReader = true; | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return AppScaffold( | ||||
|       appBar: AppBar( | ||||
|         leading: const PageBackButton(), | ||||
|         title: Text(_article?.title ?? 'loading'.tr()), | ||||
|       ), | ||||
|       body: Column( | ||||
|         children: [ | ||||
|           MaterialBanner( | ||||
|             dividerColor: Colors.transparent, | ||||
|             leading: const Icon(Icons.info), | ||||
|             content: Text(_isReadingFromReader | ||||
|                 ? 'newsReadingFromReader'.tr() | ||||
|                 : 'newsReadingFromOriginal'.tr()), | ||||
|             actions: [ | ||||
|               TextButton( | ||||
|                 child: Text('newsReadingProviderSwap').tr(), | ||||
|                 onPressed: () { | ||||
|                   setState(() => _isReadingFromReader = !_isReadingFromReader); | ||||
|                 }, | ||||
|               ), | ||||
|             ], | ||||
|           ), | ||||
|           if (_article != null && _isReadingFromReader) | ||||
|             Expanded( | ||||
|               child: Container( | ||||
|                 constraints: BoxConstraints(maxWidth: 640), | ||||
|                 child: SingleChildScrollView( | ||||
|                   child: Column( | ||||
|                     crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                     spacing: 8, | ||||
|                     children: [ | ||||
|                       Text(_article!.title, | ||||
|                           style: Theme.of(context).textTheme.titleLarge), | ||||
|                       Builder(builder: (context) { | ||||
|                         final htmlDescription = parse(_article!.description); | ||||
|                         return Text( | ||||
|                           htmlDescription.children | ||||
|                               .map((ele) => ele.text.trim()) | ||||
|                               .join(), | ||||
|                           style: Theme.of(context).textTheme.bodyMedium, | ||||
|                         ); | ||||
|                       }), | ||||
|                       Builder(builder: (context) { | ||||
|                         final date = | ||||
|                             _article!.publishedAt ?? _article!.createdAt; | ||||
|                         return Row( | ||||
|                           spacing: 2, | ||||
|                           children: [ | ||||
|                             Text(DateFormat().format(date)).textStyle( | ||||
|                                 Theme.of(context).textTheme.bodySmall!), | ||||
|                             Text(' · ') | ||||
|                                 .textStyle( | ||||
|                                     Theme.of(context).textTheme.bodySmall!) | ||||
|                                 .bold(), | ||||
|                             Text(RelativeTime(context).format(date)).textStyle( | ||||
|                                 Theme.of(context).textTheme.bodySmall!), | ||||
|                           ], | ||||
|                         ).opacity(0.75); | ||||
|                       }), | ||||
|                       Text('newsDisclaimer') | ||||
|                           .tr() | ||||
|                           .textStyle(Theme.of(context).textTheme.bodySmall!) | ||||
|                           .opacity(0.75), | ||||
|                       const Divider(), | ||||
|                       MarkdownTextContent( | ||||
|                         textScaler: TextScaler.linear(1.2), | ||||
|                         content: html2md.convert(_article!.content), | ||||
|                       ), | ||||
|                       const Divider(), | ||||
|                       InkWell( | ||||
|                         child: Row( | ||||
|                           mainAxisSize: MainAxisSize.min, | ||||
|                           children: [ | ||||
|                             Text( | ||||
|                               'Reference from original website', | ||||
|                               style: TextStyle( | ||||
|                                   decoration: TextDecoration.underline), | ||||
|                             ), | ||||
|                             const Gap(4), | ||||
|                             Icon(Icons.launch, size: 16), | ||||
|                           ], | ||||
|                         ).opacity(0.85), | ||||
|                         onTap: () { | ||||
|                           launchUrlString(_article!.url); | ||||
|                         }, | ||||
|                       ), | ||||
|                       Gap(MediaQuery.of(context).padding.bottom), | ||||
|                     ], | ||||
|                   ).padding(horizontal: 12, vertical: 16), | ||||
|                 ), | ||||
|               ).center(), | ||||
|             ) | ||||
|           else if (_article != null) | ||||
|             Expanded( | ||||
|               child: InAppWebView( | ||||
|                 key: GlobalKey(), | ||||
|                 initialUrlRequest: URLRequest(url: WebUri(_article!.url)), | ||||
|               ), | ||||
|             ), | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user