Skip to content

Chatroom features #669

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 42 additions & 24 deletions qaul_ui/lib/screens/home/tabs/chat/widgets/chat.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:audioplayers/audioplayers.dart';
import 'package:bubble/bubble.dart';
import 'package:collection/collection.dart';
import 'package:emoji_picker_flutter/locales/default_emoji_set_locale.dart';
import 'package:file_picker/file_picker.dart';
import 'package:filesize/filesize.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:emoji_picker_flutter/emoji_picker_flutter.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:flutter_chat_types/flutter_chat_types.dart' as types;
import 'package:flutter_chat_ui/flutter_chat_ui.dart'
show
Expand All @@ -19,6 +23,7 @@ import 'package:flutter_chat_ui/flutter_chat_ui.dart'
TextMessage;
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:image_picker/image_picker.dart';
import 'package:logging/logging.dart';
Expand All @@ -30,13 +35,15 @@ import 'package:qaul_rpc/qaul_rpc.dart';
import 'package:record/record.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:utils/utils.dart';
import 'package:geolocator/geolocator.dart';

import '../../../../../../decorators/cron_task_decorator.dart';
import '../../../../../providers/providers.dart';
import '../../../../../utils.dart';
import '../../../../../widgets/widgets.dart';
import '../../tab.dart';
import 'conditional/conditional.dart';
import 'map_screen.dart';

part 'audio_message_widget.dart';

Expand All @@ -52,6 +59,8 @@ part 'group_settings.dart';

part 'image_message_widget.dart';

part 'send_emoji.dart';

typedef OnSendPressed = void Function(String rawText);

const _kChatRouteName = '/chat';
Expand Down Expand Up @@ -182,6 +191,12 @@ class _ChatScreenState extends ConsumerState<ChatScreen> {
worker.sendMessage(room.conversationId, msg.text);
}, [room]);

final sendEmoji = useCallback((String emoji) {
if (!mounted) return;
final worker = ref.read(qaulWorkerProvider);
worker.sendMessage(room.conversationId, emoji);
}, [room]);

final l10n = AppLocalizations.of(context)!;
return Scaffold(
resizeToAvoidBottomInset: true,
Expand Down Expand Up @@ -350,13 +365,11 @@ class _ChatScreenState extends ConsumerState<ChatScreen> {
);
}
},
// the record package is not supported on Linux
onSendAudioPressed: Platform.isLinux
? null
: (room.messages?.isEmpty ?? true)
? null
: ({types.PartialText? text}) async {
// ignore: use_build_context_synchronously
: ({types.PartialText? text}) {
if (!context.mounted) return;
showModalBottomSheet(
context: context,
Expand All @@ -378,45 +391,50 @@ class _ChatScreenState extends ConsumerState<ChatScreen> {
},
);
},
onSendLocationPressed:
!(Platform.isAndroid || Platform.isIOS || Platform.isLinux)
? null
: ({types.PartialText? text}) async {},
),
onMessageTap: (context, message) async {
if (message is! types.FileMessage || _isReceivingFile(message)) {
if (message is! types.FileMessage &&
message is! types.TextMessage) {
return;
}
if (Platform.isIOS || Platform.isAndroid) {
OpenFilex.open(message.uri);

if (message is types.TextMessage &&
message.text.startsWith('geo:')) {
final geoUri = Uri.parse(message.text);
if (await canLaunchUrl(geoUri)) {
await launchUrl(geoUri, mode: LaunchMode.externalApplication);
}
return;
}

final file = Uri.file(message.uri);
if (message is types.FileMessage && !_isReceivingFile(message)) {
if (Platform.isIOS || Platform.isAndroid) {
OpenFilex.open(message.uri);
return;
}

final parentDirectory = File.fromUri(file).parent.uri;
final file = Uri.file(message.uri);
final parentDirectory = File.fromUri(file).parent.uri;

for (final uri in [file, parentDirectory]) {
if (await canLaunchUrl(uri)) {
launchUrl(uri);
return;
for (final uri in [file, parentDirectory]) {
if (await canLaunchUrl(uri)) {
launchUrl(uri);
return;
}
}
}
},
textMessageBuilder: (message,
{required int messageWidth, required bool showName}) {
final msgIdx = room.messages!.indexWhere(
(element) => element.messageIdBase58 == message.id);

var prevMsgWasFromSamePerson = false;
if (msgIdx > 0) {
final prevMsg = room.messages![msgIdx - 1];
prevMsgWasFromSamePerson =
prevMsg.content is TextMessageContent &&
prevMsg.senderIdBase58 == message.author.id;
}

return TextMessage(
message: message,
usePreviewData: true,
hideBackgroundOnEmojiMessages: true,
showName: showName && !prevMsgWasFromSamePerson,
showName: showName,
emojiEnlargementBehavior: EmojiEnlargementBehavior.multi,
nameBuilder: (usr) {
var user = room.members
Expand Down
123 changes: 117 additions & 6 deletions qaul_ui/lib/screens/home/tabs/chat/widgets/custom_input.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ class _CustomInput extends StatefulWidget {
this.onAttachmentPressed,
this.onPickImagePressed,
this.onSendAudioPressed,
this.onSendEmojiPicker,
this.onSendLocationPressed,
this.initialText,
this.disabledMessage,
this.isDisabled = false,
Expand All @@ -35,6 +37,10 @@ class _CustomInput extends StatefulWidget {

final Function({types.PartialText? text})? onSendAudioPressed;

final Function({types.PartialText? text})? onSendLocationPressed;

final Function({types.PartialText? text})? onSendEmojiPicker;

final SendButtonVisibilityMode sendButtonVisibilityMode;

final String? initialText;
Expand Down Expand Up @@ -78,10 +84,11 @@ class _CustomInputState extends State<_CustomInput> {
super.dispose();
}

void _handleSendPressed() {
void _handleSendPressed({types.PartialText? locationMessage}) {
final trimmedText = _textController.text.trim();
if (trimmedText != '' || !widget.isTextRequired) {
final partialText = types.PartialText(text: trimmedText);
if (trimmedText != '' || locationMessage != null) {
final partialText =
locationMessage ?? types.PartialText(text: trimmedText);
widget.onSendPressed(partialText);
_textController.clear();
}
Expand Down Expand Up @@ -173,24 +180,128 @@ class _CustomInputState extends State<_CustomInput> {
suffixIcon: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (widget.onSendLocationPressed != null)
_AttachmentButton(
icon: Icons.location_on,
onPressed: () async {
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MapScreen(
key: const ValueKey('map'),
onLocationSelected:
(position) {
final roundedLat = position
.latitude
.toStringAsFixed(2);
final roundedLng = position
.longitude
.toStringAsFixed(2);
final locationMessage =
types.PartialText(
text:
'geo:${roundedLat},${roundedLng}',
);
_handleSendPressed(
locationMessage:
locationMessage);
},
),
),
);
},
),
if (widget.onAttachmentPressed != null)
_AttachmentButton(
onPressed: () => _sendFilePressed(
widget.onAttachmentPressed),
tooltip: AppLocalizations.of(context)!.sendFileTooltip,
tooltip: AppLocalizations.of(context)!
.sendFileTooltip,
),
_AttachmentButton(
icon: Icons.emoji_emotions,
onPressed: () async {
final scrollController =
ScrollController();
showModalBottomSheet(
context: context,
enableDrag: true,
isDismissible: true,
builder: (_) {
return SingleChildScrollView(
controller: scrollController,
child: EmojiPicker(
onEmojiSelected:
(category, emoji) {
final currentText =
_textController.text;
final newText =
'$currentText${emoji.emoji}';
_textController.value =
TextEditingValue(
text: newText,
selection: TextSelection
.fromPosition(
TextPosition(
offset:
newText.length),
),
);
},
config: Config(
height: 300,
checkPlatformCompatibility:
true,
emojiSet:
getDefaultEmojiLocale,
locale: const Locale('en'),
emojiTextStyle:
const TextStyle(
fontSize: 18),
customBackspaceIcon:
const Icon(
Icons.backspace,
color: Colors.blue),
customSearchIcon:
const Icon(Icons.search,
color: Colors.blue),
viewOrderConfig:
const ViewOrderConfig(),
emojiViewConfig:
const EmojiViewConfig(),
skinToneConfig:
const SkinToneConfig(),
categoryViewConfig:
const CategoryViewConfig(),
bottomActionBarConfig:
const BottomActionBarConfig(),
searchViewConfig:
const SearchViewConfig(),
),
),
);
},
).whenComplete(() {
scrollController.dispose();
});
},
tooltip: AppLocalizations.of(context)!
.sendFileTooltip,
),
if (widget.onPickImagePressed != null)
_AttachmentButton(
icon: Icons.add_a_photo,
onPressed: () => _sendFilePressed(
widget.onPickImagePressed),
tooltip: AppLocalizations.of(context)!.sendFileTooltip,
tooltip: AppLocalizations.of(context)!
.sendFileTooltip,
),
if (widget.onSendAudioPressed != null)
_AttachmentButton(
icon: Icons.mic_none,
onPressed: widget.onSendAudioPressed,
tooltip: AppLocalizations.of(context)!.sendAudioTooltip,
tooltip: AppLocalizations.of(context)!
.sendAudioTooltip,
),
],
),
Expand Down
Loading