double tap on messages to show options #63
@ -99,6 +99,167 @@ class _ChatBubbleState extends State<ChatBubble> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void showOptionsDialog() {
|
||||||
|
showDialog(context: context, builder: (context) {
|
||||||
|
var commonReactions = <String>['👍', '👎', '😆', '❤️', '👀'];
|
||||||
|
var canReact = widget.bubbleData.messageType == GetRoomResponseObjectMessageType.comment;
|
||||||
|
return SimpleDialog(
|
||||||
|
children: [
|
||||||
|
Visibility(
|
||||||
|
visible: canReact,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Wrap(
|
||||||
|
alignment: WrapAlignment.center,
|
||||||
|
children: [
|
||||||
|
...commonReactions.map((e) => TextButton(
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
minimumSize: const Size(40, 40)
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
ReactMessage(
|
||||||
|
chatToken: widget.chatData.token,
|
||||||
|
messageId: widget.bubbleData.id,
|
||||||
|
params: ReactMessageParams(e),
|
||||||
|
).run().then((value) => widget.refetch(renew: true));
|
||||||
|
},
|
||||||
|
child: Text(e),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
showDialog(context: context, builder: (context) => AlertDialog(
|
||||||
|
contentPadding: const EdgeInsets.all(15),
|
||||||
|
titlePadding: const EdgeInsets.only(left: 6, top: 15),
|
||||||
|
title: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.arrow_back),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
const Text('Reagieren'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
content: SizedBox(
|
||||||
|
width: 256,
|
||||||
|
height: 270,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
emojis.EmojiPicker(
|
||||||
|
config: emojis.Config(
|
||||||
|
height: 256,
|
||||||
|
swapCategoryAndBottomBar: true,
|
||||||
|
emojiViewConfig: emojis.EmojiViewConfig(
|
||||||
|
backgroundColor: Theme.of(context).canvasColor,
|
||||||
|
recentsLimit: 67,
|
||||||
|
emojiSizeMax: 25,
|
||||||
|
noRecents: const Text('Keine zuletzt verwendeten Emojis'),
|
||||||
|
columns: 7,
|
||||||
|
),
|
||||||
|
bottomActionBarConfig: const emojis.BottomActionBarConfig(
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
categoryViewConfig: emojis.CategoryViewConfig(
|
||||||
|
backgroundColor: Theme.of(context).hoverColor,
|
||||||
|
iconColorSelected: Theme.of(context).primaryColor,
|
||||||
|
indicatorColor: Theme.of(context).primaryColor,
|
||||||
|
),
|
||||||
|
searchViewConfig: emojis.SearchViewConfig(
|
||||||
|
backgroundColor: Theme.of(context).dividerColor,
|
||||||
|
buttonColor: Theme.of(context).dividerColor,
|
||||||
|
hintText: 'Suchen',
|
||||||
|
buttonIconColor: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onEmojiSelected: (emojis.Category? category, emojis.Emoji emoji) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
ReactMessage(
|
||||||
|
chatToken: widget.chatData.token,
|
||||||
|
messageId: widget.bubbleData.id,
|
||||||
|
params: ReactMessageParams(emoji.emoji),
|
||||||
|
).run().then((value) => widget.refetch(renew: true));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
},
|
||||||
|
style: IconButton.styleFrom(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
minimumSize: const Size(40, 40),
|
||||||
|
),
|
||||||
|
icon: const Icon(Icons.add_circle_outline_outlined),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Visibility(
|
||||||
|
visible: canReact,
|
||||||
|
child: ListTile(
|
||||||
|
leading: const Icon(Icons.emoji_emotions_outlined),
|
||||||
|
title: const Text('Reaktionen'),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context).push(MaterialPageRoute(builder: (context) => MessageReactions(
|
||||||
|
token: widget.chatData.token,
|
||||||
|
messageId: widget.bubbleData.id,
|
||||||
|
)));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Visibility(
|
||||||
|
visible: !message.containsFile,
|
||||||
|
child: ListTile(
|
||||||
|
leading: const Icon(Icons.copy),
|
||||||
|
title: const Text('Nachricht kopieren'),
|
||||||
|
onTap: () => {
|
||||||
|
Clipboard.setData(ClipboardData(text: widget.bubbleData.message)),
|
||||||
|
Navigator.of(context).pop(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Visibility(
|
||||||
|
visible: !kReleaseMode && !widget.isSender && widget.chatData.type != GetRoomResponseObjectConversationType.oneToOne,
|
||||||
|
child: ListTile(
|
||||||
|
leading: const Icon(Icons.sms_outlined),
|
||||||
|
title: Text("Private Nachricht an '${widget.bubbleData.actorDisplayName}'"),
|
||||||
|
onTap: () => {
|
||||||
|
Navigator.of(context).pop()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Visibility(
|
||||||
|
visible: widget.isSender && DateTime.fromMillisecondsSinceEpoch(widget.bubbleData.timestamp * 1000).add(const Duration(hours: 6)).isAfter(DateTime.now()),
|
||||||
|
child: ListTile(
|
||||||
|
leading: const Icon(Icons.delete_outline),
|
||||||
|
title: const Text('Nachricht löschen'),
|
||||||
|
onTap: () {
|
||||||
|
DeleteMessage(widget.chatData.token, widget.bubbleData.id).run().then((value) {
|
||||||
|
Provider.of<ChatProps>(context, listen: false).run();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
DebugTile(context).jsonData(widget.bubbleData.toJson()),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -127,6 +288,54 @@ class _ChatBubbleState extends State<ChatBubble> {
|
|||||||
|
|
||||||
children: [
|
children: [
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
|
onLongPress: showOptionsDialog,
|
||||||
|
onDoubleTap: showOptionsDialog,
|
||||||
|
onTap: () {
|
||||||
|
if(message.file == null) return;
|
||||||
|
|
||||||
|
if(downloadProgress > 0) {
|
||||||
|
showDialog(context: context, builder: (context) => AlertDialog(
|
||||||
|
title: const Text('Download abbrechen?'),
|
||||||
|
content: const Text('Möchtest du den Download abbrechen?'),
|
||||||
|
actions: [
|
||||||
|
TextButton(onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}, child: const Text('Nein')),
|
||||||
|
TextButton(onPressed: () {
|
||||||
|
downloadCore?.then((value) {
|
||||||
|
if(!value.isCancelled) value.cancel();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
});
|
||||||
|
setState(() {
|
||||||
|
downloadProgress = 0;
|
||||||
|
downloadCore = null;
|
||||||
|
});
|
||||||
|
}, child: const Text('Ja, Abbrechen'))
|
||||||
|
],
|
||||||
|
));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadProgress = 1;
|
||||||
|
downloadCore = FileElement.download(context, message.file!.path!, message.file!.name, (progress) {
|
||||||
|
if(progress > 1) {
|
||||||
|
setState(() {
|
||||||
|
downloadProgress = progress;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, (result) {
|
||||||
|
setState(() {
|
||||||
|
downloadProgress = 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
if(result.type != ResultType.done) {
|
||||||
|
showDialog(context: context, builder: (context) => AlertDialog(
|
||||||
|
content: Text(result.message),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
child: Bubble(
|
child: Bubble(
|
||||||
style: getStyle(),
|
style: getStyle(),
|
||||||
child: Container(
|
child: Container(
|
||||||
@ -182,212 +391,6 @@ class _ChatBubbleState extends State<ChatBubble> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onLongPress: () {
|
|
||||||
showDialog(context: context, builder: (context) {
|
|
||||||
var commonReactions = <String>['👍', '👎', '😆', '❤️', '👀'];
|
|
||||||
var canReact = widget.bubbleData.messageType == GetRoomResponseObjectMessageType.comment;
|
|
||||||
return SimpleDialog(
|
|
||||||
children: [
|
|
||||||
Visibility(
|
|
||||||
visible: canReact,
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Wrap(
|
|
||||||
alignment: WrapAlignment.center,
|
|
||||||
children: [
|
|
||||||
...commonReactions.map((e) => TextButton(
|
|
||||||
style: TextButton.styleFrom(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
||||||
minimumSize: const Size(40, 40)
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
ReactMessage(
|
|
||||||
chatToken: widget.chatData.token,
|
|
||||||
messageId: widget.bubbleData.id,
|
|
||||||
params: ReactMessageParams(e),
|
|
||||||
).run().then((value) => widget.refetch(renew: true));
|
|
||||||
},
|
|
||||||
child: Text(e),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
onPressed: () {
|
|
||||||
showDialog(context: context, builder: (context) => AlertDialog(
|
|
||||||
contentPadding: const EdgeInsets.all(15),
|
|
||||||
titlePadding: const EdgeInsets.only(left: 6, top: 15),
|
|
||||||
title: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
IconButton(
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.arrow_back),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
const Text('Reagieren'),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
content: SizedBox(
|
|
||||||
width: 256,
|
|
||||||
height: 270,
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
emojis.EmojiPicker(
|
|
||||||
config: emojis.Config(
|
|
||||||
height: 256,
|
|
||||||
swapCategoryAndBottomBar: true,
|
|
||||||
emojiViewConfig: emojis.EmojiViewConfig(
|
|
||||||
backgroundColor: Theme.of(context).canvasColor,
|
|
||||||
recentsLimit: 67,
|
|
||||||
emojiSizeMax: 25,
|
|
||||||
noRecents: const Text('Keine zuletzt verwendeten Emojis'),
|
|
||||||
columns: 7,
|
|
||||||
),
|
|
||||||
bottomActionBarConfig: const emojis.BottomActionBarConfig(
|
|
||||||
enabled: false,
|
|
||||||
),
|
|
||||||
categoryViewConfig: emojis.CategoryViewConfig(
|
|
||||||
backgroundColor: Theme.of(context).hoverColor,
|
|
||||||
iconColorSelected: Theme.of(context).primaryColor,
|
|
||||||
indicatorColor: Theme.of(context).primaryColor,
|
|
||||||
),
|
|
||||||
searchViewConfig: emojis.SearchViewConfig(
|
|
||||||
backgroundColor: Theme.of(context).dividerColor,
|
|
||||||
buttonColor: Theme.of(context).dividerColor,
|
|
||||||
hintText: 'Suchen',
|
|
||||||
buttonIconColor: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onEmojiSelected: (emojis.Category? category, emojis.Emoji emoji) {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
ReactMessage(
|
|
||||||
chatToken: widget.chatData.token,
|
|
||||||
messageId: widget.bubbleData.id,
|
|
||||||
params: ReactMessageParams(emoji.emoji),
|
|
||||||
).run().then((value) => widget.refetch(renew: true));
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
));
|
|
||||||
},
|
|
||||||
style: IconButton.styleFrom(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
||||||
minimumSize: const Size(40, 40),
|
|
||||||
),
|
|
||||||
icon: const Icon(Icons.add_circle_outline_outlined),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const Divider(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Visibility(
|
|
||||||
visible: canReact,
|
|
||||||
child: ListTile(
|
|
||||||
leading: const Icon(Icons.emoji_emotions_outlined),
|
|
||||||
title: const Text('Reaktionen'),
|
|
||||||
onTap: () {
|
|
||||||
Navigator.of(context).push(MaterialPageRoute(builder: (context) => MessageReactions(
|
|
||||||
token: widget.chatData.token,
|
|
||||||
messageId: widget.bubbleData.id,
|
|
||||||
)));
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Visibility(
|
|
||||||
visible: !message.containsFile,
|
|
||||||
child: ListTile(
|
|
||||||
leading: const Icon(Icons.copy),
|
|
||||||
title: const Text('Nachricht kopieren'),
|
|
||||||
onTap: () => {
|
|
||||||
Clipboard.setData(ClipboardData(text: widget.bubbleData.message)),
|
|
||||||
Navigator.of(context).pop(),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Visibility(
|
|
||||||
visible: !kReleaseMode && !widget.isSender && widget.chatData.type != GetRoomResponseObjectConversationType.oneToOne,
|
|
||||||
child: ListTile(
|
|
||||||
leading: const Icon(Icons.sms_outlined),
|
|
||||||
title: Text("Private Nachricht an '${widget.bubbleData.actorDisplayName}'"),
|
|
||||||
onTap: () => {
|
|
||||||
Navigator.of(context).pop()
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Visibility(
|
|
||||||
visible: widget.isSender && DateTime.fromMillisecondsSinceEpoch(widget.bubbleData.timestamp * 1000).add(const Duration(hours: 6)).isAfter(DateTime.now()),
|
|
||||||
child: ListTile(
|
|
||||||
leading: const Icon(Icons.delete_outline),
|
|
||||||
title: const Text('Nachricht löschen'),
|
|
||||||
onTap: () {
|
|
||||||
DeleteMessage(widget.chatData.token, widget.bubbleData.id).run().then((value) {
|
|
||||||
Provider.of<ChatProps>(context, listen: false).run();
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
DebugTile(context).jsonData(widget.bubbleData.toJson()),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onTap: () {
|
|
||||||
if(message.file == null) return;
|
|
||||||
|
|
||||||
if(downloadProgress > 0) {
|
|
||||||
showDialog(context: context, builder: (context) => AlertDialog(
|
|
||||||
title: const Text('Download abbrechen?'),
|
|
||||||
content: const Text('Möchtest du den Download abbrechen?'),
|
|
||||||
actions: [
|
|
||||||
TextButton(onPressed: () {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
}, child: const Text('Nein')),
|
|
||||||
TextButton(onPressed: () {
|
|
||||||
downloadCore?.then((value) {
|
|
||||||
if(!value.isCancelled) value.cancel();
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
});
|
|
||||||
setState(() {
|
|
||||||
downloadProgress = 0;
|
|
||||||
downloadCore = null;
|
|
||||||
});
|
|
||||||
}, child: const Text('Ja, Abbrechen'))
|
|
||||||
],
|
|
||||||
));
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
downloadProgress = 1;
|
|
||||||
downloadCore = FileElement.download(context, message.file!.path!, message.file!.name, (progress) {
|
|
||||||
if(progress > 1) {
|
|
||||||
setState(() {
|
|
||||||
downloadProgress = progress;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, (result) {
|
|
||||||
setState(() {
|
|
||||||
downloadProgress = 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
if(result.type != ResultType.done) {
|
|
||||||
showDialog(context: context, builder: (context) => AlertDialog(
|
|
||||||
content: Text(result.message),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
Visibility(
|
Visibility(
|
||||||
visible: widget.bubbleData.reactions != null,
|
visible: widget.bubbleData.reactions != null,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user