Files
2026-06-07 10:58:05 +08:00

157 lines
5.1 KiB
Dart

import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import '../../data/api/report_data_source.dart';
import '../../data/content_providers.dart';
import '../../data/models/models.dart';
import '../../data/providers.dart';
import '../../routing/app_routes.dart';
import '../../theme/yanting_tokens.dart';
import '../../widgets/badges.dart';
import '../../widgets/mini_player.dart';
import '../../widgets/page_header.dart';
import '../../widgets/states.dart';
import '../shared/report_card_widget.dart';
class FeedPage extends HookConsumerWidget {
const FeedPage({
required this.dataSource,
required this.onPlay,
this.player = const PlayerStateModel(),
this.onStartModuleAudio,
this.onToggleAudio,
this.onSeekAudio,
this.onSpeed,
super.key,
});
final ReportDataSource dataSource;
final void Function(AudioItem item) onPlay;
final PlayerStateModel player;
final void Function(
String audioId,
String reportId,
String title,
int durationSec,
)?
onStartModuleAudio;
final VoidCallback? onToggleAudio;
final void Function(int delta)? onSeekAudio;
final VoidCallback? onSpeed;
@override
Widget build(BuildContext context, WidgetRef ref) {
final currentTopic = ref.watch(recommendTopicProvider);
final snapshot = ref.watch(recommendedByTopicProvider);
const topics = ['全部', '宏观', '贵金属', '大宗', '能源', '跨资产', '央行'];
return snapshot.when(
loading: () => const LoadingState(),
error: (error, _) => ErrorState(
message: error.toString(),
onRetry: () => ref.invalidate(recommendedByTopicProvider),
),
data: (items) {
if (items.isEmpty) {
return EmptyState(
title: currentTopic == '全部' ? '暂无可推荐的研报解读' : '当前主题暂无内容',
message: currentTopic == '全部' ? '稍后再来看看最新内容' : '换个主题,或去研报页看看全部内容',
icon: currentTopic == '全部'
? Icons.inbox_outlined
: Icons.filter_alt_off,
);
}
return ListView(
padding: const EdgeInsets.fromLTRB(
YantingSpacing.screenX,
4,
YantingSpacing.screenX,
16,
),
children: [
const PageHeader(title: '研听', subtitle: '全球机构研报中文解读'),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
for (final t in topics)
Padding(
padding: const EdgeInsets.only(right: YantingSpacing.x2),
child: AppChip(
label: t,
selected: t == currentTopic,
onTap: () =>
ref.read(recommendTopicProvider.notifier).select(t),
),
),
],
),
),
const SizedBox(height: YantingSpacing.cardGap),
ReportCardWidget(
report: items.first,
hero: true,
onTap: () {
ref
.read(profileControllerProvider.notifier)
.addHistory(items.first.id);
openReportDetail(
context,
dataSource,
items.first,
player: player,
onStartAudio: onStartModuleAudio,
onToggleAudio: onToggleAudio,
onSeekAudio: onSeekAudio,
onSpeed: onSpeed,
);
},
onPlayTap: () => _playFromReport(onPlay, items.first),
),
const SizedBox(height: YantingSpacing.sectionGap),
const SectionTitle(title: '最新解读', icon: Icons.chevron_right),
for (final report in items.skip(1)) ...[
ReportCardWidget(
report: report,
onTap: () {
ref
.read(profileControllerProvider.notifier)
.addHistory(report.id);
openReportDetail(
context,
dataSource,
report,
player: player,
onStartAudio: onStartModuleAudio,
onToggleAudio: onToggleAudio,
onSeekAudio: onSeekAudio,
onSpeed: onSpeed,
);
},
onPlayTap: () => _playFromReport(onPlay, report),
),
const SizedBox(height: YantingSpacing.x3),
],
],
);
},
);
}
}
void _playFromReport(
void Function(AudioItem item) onPlay,
ReportCardModel report,
) {
onPlay(
AudioItem(
audioId: 'local_${report.id}',
reportId: report.id,
titleCn: report.titleCn,
reportTitleCn: report.titleCn,
durationSec: 180,
institution: report.institution,
),
);
}