Rumahjo-Android-APP/lib/Ui/screens/chat/chatAudio/widgets/chat_widget.dart

447 lines
18 KiB
Dart
Raw Permalink Normal View History

2024-09-07 00:58:50 +00:00
// ignore_for_file: invalid_use_of_protected_member, invalid_use_of_visible_for_testing_member
import 'dart:io';
import 'package:any_link_preview/any_link_preview.dart';
import 'package:audioplayers/audioplayers.dart';
import 'package:dio/dio.dart';
import 'package:ebroker/Ui/screens/chat/chat_screen.dart';
import 'package:ebroker/app/app_theme.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:open_filex/open_filex.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../../../../data/cubits/chatCubits/send_message.dart';
import '../../../../../data/cubits/system/app_theme_cubit.dart';
import '../../../../../utils/Extensions/extensions.dart';
import '../../../../../utils/Notification/chat_message_handler.dart';
import '../../../../../utils/helper_utils.dart';
import '../../../../../utils/hive_utils.dart';
import '../../../../../utils/ui_utils.dart';
part "parts/attachment.part.dart";
part "parts/linkpreview.part.dart";
part "parts/recordmsg.part.dart";
////Please don't make chaneges without sufficent knowledege in this file. otherwise you will be responsable for it
///
//This will store and ensure that msg is already sent so we don't have to send it again
Set sentMessages = {};
class ChatMessage extends StatefulWidget {
final String message;
final String senderId;
final bool isSentByMe;
final bool? isSentNow;
final String propertyId;
final String reciverId;
final bool isChatAudio;
final bool hasAttachment;
final dynamic audioFile;
final String time;
final dynamic attachment;
final Function(int id)? onHold;
const ChatMessage(
{super.key,
this.isSentNow,
required this.message,
required this.isSentByMe,
required this.isChatAudio,
this.audioFile,
this.attachment,
required this.senderId,
required this.time,
required this.hasAttachment,
required this.propertyId,
required this.reciverId,
this.onHold});
@override
State<ChatMessage> createState() => ChatMessageState();
Map toMap() {
Map data = {};
data['key'] = key;
data['message'] = message;
data['isSentNow'] = isSentNow;
data['isSentByMe'] = isSentByMe;
data['isChatAudio'] = isChatAudio;
data['senderId'] = senderId;
data['propertyId'] = propertyId;
data['reciverId'] = reciverId;
data['hasAttachment'] = hasAttachment;
data['audioFile'] = audioFile;
data['time'] = time;
data['attachment'] = attachment;
return data;
}
factory ChatMessage.fromMap(Map json) {
var chat = ChatMessage(
key: json['key'],
message: json['message'],
isSentByMe: json['isSentByMe'],
isChatAudio: json['isChatAudio'],
senderId: json['senderId'],
audioFile: json['audioFile'],
attachment: json['attachment'],
time: json['time'],
hasAttachment: json['hasAttachment'],
propertyId: json['propertyId'],
reciverId: json['reciverId']);
return chat;
}
}
class ChatMessageState extends State<ChatMessage>
with AutomaticKeepAliveClientMixin {
bool isChatSent = false;
bool selectedMessage = false;
static bool isMounted = false;
String? link;
final ValueNotifier _linkAddNotifier = ValueNotifier("");
@override
void initState() {
///isSentNow is for check if we are not appending messages multiple time
if (widget.isSentByMe &&
(widget.isSentNow == true) &&
isChatSent == false) {
if (!sentMessages.contains(widget.key)) {
context.read<SendMessageCubit>().send(
senderId: HiveUtils.getUserId().toString(),
recieverId: widget.reciverId,
attachment: widget.attachment,
message: widget.message,
proeprtyId: widget.propertyId,
audio: widget.audioFile,
);
}
sentMessages.add(widget.key);
isMounted = true;
}
super.initState();
}
String _emptyTextIfAttachmentHasNoText() {
if (widget.hasAttachment) {
if (widget.message == "[File]") {
return "";
} else {
return widget.message;
}
} else {
return widget.message;
}
}
bool _isLink(String input) {
///This will check if text contains link
final matcher = RegExp(
r"(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)");
return matcher.hasMatch(input);
}
List _replaceLink() {
//This function will make part of text where link starts. we put invisible charector so we can split it with it
final linkPattern = RegExp(
r"(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)");
///This is invisible charector [You can replace it with any special chareactor which generally nobody use]
const String substringIdentifier = "";
///This will find and add invisible charector in prefix and suffix
String splitMapJoin = _emptyTextIfAttachmentHasNoText().splitMapJoin(
linkPattern,
onMatch: (match) {
return substringIdentifier + match.group(0)! + substringIdentifier;
},
onNonMatch: (match) {
return match;
},
);
//finally we split it with invisible charector so it will become list
return splitMapJoin.split(substringIdentifier);
}
List<String> _matchAstric(String data) {
var pattern = RegExp(r"\*(.*?)\*");
String mapJoin = data.splitMapJoin(
pattern,
onMatch: (p0) {
return "${p0.group(0)!}";
},
onNonMatch: (p0) {
return p0;
},
);
return mapJoin.split("");
}
@override
Widget build(BuildContext context) {
super.build(context);
bool isDark =
context.watch<AppThemeCubit>().state.appTheme == AppTheme.dark;
return GestureDetector(
onLongPress: () {
selectedMessageid.value = (widget.key as ValueKey).value;
selectedRecieverId.value = int.parse(widget.reciverId);
showDeletebutton.value = true;
},
onTap: () {
selectedMessage = false;
},
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 7),
child: Container(
alignment:
widget.isSentByMe ? Alignment.centerRight : Alignment.centerLeft,
width: MediaQuery.of(context).size.width,
margin: EdgeInsets.only(
// top: MediaQuery.of(context).size.height * 0.007,
right: widget.isSentByMe ? 20 : 0,
left: widget.isSentByMe ? 0 : 20,
),
child: Column(
crossAxisAlignment: widget.isSentByMe
? CrossAxisAlignment.end
: CrossAxisAlignment.start,
children: [
Container(
constraints:
BoxConstraints(maxWidth: context.screenWidth * 0.74),
decoration: BoxDecoration(
color: selectedMessage == true
? (widget.isSentByMe == true
? context.color.tertiaryColor.darken(45)
: context.color.secondaryColor.darken(45))
: (widget.isSentByMe
? const Color(0xffEEEEEE)
: context.color.secondaryColor),
borderRadius: BorderRadius.circular(8)
// BorderRadius.only(
// topRight: widget.isSentByMe
// ? Radius.zero
// : const Radius.circular(10),
// topLeft: widget.isSentByMe
// ? const Radius.circular(10)
// : Radius.zero,
// bottomLeft: const Radius.circular(10),
// bottomRight: const Radius.circular(10),
// ),
),
child: Wrap(
runAlignment: WrapAlignment.end,
alignment: WrapAlignment.end,
crossAxisAlignment: WrapCrossAlignment.end,
children: [
Padding(
padding: const EdgeInsets.all(12),
child: Container(
child: widget.isChatAudio
? RecordMessage(
url: widget.audioFile ?? "",
isSentByMe: widget.isSentByMe,
)
: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (widget.hasAttachment)
AttachmentMessage(
url: widget.attachment,
isSentByMe: widget.isSentByMe),
//This is preview builder for image
ValueListenableBuilder(
valueListenable: _linkAddNotifier,
builder: (context, dynamic value, c) {
if (value == null) {
return const SizedBox.shrink();
}
return FutureBuilder(
future: AnyLinkPreview.getMetadata(
link: value),
builder: (context,
AsyncSnapshot snapshot) {
if (snapshot.connectionState ==
ConnectionState.done) {
if (snapshot.data == null) {
return const SizedBox.shrink();
}
return LinkPreviw(
snapshot: snapshot,
link: value,
);
}
return const SizedBox.shrink();
},
);
}),
SelectableText.rich(
TextSpan(
style: TextStyle(
color: (isDark && !widget.isSentByMe)
? context.color.buttonColor
: Colors.black),
children: _replaceLink().map((data) {
//This will add link to msg
if (_isLink(data)) {
//This will notify priview object that it has link
_linkAddNotifier.value = data;
_linkAddNotifier.notifyListeners();
return TextSpan(
text: data,
recognizer: TapGestureRecognizer()
..onTap = () async {
await launchUrl(
Uri.parse(data));
},
style: TextStyle(
decoration:
TextDecoration.underline,
color: Colors.blue[800]));
}
//This will make text bold
return TextSpan(
text: "",
children:
_matchAstric(data).map((text) {
if (text
.toString()
.startsWith("*") &&
text.toString().endsWith("*")) {
return TextSpan(
text:
text.replaceAll("*", ""),
style: TextStyle(
color: (isDark &&
!widget
.isSentByMe)
? context
.color.buttonColor
: Colors.black,
fontWeight:
FontWeight.w800));
}
return TextSpan(
text: text,
style: TextStyle(
color: (isDark &&
!widget.isSentByMe)
? context
.color.buttonColor
: Colors.black));
}).toList(),
style: TextStyle(
color: widget.isSentByMe
? context.color.secondaryColor
: context
.color.textColorDark),
);
}).toList(),
),
style: TextStyle(
color: (isDark && !widget.isSentByMe)
? context.color.buttonColor
: Colors.black),
),
],
),
),
),
if (widget.isSentByMe && widget.isSentNow == true) ...[
BlocConsumer<SendMessageCubit, SendMessageState>(
listener: (context, state) {
if (state is SendMessageSuccess) {
isChatSent = true;
///Value which we added locally
ValueKey? uniqueIdentifier = widget.key as ValueKey;
////We were added local id so whenit completed we will replace it with server message id
ChatMessageHandlerOLD.updateMessageId(
uniqueIdentifier.value, state.messageId);
WidgetsBinding.instance
.addPostFrameCallback((timeStamp) {
if (mounted) setState(() {});
});
}
},
builder: (context, state) {
if (state is SendMessageInProgress) {
return Padding(
padding:
const EdgeInsets.only(right: 5.0, bottom: 2),
child: Icon(
Icons.watch_later_outlined,
size: context.font.smaller,
color: Colors.black,
),
);
}
if (state is SendMessageFailed) {
return Padding(
padding:
const EdgeInsets.only(right: 5.0, bottom: 2),
child: Icon(
Icons.error,
size: context.font.smaller,
color: Colors.black,
),
);
}
return const SizedBox.shrink();
},
)
]
],
),
),
const SizedBox(
height: 5,
),
Padding(
padding: const EdgeInsets.only(right: 3.0),
child: Text(
(DateTime.parse(widget.time))
.toLocal()
.toIso8601String()
.toString()
.formatDate(format: "hh:mm aa"), //
style: TextStyle(
color: widget.isSentByMe
? context.color.textLightColor
: context.color.textLightColor),
).size(context.font.smaller),
),
],
),
),
),
);
}
@override
bool get wantKeepAlive => true;
}