106 lines
3.5 KiB
Dart
106 lines
3.5 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
|
import 'package:gap/gap.dart';
|
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
import 'package:island/widgets/content/markdown.dart';
|
|
import 'package:url_launcher/url_launcher_string.dart';
|
|
import 'package:island/models/webfeed.dart';
|
|
import 'package:island/pods/article_detail.dart';
|
|
import 'package:island/widgets/app_scaffold.dart';
|
|
import 'package:island/widgets/loading_indicator.dart';
|
|
import 'package:html2md/html2md.dart' as html2md;
|
|
|
|
class ArticleDetailScreen extends ConsumerWidget {
|
|
final String articleId;
|
|
|
|
const ArticleDetailScreen({super.key, required this.articleId});
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final articleAsync = ref.watch(articleDetailProvider(articleId));
|
|
|
|
return AppScaffold(
|
|
body: articleAsync.when(
|
|
data:
|
|
(article) => AppScaffold(
|
|
appBar: AppBar(
|
|
leading: const BackButton(),
|
|
title: Text(article.title),
|
|
),
|
|
body: _ArticleDetailContent(article: article),
|
|
),
|
|
loading: () => const Center(child: LoadingIndicator()),
|
|
error:
|
|
(error, stackTrace) =>
|
|
Center(child: Text('Failed to load article: $error')),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _ArticleDetailContent extends HookConsumerWidget {
|
|
final SnWebArticle article;
|
|
|
|
const _ArticleDetailContent({required this.article});
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final markdownContent = useMemoized(
|
|
() => html2md.convert(article.content ?? ''),
|
|
[article],
|
|
);
|
|
|
|
return SingleChildScrollView(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
if (article.preview?.imageUrl != null)
|
|
Image.network(
|
|
article.preview!.imageUrl!,
|
|
width: double.infinity,
|
|
height: 200,
|
|
fit: BoxFit.cover,
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
article.title,
|
|
style: Theme.of(context).textTheme.headlineSmall,
|
|
),
|
|
const SizedBox(height: 8),
|
|
if (article.feed?.title != null)
|
|
Text(
|
|
article.feed!.title,
|
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
),
|
|
),
|
|
const Divider(height: 32),
|
|
if (article.content != null)
|
|
...MarkdownTextContent.buildGenerator(
|
|
isDark: Theme.of(context).brightness == Brightness.dark,
|
|
).buildWidgets(markdownContent)
|
|
else if (article.preview?.description != null)
|
|
Text(article.preview!.description!),
|
|
const Gap(24),
|
|
FilledButton(
|
|
onPressed:
|
|
() => launchUrlString(
|
|
article.url,
|
|
mode: LaunchMode.externalApplication,
|
|
),
|
|
child: const Text('Read Full Article'),
|
|
),
|
|
Gap(MediaQuery.of(context).padding.bottom),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|