mirror of
https://github.com/nostrlabs-io/zap-stream-flutter.git
synced 2025-06-16 11:58:50 +00:00
feat: improve chat modal
This commit is contained in:
@ -8,7 +8,7 @@ import 'package:zap_stream_flutter/utils.dart';
|
||||
import 'package:zap_stream_flutter/widgets/avatar.dart';
|
||||
import 'package:zap_stream_flutter/widgets/nostr_text.dart';
|
||||
import 'package:zap_stream_flutter/widgets/profile.dart';
|
||||
import 'package:zap_stream_flutter/widgets/profile_modal.dart';
|
||||
import 'package:zap_stream_flutter/widgets/chat_modal.dart';
|
||||
|
||||
class ChatWidget extends StatelessWidget {
|
||||
final StreamEvent stream;
|
||||
@ -250,7 +250,7 @@ class _ChatMessageWidget extends StatelessWidget {
|
||||
context: context,
|
||||
constraints: BoxConstraints.expand(),
|
||||
builder:
|
||||
(ctx) => ProfileModalWidget(profile: profile, event: msg),
|
||||
(ctx) => ChatModalWidget(profile: profile, event: msg),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
83
lib/widgets/chat_modal.dart
Normal file
83
lib/widgets/chat_modal.dart
Normal file
@ -0,0 +1,83 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:ndk/ndk.dart';
|
||||
import 'package:zap_stream_flutter/theme.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';
|
||||
import 'package:zap_stream_flutter/widgets/reaction.dart';
|
||||
import 'package:zap_stream_flutter/widgets/zap.dart';
|
||||
|
||||
class ChatModalWidget extends StatefulWidget {
|
||||
final Metadata profile;
|
||||
final Nip01Event event;
|
||||
|
||||
const ChatModalWidget({
|
||||
super.key,
|
||||
required this.profile,
|
||||
required this.event,
|
||||
});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _ChatModalWidget();
|
||||
}
|
||||
|
||||
class _ChatModalWidget extends State<ChatModalWidget> {
|
||||
bool _showEmojiPicker = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: EdgeInsets.fromLTRB(5, 10, 5, 0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
spacing: 10,
|
||||
children: [
|
||||
ProfileWidget(profile: widget.profile),
|
||||
Container(
|
||||
width: double.maxFinite,
|
||||
decoration: BoxDecoration(color: LAYER_2, borderRadius: DEFAULT_BR),
|
||||
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8),
|
||||
child: NoteText(event: widget.event),
|
||||
),
|
||||
Row(
|
||||
spacing: 8,
|
||||
children: [
|
||||
IconButton.filled(
|
||||
color: LAYER_5,
|
||||
style: ButtonStyle(
|
||||
backgroundColor: WidgetStateColor.resolveWith((_) => LAYER_3),
|
||||
),
|
||||
onPressed:
|
||||
() => setState(() {
|
||||
_showEmojiPicker = !_showEmojiPicker;
|
||||
}),
|
||||
icon: Icon(Icons.mood),
|
||||
),
|
||||
IconButton.filled(
|
||||
color: ZAP_1,
|
||||
style: ButtonStyle(
|
||||
backgroundColor: WidgetStateColor.resolveWith((_) => LAYER_3),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (ctx) {
|
||||
return ZapWidget(
|
||||
pubkey: widget.event.pubKey,
|
||||
target: widget.event,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
icon: Icon(Icons.bolt),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (_showEmojiPicker) ReactionWidget(event: widget.event),
|
||||
MuteButton(pubkey: widget.event.pubKey),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -8,6 +8,21 @@ import 'package:zap_stream_flutter/theme.dart';
|
||||
import 'package:zap_stream_flutter/utils.dart';
|
||||
import 'package:zap_stream_flutter/widgets/profile.dart';
|
||||
|
||||
class NoteText extends StatelessWidget {
|
||||
final Nip01Event event;
|
||||
|
||||
const NoteText({super.key, required this.event});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return RichText(
|
||||
text: TextSpan(
|
||||
children: textToSpans(event.content, event.tags, event.pubKey),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a nostr note text containing links
|
||||
/// and mentions into multiple spans for rendering
|
||||
List<InlineSpan> textToSpans(
|
||||
|
@ -1,85 +0,0 @@
|
||||
import 'dart:developer' as developer;
|
||||
|
||||
import 'package:emoji_picker_flutter/emoji_picker_flutter.dart';
|
||||
import 'package:flutter/foundation.dart' as foundation;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:ndk/ndk.dart';
|
||||
import 'package:zap_stream_flutter/main.dart';
|
||||
import 'package:zap_stream_flutter/theme.dart';
|
||||
import 'package:zap_stream_flutter/widgets/button.dart';
|
||||
import 'package:zap_stream_flutter/widgets/mute_button.dart';
|
||||
import 'package:zap_stream_flutter/widgets/profile.dart';
|
||||
import 'package:zap_stream_flutter/widgets/zap.dart';
|
||||
|
||||
class ProfileModalWidget extends StatelessWidget {
|
||||
final Metadata profile;
|
||||
final Nip01Event event;
|
||||
|
||||
const ProfileModalWidget({
|
||||
super.key,
|
||||
required this.profile,
|
||||
required this.event,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: EdgeInsets.fromLTRB(5, 10, 5, 0),
|
||||
child: Column(
|
||||
spacing: 10,
|
||||
children: [
|
||||
ProfileWidget(profile: profile),
|
||||
EmojiPicker(
|
||||
onEmojiSelected: (category, emoji) {
|
||||
developer.log(emoji.emoji);
|
||||
ndk.broadcast.broadcastReaction(
|
||||
eventId: event.id,
|
||||
reaction: emoji.emoji,
|
||||
);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
config: Config(
|
||||
height: 256,
|
||||
checkPlatformCompatibility: true,
|
||||
emojiViewConfig: EmojiViewConfig(
|
||||
emojiSizeMax:
|
||||
28 *
|
||||
(foundation.defaultTargetPlatform == TargetPlatform.iOS
|
||||
? 1.20
|
||||
: 1.0),
|
||||
backgroundColor: LAYER_1,
|
||||
),
|
||||
viewOrderConfig: const ViewOrderConfig(
|
||||
top: EmojiPickerItem.categoryBar,
|
||||
middle: EmojiPickerItem.emojiView,
|
||||
bottom: EmojiPickerItem.searchBar,
|
||||
),
|
||||
bottomActionBarConfig: BottomActionBarConfig(
|
||||
backgroundColor: LAYER_2,
|
||||
buttonColor: PRIMARY_1,
|
||||
),
|
||||
categoryViewConfig: CategoryViewConfig(backgroundColor: LAYER_2),
|
||||
searchViewConfig: SearchViewConfig(
|
||||
backgroundColor: LAYER_2,
|
||||
buttonIconColor: PRIMARY_1,
|
||||
),
|
||||
),
|
||||
),
|
||||
BasicButton.text(
|
||||
"Zap",
|
||||
onTap: () {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
constraints: BoxConstraints.expand(),
|
||||
builder: (ctx) {
|
||||
return ZapWidget(pubkey: event.pubKey, target: event);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
MuteButton(pubkey: event.pubKey),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
51
lib/widgets/reaction.dart
Normal file
51
lib/widgets/reaction.dart
Normal file
@ -0,0 +1,51 @@
|
||||
import 'package:emoji_picker_flutter/emoji_picker_flutter.dart';
|
||||
import 'package:flutter/foundation.dart' as foundation;
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:ndk/entities.dart';
|
||||
import 'package:zap_stream_flutter/main.dart';
|
||||
import 'package:zap_stream_flutter/theme.dart';
|
||||
|
||||
class ReactionWidget extends StatelessWidget {
|
||||
final Nip01Event event;
|
||||
|
||||
const ReactionWidget({super.key, required this.event});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return EmojiPicker(
|
||||
onEmojiSelected: (category, emoji) {
|
||||
ndk.broadcast.broadcastReaction(
|
||||
eventId: event.id,
|
||||
reaction: emoji.emoji,
|
||||
);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
config: Config(
|
||||
height: 256,
|
||||
checkPlatformCompatibility: true,
|
||||
emojiViewConfig: EmojiViewConfig(
|
||||
emojiSizeMax:
|
||||
28 *
|
||||
(foundation.defaultTargetPlatform == TargetPlatform.iOS
|
||||
? 1.20
|
||||
: 1.0),
|
||||
backgroundColor: LAYER_1,
|
||||
),
|
||||
viewOrderConfig: const ViewOrderConfig(
|
||||
top: EmojiPickerItem.categoryBar,
|
||||
middle: EmojiPickerItem.emojiView,
|
||||
bottom: EmojiPickerItem.searchBar,
|
||||
),
|
||||
bottomActionBarConfig: BottomActionBarConfig(
|
||||
backgroundColor: LAYER_2,
|
||||
buttonColor: PRIMARY_1,
|
||||
),
|
||||
categoryViewConfig: CategoryViewConfig(backgroundColor: LAYER_2),
|
||||
searchViewConfig: SearchViewConfig(
|
||||
backgroundColor: LAYER_2,
|
||||
buttonIconColor: PRIMARY_1,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -84,6 +84,7 @@ class _ZapWidget extends State<ZapWidget> {
|
||||
),
|
||||
BasicButton.text(
|
||||
"Zap",
|
||||
decoration: BoxDecoration(color: LAYER_3, borderRadius: DEFAULT_BR),
|
||||
onTap: () {
|
||||
try {
|
||||
_loadZap();
|
||||
@ -147,7 +148,9 @@ class _ZapWidget extends State<ZapWidget> {
|
||||
pubKey: widget.pubkey,
|
||||
eventId: widget.target?.id,
|
||||
addressableId:
|
||||
widget.target != null && widget.target!.kind >= 30_000 && widget.target!.kind < 40_000
|
||||
widget.target != null &&
|
||||
widget.target!.kind >= 30_000 &&
|
||||
widget.target!.kind < 40_000
|
||||
? "${widget.target!.kind}:${widget.target!.pubKey}:${widget.target!.getDtag()!}"
|
||||
: null,
|
||||
relays: defaultRelays,
|
||||
@ -174,7 +177,7 @@ class _ZapWidget extends State<ZapWidget> {
|
||||
}),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: n == _amount ? LAYER_2 : LAYER_1,
|
||||
color: n == _amount ? LAYER_4 : LAYER_3,
|
||||
borderRadius: DEFAULT_BR,
|
||||
),
|
||||
alignment: AlignmentDirectional.center,
|
||||
|
Reference in New Issue
Block a user