From e0e917553654a5616fa053287685358fa4be01b5 Mon Sep 17 00:00:00 2001 From: Kieran Date: Wed, 14 May 2025 14:04:44 +0100 Subject: [PATCH] feat: follow button closes #22 --- lib/widgets/button_follow.dart | 69 ++++++++++++++++++++++++++++++++++ lib/widgets/chat_modal.dart | 7 ++++ lib/widgets/mute_button.dart | 5 ++- lib/widgets/stream_cards.dart | 40 ++++++++++++-------- lib/widgets/stream_info.dart | 7 ++++ 5 files changed, 111 insertions(+), 17 deletions(-) create mode 100644 lib/widgets/button_follow.dart diff --git a/lib/widgets/button_follow.dart b/lib/widgets/button_follow.dart new file mode 100644 index 0000000..12090b0 --- /dev/null +++ b/lib/widgets/button_follow.dart @@ -0,0 +1,69 @@ +import 'package:flutter/material.dart'; +import 'package:zap_stream_flutter/main.dart'; +import 'package:zap_stream_flutter/theme.dart'; +import 'package:zap_stream_flutter/widgets/button.dart'; + +class FollowButton extends StatelessWidget { + final String pubkey; + final void Function()? onTap; + final void Function()? onFollow; + final void Function()? onUnfollow; + + const FollowButton({ + super.key, + required this.pubkey, + this.onTap, + this.onFollow, + this.onUnfollow, + }); + + @override + Widget build(BuildContext context) { + final signer = ndk.accounts.getLoggedAccount()?.signer; + if (signer == null || signer.getPublicKey() == pubkey) { + return SizedBox.shrink(); + } + + return FutureBuilder( + future: ndk.follows.getContactList(signer.getPublicKey()), + builder: (context, state) { + final follows = state.data?.contacts ?? []; + final isFollowing = follows.contains(pubkey); + return BasicButton( + Row( + mainAxisAlignment: MainAxisAlignment.center, + spacing: 4, + children: [ + Icon(isFollowing ? Icons.person_remove : Icons.person_add, size: 16), + Text( + isFollowing ? "Unfollow" : "Follow", + style: TextStyle(fontWeight: FontWeight.bold), + ), + ], + ), + padding: EdgeInsets.symmetric(vertical: 4, horizontal: 12), + decoration: BoxDecoration( + borderRadius: DEFAULT_BR, + color: LAYER_2, + ), + onTap: () async { + if (onTap != null) { + onTap!(); + } + if (isFollowing) { + await ndk.follows.broadcastRemoveContact(pubkey); + if (onUnfollow != null) { + onUnfollow!(); + } + } else { + await ndk.follows.broadcastAddContact(pubkey); + if (onFollow != null) { + onFollow!(); + } + } + }, + ); + }, + ); + } +} diff --git a/lib/widgets/chat_modal.dart b/lib/widgets/chat_modal.dart index 19a89d0..703d3e3 100644 --- a/lib/widgets/chat_modal.dart +++ b/lib/widgets/chat_modal.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:ndk/ndk.dart'; import 'package:zap_stream_flutter/theme.dart'; +import 'package:zap_stream_flutter/widgets/button_follow.dart'; import 'package:zap_stream_flutter/widgets/mute_button.dart'; import 'package:zap_stream_flutter/widgets/nostr_text.dart'; import 'package:zap_stream_flutter/widgets/profile.dart'; @@ -75,6 +76,12 @@ class _ChatModalWidget extends State { ], ), if (_showEmojiPicker) ReactionWidget(event: widget.event), + FollowButton( + pubkey: widget.event.pubKey, + onTap: () { + Navigator.pop(context); + }, + ), MuteButton( pubkey: widget.event.pubKey, onTap: () { diff --git a/lib/widgets/mute_button.dart b/lib/widgets/mute_button.dart index 918827e..334efae 100644 --- a/lib/widgets/mute_button.dart +++ b/lib/widgets/mute_button.dart @@ -39,7 +39,10 @@ class MuteButton extends StatelessWidget { ), ), padding: EdgeInsets.symmetric(vertical: 4, horizontal: 12), - decoration: BoxDecoration(color: WARNING, borderRadius: DEFAULT_BR), + decoration: BoxDecoration( + color: isMuted ? LAYER_2 : WARNING, + borderRadius: DEFAULT_BR, + ), onTap: () async { if (onTap != null) { onTap!(); diff --git a/lib/widgets/stream_cards.dart b/lib/widgets/stream_cards.dart index 2b87cb3..29fabbc 100644 --- a/lib/widgets/stream_cards.dart +++ b/lib/widgets/stream_cards.dart @@ -60,26 +60,34 @@ class StreamCardsWidget extends StatelessWidget { decoration: BoxDecoration(color: LAYER_2, borderRadius: DEFAULT_BR), child: Column( spacing: 8, + crossAxisAlignment: CrossAxisAlignment.start, children: [ if (title?.isNotEmpty ?? false) - Text( - title!, - style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + Center( + child: 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!)), + Center( + child: + 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) { diff --git a/lib/widgets/stream_info.dart b/lib/widgets/stream_info.dart index e41f0d0..06a919a 100644 --- a/lib/widgets/stream_info.dart +++ b/lib/widgets/stream_info.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; 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/button_follow.dart'; import 'package:zap_stream_flutter/widgets/profile.dart'; import 'package:zap_stream_flutter/widgets/stream_cards.dart'; @@ -23,6 +24,12 @@ class StreamInfoWidget extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ ProfileWidget.pubkey(stream.info.host), + FollowButton( + pubkey: stream.info.host, + onTap: () { + Navigator.pop(context); + }, + ), Text( stream.info.title ?? "", style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),