✨ Send message with poll
This commit is contained in:
@@ -46,6 +46,18 @@ sealed class SnPoll with _$SnPoll {
|
|||||||
}) = _SnPoll;
|
}) = _SnPoll;
|
||||||
|
|
||||||
factory SnPoll.fromJson(Map<String, dynamic> json) => _$SnPollFromJson(json);
|
factory SnPoll.fromJson(Map<String, dynamic> json) => _$SnPollFromJson(json);
|
||||||
|
|
||||||
|
factory SnPoll.fromPollWithStats(SnPollWithStats pollWithStats) => SnPoll(
|
||||||
|
id: pollWithStats.id,
|
||||||
|
questions: pollWithStats.questions,
|
||||||
|
title: pollWithStats.title,
|
||||||
|
description: pollWithStats.description,
|
||||||
|
endedAt: pollWithStats.endedAt,
|
||||||
|
publisherId: pollWithStats.publisherId,
|
||||||
|
createdAt: pollWithStats.createdAt,
|
||||||
|
updatedAt: pollWithStats.updatedAt,
|
||||||
|
deletedAt: pollWithStats.deletedAt,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import "package:island/database/drift_db.dart";
|
|||||||
import "package:island/database/message.dart";
|
import "package:island/database/message.dart";
|
||||||
import "package:island/models/chat.dart";
|
import "package:island/models/chat.dart";
|
||||||
import "package:island/models/file.dart";
|
import "package:island/models/file.dart";
|
||||||
|
import "package:island/models/poll.dart";
|
||||||
import "package:island/pods/database.dart";
|
import "package:island/pods/database.dart";
|
||||||
import "package:island/pods/lifecycle.dart";
|
import "package:island/pods/lifecycle.dart";
|
||||||
import "package:island/pods/network.dart";
|
import "package:island/pods/network.dart";
|
||||||
@@ -437,6 +438,7 @@ class MessagesNotifier extends _$MessagesNotifier {
|
|||||||
WidgetRef ref,
|
WidgetRef ref,
|
||||||
String content,
|
String content,
|
||||||
List<UniversalFile> attachments, {
|
List<UniversalFile> attachments, {
|
||||||
|
SnPoll? poll,
|
||||||
SnChatMessage? editingTo,
|
SnChatMessage? editingTo,
|
||||||
SnChatMessage? forwardingTo,
|
SnChatMessage? forwardingTo,
|
||||||
SnChatMessage? replyingTo,
|
SnChatMessage? replyingTo,
|
||||||
@@ -498,6 +500,7 @@ class MessagesNotifier extends _$MessagesNotifier {
|
|||||||
'attachments_id': cloudAttachments.map((e) => e.id).toList(),
|
'attachments_id': cloudAttachments.map((e) => e.id).toList(),
|
||||||
'replied_message_id': replyingTo?.id,
|
'replied_message_id': replyingTo?.id,
|
||||||
'forwarded_message_id': forwardingTo?.id,
|
'forwarded_message_id': forwardingTo?.id,
|
||||||
|
'poll_id': poll?.id,
|
||||||
'meta': {},
|
'meta': {},
|
||||||
'nonce': nonce,
|
'nonce': nonce,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import "package:hooks_riverpod/hooks_riverpod.dart";
|
|||||||
import "package:island/database/message.dart";
|
import "package:island/database/message.dart";
|
||||||
import "package:island/models/chat.dart";
|
import "package:island/models/chat.dart";
|
||||||
import "package:island/models/file.dart";
|
import "package:island/models/file.dart";
|
||||||
|
import "package:island/models/poll.dart";
|
||||||
import "package:island/pods/chat/chat_rooms.dart";
|
import "package:island/pods/chat/chat_rooms.dart";
|
||||||
import "package:island/pods/chat/chat_subscribe.dart";
|
import "package:island/pods/chat/chat_subscribe.dart";
|
||||||
import "package:island/pods/chat/messages_notifier.dart";
|
import "package:island/pods/chat/messages_notifier.dart";
|
||||||
@@ -170,6 +171,7 @@ class ChatRoomScreen extends HookConsumerWidget {
|
|||||||
final messageReplyingTo = useState<SnChatMessage?>(null);
|
final messageReplyingTo = useState<SnChatMessage?>(null);
|
||||||
final messageForwardingTo = useState<SnChatMessage?>(null);
|
final messageForwardingTo = useState<SnChatMessage?>(null);
|
||||||
final messageEditingTo = useState<SnChatMessage?>(null);
|
final messageEditingTo = useState<SnChatMessage?>(null);
|
||||||
|
final selectedPoll = useState<SnPoll?>(null);
|
||||||
final attachments = useState<List<UniversalFile>>([]);
|
final attachments = useState<List<UniversalFile>>([]);
|
||||||
final attachmentProgress = useState<Map<String, Map<int, double?>>>({});
|
final attachmentProgress = useState<Map<String, Map<int, double?>>>({});
|
||||||
|
|
||||||
@@ -285,11 +287,13 @@ class ChatRoomScreen extends HookConsumerWidget {
|
|||||||
|
|
||||||
void sendMessage() {
|
void sendMessage() {
|
||||||
if (messageController.text.trim().isNotEmpty ||
|
if (messageController.text.trim().isNotEmpty ||
|
||||||
attachments.value.isNotEmpty) {
|
attachments.value.isNotEmpty ||
|
||||||
|
selectedPoll.value != null) {
|
||||||
messagesNotifier.sendMessage(
|
messagesNotifier.sendMessage(
|
||||||
ref,
|
ref,
|
||||||
messageController.text.trim(),
|
messageController.text.trim(),
|
||||||
attachments.value,
|
attachments.value,
|
||||||
|
poll: selectedPoll.value,
|
||||||
editingTo: messageEditingTo.value,
|
editingTo: messageEditingTo.value,
|
||||||
forwardingTo: messageForwardingTo.value,
|
forwardingTo: messageForwardingTo.value,
|
||||||
replyingTo: messageReplyingTo.value,
|
replyingTo: messageReplyingTo.value,
|
||||||
@@ -304,6 +308,7 @@ class ChatRoomScreen extends HookConsumerWidget {
|
|||||||
messageEditingTo.value = null;
|
messageEditingTo.value = null;
|
||||||
messageReplyingTo.value = null;
|
messageReplyingTo.value = null;
|
||||||
messageForwardingTo.value = null;
|
messageForwardingTo.value = null;
|
||||||
|
selectedPoll.value = null;
|
||||||
attachments.value = [];
|
attachments.value = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1246,10 +1251,13 @@ class ChatRoomScreen extends HookConsumerWidget {
|
|||||||
messageEditingTo.value = null;
|
messageEditingTo.value = null;
|
||||||
messageReplyingTo.value = null;
|
messageReplyingTo.value = null;
|
||||||
messageForwardingTo.value = null;
|
messageForwardingTo.value = null;
|
||||||
|
selectedPoll.value = null;
|
||||||
},
|
},
|
||||||
messageEditingTo: messageEditingTo.value,
|
messageEditingTo: messageEditingTo.value,
|
||||||
messageReplyingTo: messageReplyingTo.value,
|
messageReplyingTo: messageReplyingTo.value,
|
||||||
messageForwardingTo: messageForwardingTo.value,
|
messageForwardingTo: messageForwardingTo.value,
|
||||||
|
selectedPoll: selectedPoll.value,
|
||||||
|
onPollSelected: (poll) => selectedPoll.value = poll,
|
||||||
onPickFile: (bool isPhoto) {
|
onPickFile: (bool isPhoto) {
|
||||||
if (isPhoto) {
|
if (isPhoto) {
|
||||||
pickPhotoMedia();
|
pickPhotoMedia();
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import "package:island/models/account.dart";
|
|||||||
import "package:island/models/autocomplete_response.dart";
|
import "package:island/models/autocomplete_response.dart";
|
||||||
import "package:island/models/chat.dart";
|
import "package:island/models/chat.dart";
|
||||||
import "package:island/models/file.dart";
|
import "package:island/models/file.dart";
|
||||||
|
import "package:island/models/poll.dart";
|
||||||
import "package:island/models/publisher.dart";
|
import "package:island/models/publisher.dart";
|
||||||
import "package:island/models/realm.dart";
|
import "package:island/models/realm.dart";
|
||||||
import "package:island/models/sticker.dart";
|
import "package:island/models/sticker.dart";
|
||||||
@@ -26,6 +27,7 @@ import "package:styled_widget/styled_widget.dart";
|
|||||||
import "package:material_symbols_icons/symbols.dart";
|
import "package:material_symbols_icons/symbols.dart";
|
||||||
import "package:island/widgets/stickers/sticker_picker.dart";
|
import "package:island/widgets/stickers/sticker_picker.dart";
|
||||||
import "package:island/pods/chat/chat_subscribe.dart";
|
import "package:island/pods/chat/chat_subscribe.dart";
|
||||||
|
import "package:island/widgets/post/compose_poll.dart";
|
||||||
|
|
||||||
void _insertPlaceholder(TextEditingController controller, String placeholder) {
|
void _insertPlaceholder(TextEditingController controller, String placeholder) {
|
||||||
final text = controller.text;
|
final text = controller.text;
|
||||||
@@ -43,8 +45,14 @@ const kInputDrawerExpandedHeight = 180.0;
|
|||||||
|
|
||||||
class _ExpandedSection extends StatelessWidget {
|
class _ExpandedSection extends StatelessWidget {
|
||||||
final TextEditingController messageController;
|
final TextEditingController messageController;
|
||||||
|
final SnPoll? selectedPoll;
|
||||||
|
final Function(SnPoll?) onPollSelected;
|
||||||
|
|
||||||
const _ExpandedSection({required this.messageController});
|
const _ExpandedSection({
|
||||||
|
required this.messageController,
|
||||||
|
this.selectedPoll,
|
||||||
|
required this.onPollSelected,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -90,7 +98,16 @@ class _ExpandedSection extends StatelessWidget {
|
|||||||
borderRadius: const BorderRadius.all(
|
borderRadius: const BorderRadius.all(
|
||||||
Radius.circular(8),
|
Radius.circular(8),
|
||||||
),
|
),
|
||||||
onTap: () {},
|
onTap: () async {
|
||||||
|
final poll = await showModalBottomSheet<SnPoll>(
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
builder: (context) => const ComposePollSheet(),
|
||||||
|
);
|
||||||
|
if (poll != null) {
|
||||||
|
onPollSelected(poll);
|
||||||
|
}
|
||||||
|
},
|
||||||
child: Card(
|
child: Card(
|
||||||
margin: EdgeInsets.zero,
|
margin: EdgeInsets.zero,
|
||||||
color:
|
color:
|
||||||
@@ -176,6 +193,8 @@ class ChatInput extends HookConsumerWidget {
|
|||||||
final Function(int, int) onMoveAttachment;
|
final Function(int, int) onMoveAttachment;
|
||||||
final Function(List<UniversalFile>) onAttachmentsChanged;
|
final Function(List<UniversalFile>) onAttachmentsChanged;
|
||||||
final Map<String, Map<int, double?>> attachmentProgress;
|
final Map<String, Map<int, double?>> attachmentProgress;
|
||||||
|
final SnPoll? selectedPoll;
|
||||||
|
final Function(SnPoll?) onPollSelected;
|
||||||
|
|
||||||
const ChatInput({
|
const ChatInput({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -196,6 +215,8 @@ class ChatInput extends HookConsumerWidget {
|
|||||||
required this.onMoveAttachment,
|
required this.onMoveAttachment,
|
||||||
required this.onAttachmentsChanged,
|
required this.onAttachmentsChanged,
|
||||||
required this.attachmentProgress,
|
required this.attachmentProgress,
|
||||||
|
this.selectedPoll,
|
||||||
|
required this.onPollSelected,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -413,6 +434,87 @@ class ChatInput extends HookConsumerWidget {
|
|||||||
key: ValueKey('no-attachments'),
|
key: ValueKey('no-attachments'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
AnimatedSwitcher(
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
switchInCurve: Curves.easeOutCubic,
|
||||||
|
switchOutCurve: Curves.easeInCubic,
|
||||||
|
transitionBuilder: (Widget child, Animation<double> animation) {
|
||||||
|
return SlideTransition(
|
||||||
|
position: Tween<Offset>(
|
||||||
|
begin: const Offset(0, -0.25),
|
||||||
|
end: Offset.zero,
|
||||||
|
).animate(animation),
|
||||||
|
child: FadeTransition(
|
||||||
|
opacity: animation,
|
||||||
|
child: SizeTransition(
|
||||||
|
sizeFactor: animation,
|
||||||
|
axisAlignment: -1.0,
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child:
|
||||||
|
selectedPoll != null
|
||||||
|
? Container(
|
||||||
|
key: const ValueKey('selected-poll'),
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16,
|
||||||
|
vertical: 8,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color:
|
||||||
|
Theme.of(
|
||||||
|
context,
|
||||||
|
).colorScheme.surfaceContainerHigh,
|
||||||
|
borderRadius: BorderRadius.circular(24),
|
||||||
|
border: Border.all(
|
||||||
|
color: Theme.of(
|
||||||
|
context,
|
||||||
|
).colorScheme.outline.withOpacity(0.2),
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
margin: const EdgeInsets.only(
|
||||||
|
left: 8,
|
||||||
|
right: 8,
|
||||||
|
top: 8,
|
||||||
|
bottom: 8,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Symbols.how_to_vote,
|
||||||
|
size: 18,
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
selectedPoll!.title ?? 'Poll',
|
||||||
|
style: Theme.of(context).textTheme.bodySmall!
|
||||||
|
.copyWith(fontWeight: FontWeight.w500),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
child: IconButton(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
icon: const Icon(Icons.close, size: 18),
|
||||||
|
onPressed: () => onPollSelected(null),
|
||||||
|
tooltip: 'clear'.tr(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: const SizedBox.shrink(
|
||||||
|
key: ValueKey('no-selected-poll'),
|
||||||
|
),
|
||||||
|
),
|
||||||
AnimatedSwitcher(
|
AnimatedSwitcher(
|
||||||
duration: const Duration(milliseconds: 200),
|
duration: const Duration(milliseconds: 200),
|
||||||
switchInCurve: Curves.easeOutCubic,
|
switchInCurve: Curves.easeOutCubic,
|
||||||
@@ -798,7 +900,11 @@ class ChatInput extends HookConsumerWidget {
|
|||||||
},
|
},
|
||||||
child:
|
child:
|
||||||
isExpanded.value
|
isExpanded.value
|
||||||
? _ExpandedSection(messageController: messageController)
|
? _ExpandedSection(
|
||||||
|
messageController: messageController,
|
||||||
|
selectedPoll: selectedPoll,
|
||||||
|
onPollSelected: onPollSelected,
|
||||||
|
)
|
||||||
: const SizedBox.shrink(key: ValueKey('collapsed')),
|
: const SizedBox.shrink(key: ValueKey('collapsed')),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -67,7 +67,9 @@ class ComposePollSheet extends HookConsumerWidget {
|
|||||||
title: Text(poll.title ?? 'untitled'.tr()),
|
title: Text(poll.title ?? 'untitled'.tr()),
|
||||||
subtitle: _buildPollSubtitle(poll),
|
subtitle: _buildPollSubtitle(poll),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).pop(poll);
|
Navigator.of(
|
||||||
|
context,
|
||||||
|
).pop(SnPoll.fromPollWithStats(poll));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user