feat: stream cards

closes #21
This commit is contained in:
2025-05-14 13:46:14 +01:00
parent eefbbc2f73
commit 465c6f222e
6 changed files with 130 additions and 7 deletions

View File

@ -0,0 +1,95 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_markdown_plus/flutter_markdown_plus.dart';
import 'package:flutter_svg/svg.dart';
import 'package:ndk/ndk.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:zap_stream_flutter/imgproxy.dart';
import 'package:zap_stream_flutter/rx_filter.dart';
import 'package:zap_stream_flutter/theme.dart';
import 'package:zap_stream_flutter/utils.dart';
import 'package:zap_stream_flutter/widgets/nostr_text.dart';
class StreamCardsWidget extends StatelessWidget {
final StreamEvent stream;
const StreamCardsWidget({super.key, required this.stream});
@override
Widget build(BuildContext context) {
return RxFilter<Nip01Event>(
Key("stream:cards:${stream.aTag}"),
filters: [
Filter(kinds: [17_777], authors: [stream.info.host], limit: 1),
],
builder: (context, state) {
final cardList = state?.firstOrNull;
if (cardList == null) return SizedBox();
final cardIds = cardList.getTags("a");
return RxFilter<Nip01Event>(
Key("stream:cards:${stream.aTag}:cards"),
filters: [
Filter(
kinds: [37_777],
authors: [stream.info.host],
dTags: cardIds.map((i) => i.split(":").last).toList(),
),
],
builder: (context, state) {
final cards = state ?? [];
return Column(
spacing: 8,
children: cards.map((c) => _streamCard(context, c)).toList(),
);
},
);
},
);
}
Widget _streamCard(BuildContext context, Nip01Event card) {
final title = card.getFirstTag("title") ?? card.getFirstTag("subject");
final image = card.getFirstTag("image");
final link = card.getFirstTag("r");
return Container(
padding: EdgeInsets.all(8),
width: double.maxFinite,
decoration: BoxDecoration(color: LAYER_2, borderRadius: DEFAULT_BR),
child: Column(
spacing: 8,
children: [
if (title?.isNotEmpty ?? false)
Text(
title!,
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
if (image?.isNotEmpty ?? false)
link != null
? GestureDetector(
onTap: () {
launchUrl(Uri.parse(link));
},
child: CachedNetworkImage(
imageUrl: proxyImg(context, image!),
errorWidget:
(_, _, _) =>
SvgPicture.asset("assets/svg/logo.svg", height: 40),
),
)
: CachedNetworkImage(imageUrl: proxyImg(context, image!)),
MarkdownBody(
data: card.content,
onTapLink: (text, href, title) {
if (href != null) {
launchUrl(Uri.parse(href));
}
},
),
],
),
);
}
}

View File

@ -3,6 +3,7 @@ import 'package:intl/intl.dart';
import 'package:zap_stream_flutter/theme.dart';
import 'package:zap_stream_flutter/utils.dart';
import 'package:zap_stream_flutter/widgets/profile.dart';
import 'package:zap_stream_flutter/widgets/stream_cards.dart';
class StreamInfoWidget extends StatelessWidget {
final StreamEvent stream;
@ -38,6 +39,7 @@ class StreamInfoWidget extends StatelessWidget {
),
if (stream.info.summary?.isNotEmpty ?? false)
Text(stream.info.summary!),
StreamCardsWidget(stream: stream),
],
),
);