Closed sakibguy closed 1 year ago
No major conflict just new fun added chatApiController.dart
No major conflict just new way soln lib/screens/chatDetails/customWidgets/chats/linkableTextView.dart
No major conflict just constant vs hand coded json key so keep it as it was lib/screens/chatDetails/customWidgets/conversationWidgets/conversationBaseWidget.dart
Fixed merge conflicts very easily
1- codebase known 2- business logic known 3- proj applied dart/flutter use cases known 4- not afraid to brake/experiment any other code parts/call
PR updates: https://github.com/alice-labs/myalice_app/pull/55
https://github.com/sakibguy/worked-issues/issues/85
https://github.com/sakibguy/worked-issues/issues/84
revamp vs revamp_features
So, closed issue.
revamp vs revamp_features
Not able to stash or switch other branch without fixing merge conflict.
sakib_conv
sakib_conv_revamp_features
git pull origin revamp_features
git status all conflicted files and copy paste from revamp_features files to exact files. Solve merge conflicts super fast. But getting a single logic issue below...
widget.conversation.source == JKChatResponse.BOT
? widget.conversation.data!.type == JKChatResponse.QUICK_REPLY
? widget.conversation.data!.buttons ?? []
: widget.conversation.data!.button ?? []
: widget.conversation.data!.type == JKChatResponse.QUICK_REPLY
? widget.conversation.data!.data?.buttons ?? []
: widget.conversation.data!.data?.button ?? [],
Did step 2 to fix.
Merge Conflicts 1
Filter
sakib_revamp_features
vsrevamp_features
https://github.com/alice-labs/myalice_app/pull/68
parent -> child branch sequence
dev_branch
git pull origin dev_branch
Manual copy paste conflicted files
to fixchatApiController.dart
copy paste below on dev_branch
import 'package:get/get_rx/src/rx_types/rx_types.dart';
class ChatResponse {
bool? success;
List<ChatDataSource>? dataSource;
List<Actions>? actions;
Data? data;
ChatResponse({this.success, this.dataSource, this.actions});
ChatResponse.fromJson(Map<String, dynamic> json) {
success = json['success'];
data = json['data'] != null ? new Data.fromJson(json['data']) : null;
if (json['dataSource'] != null) {
dataSource = <ChatDataSource>[];
json['dataSource'].forEach((v) {
dataSource!.add(new ChatDataSource.fromJson(v));
});
}
if (json['actions'] != null) {
actions = <Actions>[];
json['actions'].forEach((v) {
actions!.add(new Actions.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['success'] = this.success;
if (this.dataSource != null) {
data['dataSource'] = this.dataSource!.map((v) => v.toJson()).toList();
}
if (this.actions != null) {
data['actions'] = this.actions!.map((v) => v.toJson()).toList();
}
return data;
}
}
class ChatDataSource {
String? sId;
String? source;
String? type;
String? platformType;
//int? conversationId;
int? timestamp;
int? platformId;
int? projectId;
int? customerId;
Data? data;
bool? success;
Report? report;
int? adminId;
AdminInfo? adminInfo;
String? pusherKey;
List<String>? imageUrls;
List<String>? videoUrls;
List<String>? attachedUrl;
List<String>? audioUrl;
RxBool isPusherSucceeded = false.obs;
//For DB
String? text;
String? imageUrl;
String? subType;
dynamic? conversationId;
ChatDataSource(
{this.sId,
this.source,
this.type,
this.platformType,
this.conversationId,
this.timestamp,
this.platformId,
this.projectId,
this.customerId,
this.data,
this.success,
this.report,
this.adminId,
this.adminInfo,
this.text,
this.imageUrl,
this.subType,
this.pusherKey,
this.imageUrls,
this.videoUrls,
this.attachedUrl,
this.audioUrl});
ChatDataSource.fromJson(Map<String, dynamic> json) {
sId = json['_id'];
source = json['source'];
type = json['type'];
platformType = json['platform_type'];
conversationId = json['conversation_id'];
timestamp = json['timestamp'];
platformId = json['platform_id'];
projectId = json['project_id'];
customerId = json['customer_id'];
data = json['data'] != null ? new Data.fromJson(json['data']) : null;
success = json['success'];
report =
json['report'] != null ? new Report.fromJson(json['report']) : null;
adminId = json['admin_id'];
adminInfo = json['admin_info'] != null
? new AdminInfo.fromJson(json['admin_info'])
: null;
this.imageUrls = json['image_urls'];
this.videoUrls = json['video_urls'];
this.text = json['text'];
this.imageUrl = json['image_url'];
this.subType = json['sub_type'];
this.pusherKey = json['pusher_key'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['_id'] = this.sId;
data['source'] = this.source;
data['type'] = this.type;
data['platform_type'] = this.platformType;
data['conversation_id'] = this.conversationId;
data['timestamp'] = this.timestamp;
data['platform_id'] = this.platformId;
data['project_id'] = this.projectId;
data['customer_id'] = this.customerId;
if (this.data != null) {
data['data'] = this.data!.toJson();
}
data['success'] = this.success;
if (this.report != null) {
data['report'] = this.report!.toJson();
}
data['admin_id'] = this.adminId;
data['pusher_key'] = this.pusherKey;
if (this.adminInfo != null) {
data['admin_info'] = this.adminInfo!.toJson();
}
return data;
}
Map<String, dynamic> toJsonForDB() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['text'] =
this.source == "customer" ? this.data!.text : this.data!.data!.text;
data['image_url'] = this.source == "customer"
? this.data!.type == "attachment"
? this.data!.attachment!.type == "image"
? this.data!.attachment!.urls!.elementAt(0)
: ""
: ""
: this.data!.type == "attachment"
? this.data!.data!.subType == "image"
? this.data!.data!.urls!.elementAt(0)
: ""
: "";
data['image_urls'] = this.source == "customer"
? this.data!.type == "attachment"
? this.data!.attachment!.type == "image"
? this.data!.attachment!.urls!
: []
: []
: this.data!.type == "attachment"
? this.data!.data!.subType == "image"
? this.data!.data!.urls!
: []
: [];
data['video_urls'] = this.source == "customer"
? this.data!.type == "attachment"
? this.data!.attachment!.type == "video"
? this.data!.attachment!.urls!
: []
: []
: this.data!.type == "attachment"
? this.data!.data!.subType == "video"
? this.data!.data!.urls!
: []
: [];
data['urls'] = this.source == "customer"
? this.data!.type == "attachment"
? this.data!.attachment!.type == "file"
? this.data!.attachment!.urls!
: []
: []
: this.data!.type == "attachment"
? this.data!.data!.subType == "file"
? this.data!.data!.urls!
: []
: [];
data['source'] = this.source;
data['type'] = this.data!.type;
data['sub_type'] = this.source == "admin" || this.source == "bot"
? (this.data!.type == "attachment" ? this.data!.data!.subType : "")
: (this.data!.type == "attachment" ? this.data!.attachment!.type : "");
data['timestamp'] = this.timestamp;
return data;
}
@override
String toString() {
return 'Chats {text : ${this.source == "customer" ? this.data!.text : this.data!.data!.text}}';
}
}
class Data {
String? type;
String? text;
String? payload;
//Null extra;
Datam? data;
Attachment? attachment;
Data(
{this.type,
this.text,
this.payload,
/* this.extra, */ this.data,
this.attachment});
Data.fromJson(Map<String, dynamic> json) {
type = json['type'];
text = json['text'];
payload = json['payload'];
//extra = json['extra'];
data = json['data'] != null ? new Datam.fromJson(json['data']) : null;
attachment = json["attachment"] != null
? Attachment.fromJson(json["attachment"])
: Attachment();
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['type'] = this.type;
data['text'] = this.text;
data['payload'] = this.payload;
//data['extra'] = this.extra;
if (this.data != null) {
data['data'] = this.data!.toJson();
}
attachment = data['attachment'] != null
? new Attachment.fromJson(data['attachment'])
: null;
return data;
}
}
class Attachment {
String? type;
List<String>? urls;
Attachment({this.type, this.urls});
Attachment.fromJson(Map<String, dynamic> json) {
type = json['type'];
urls = json['urls'].cast<String>();
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['type'] = this.type;
data['urls'] = this.urls;
return data;
}
}
class Datam {
String? text;
bool? save;
//Null attribute;
List<Buttons>? buttons;
List<Elements>? elements;
//Null api;
String? subType;
List<String>? urls;
Datam(
{this.text,
this.save,
//this.attribute,
this.buttons,
//this.api,
this.subType,
this.elements});
Datam.fromJson(Map<String, dynamic> json) {
text = json['text'];
save = json['save'];
if (json['urls'] != null) {
urls = json['urls'].cast<String>();
}
if (json['elements'] != null) {
elements = <Elements>[];
json['elements'].forEach((v) {
elements!.add(new Elements.fromJson(v));
});
}
//attribute = json['attribute'];
if (json['buttons'] != null) {
buttons = <Buttons>[];
json['buttons'].forEach((v) {
buttons!.add(new Buttons.fromJson(v));
});
}
// api = json['api'];
subType = json['sub_type'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['text'] = this.text;
data['save'] = this.save;
// data['attribute'] = this.attribute;
if (this.elements != null) {
data['elements'] = this.elements!.map((v) => v.toJson()).toList();
}
if (this.buttons != null) {
data['buttons'] = this.buttons!.map((v) => v.toJson()).toList();
}
//data['api'] = this.api;
data['sub_type'] = this.subType;
data['urls'] = this.urls;
return data;
}
}
class Elements {
int? id;
String? url;
String? image;
String? title;
List<ElementButtons>? buttons;
String? subtitle;
Elements(
{this.id, this.url, this.image, this.title, this.buttons, this.subtitle});
Elements.fromJson(Map<String, dynamic> json) {
id = json['id'];
url = json['url'];
image = json['image'];
title = json['title'];
if (json['buttons'] != null) {
buttons = <ElementButtons>[];
json['buttons'].forEach((v) {
buttons!.add(new ElementButtons.fromJson(v));
});
}
subtitle = json['subtitle'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['url'] = this.url;
data['image'] = this.image;
data['title'] = this.title;
if (this.buttons != null) {
data['buttons'] = this.buttons!.map((v) => v.toJson()).toList();
}
data['subtitle'] = this.subtitle;
return data;
}
}
class ElementButtons {
int? id;
String? type;
String? extra;
String? title;
String? value;
String? payload;
String? verbose;
int? formSequence;
//Null formSequenceTitle;
bool? messengerExtensions;
String? webviewHeightRatio;
ElementButtons(
{this.id,
this.type,
this.extra,
this.title,
this.value,
this.payload,
this.verbose,
this.formSequence,
// this.formSequenceTitle,
this.messengerExtensions,
this.webviewHeightRatio});
ElementButtons.fromJson(Map<String, dynamic> json) {
id = json['id'];
type = json['type'];
extra = json['extra'];
title = json['title'];
value = json['value'].toString();
payload = json['payload'];
verbose = json['verbose'];
formSequence = json['form_sequence'];
// formSequenceTitle = json['form_sequence_title'];
messengerExtensions = json['messenger_extensions'];
webviewHeightRatio = json['webview_height_ratio'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['type'] = this.type;
data['extra'] = this.extra;
data['title'] = this.title;
data['value'] = this.value;
data['payload'] = this.payload;
data['verbose'] = this.verbose;
data['form_sequence'] = this.formSequence;
// data['form_sequence_title'] = this.formSequenceTitle;
data['messenger_extensions'] = this.messengerExtensions;
data['webview_height_ratio'] = this.webviewHeightRatio;
return data;
}
}
class Buttons {
int? columns;
int? rows;
String? text;
String? actionType;
String? actionBody;
String? title;
Buttons(
{this.columns, this.rows, this.text, this.actionType, this.actionBody, this.title});
Buttons.fromJson(Map<String, dynamic> json) {
columns = json['Columns'];
rows = json['Rows'];
text = json['Text'];
actionType = json['ActionType'];
actionBody = json['ActionBody'];
title = json['title'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['Columns'] = this.columns;
data['Rows'] = this.rows;
data['Text'] = this.text;
data['ActionType'] = this.actionType;
data['ActionBody'] = this.actionBody;
data['title'] = this.title;
return data;
}
}
class Report {
int? status;
String? statusMessage;
int? messageToken;
String? chatHostname;
dynamic? error;
Report(
{this.status, this.statusMessage, this.messageToken, this.chatHostname,});
Report.fromJson(Map<String, dynamic> json) {
status = json['status'];
statusMessage = json['status_message'];
messageToken = json['message_token'];
chatHostname = json['chat_hostname'];
error = json['error'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['status'] = this.status;
data['status_message'] = this.statusMessage;
data['message_token'] = this.messageToken;
data['chat_hostname'] = this.chatHostname;
data['error'] = this.error;
return data;
}
}
class AdminInfo {
int? id;
String? email;
String? avatar;
String? fullName;
AdminInfo({this.id, this.email, this.avatar, this.fullName});
AdminInfo.fromJson(Map<String, dynamic> json) {
id = json['id'];
email = json['email'];
avatar = json['avatar'];
fullName = json['full_name'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['email'] = this.email;
data['avatar'] = this.avatar;
data['full_name'] = this.fullName;
return data;
}
}
class Actions {
String? action;
bool? isAllowed;
Actions({this.action, this.isAllowed});
Actions.fromJson(Map<String, dynamic> json) {
action = json['action'];
isAllowed = json['is_allowed'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['action'] = this.action;
data['is_allowed'] = this.isAllowed;
return data;
}
}
conversationBaseWidget.dart
copy paste below on dev_branch
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:get/get.dart';
import 'package:get/get_core/src/get_main.dart';
import 'package:myalice/controllers/apiControllers/inboxController.dart';
import 'package:myalice/screens/chatDetails/customWidgets/conversationWidgets/conversationVideoViewWidget.dart';
import 'package:myalice/utils/colors.dart';
import 'package:myalice/utils/readTimeStamp.dart';
import '../../../../models/responseModels/chatResponse.dart';
import '../../styles/textStyles.dart';
import '../modals/convCopyBookmarkModal.dart';
import 'conversationAttachedFileView.dart';
import 'conversationAudioPlayerWidget.dart';
import 'conversationGalleryViewer.dart';
import '../chats/linkableTextView.dart';
import 'conversationBtnQuickReplyWidget.dart';
import 'conversationImageViewWidget.dart';
class ConversationBaseWidget extends StatefulWidget {
final url;
final name;
final ChatDataSource conversation;
final platformType;
final index;
final lastMsgIndex;
ConversationBaseWidget(
{Key? key,
required this.url,
required this.name,
required this.conversation,
required this.platformType,
required this.index,
required this.lastMsgIndex})
: super(key: key);
@override
_ConversationBaseWidget createState() {
return _ConversationBaseWidget();
}
}
class _ConversationBaseWidget extends State<ConversationBaseWidget> {
String imageUrl = '';
String sourceName = '';
String sourceNameForInitials = '';
InboxController? _inboxController;
@override
void initState() {
// TODO: implement initState
super.initState();
init();
}
String getNameInitials(String? groupName) {
if (widget.conversation.source == 'bot') {
groupName =
_inboxController != null && _inboxController?.projectName != null
? _inboxController?.projectName
: groupName;
}
List<String> list = groupName!.split(' ');
try {
if (list.length == 0) {
return "";
} else if (list.length == 1) {
if (list.elementAt(0).isEmpty) {
return '';
}
return (list.elementAt(0)[0]).toUpperCase();
} else {
String s = '';
s = list.elementAt(0).isNotEmpty ? list.elementAt(0)[0] : '';
s += list.elementAt(1).isNotEmpty ? list.elementAt(1)[0] : '';
return (s).toUpperCase();
}
} catch (e) {
print(e);
return groupName[0];
}
}
bool isTemplateAvailable = false;
init() {
try {
_inboxController = Get.find<InboxController>();
} catch (e) {}
imageUrl = widget.conversation.source == 'customer'
? widget.url
: widget.conversation.source == 'echo' ? '' : widget.conversation.source == 'bot'
? _inboxController != null &&
_inboxController?.selectedProject != null &&
_inboxController?.selectedProject?.image != null
? _inboxController?.selectedProject?.image
: ''
: widget.conversation.adminInfo?.avatar;
//FB Business Manager
sourceNameForInitials = widget.conversation.source == 'customer'
? widget.name.isNotEmpty
? widget.name
: 'Anonymous'
: widget.conversation.source == 'bot'
? 'Bot'
: widget.conversation.adminInfo != null
? widget.conversation.adminInfo?.fullName : '';
sourceName = widget.conversation.source == 'customer'
? widget.name.isNotEmpty
? widget.name
: 'Anonymous'
: widget.conversation.source == 'echo' ? 'FB Business Manager' : widget.conversation.source == 'bot'
? 'Bot'
: widget.conversation.adminInfo != null
? widget.conversation.type != 'note' ? widget.conversation.adminInfo?.fullName : isUserCreator()
: '';
isTemplateAvailable = getIsTemplateAvailable();
}
String isUserCreator(){
return (_inboxController?.user.dataSource!.id == widget.conversation.adminInfo?.id ?
'You' : widget.conversation.adminInfo?.fullName)??'';
return '';
}
bool getIsTemplateAvailable() {
return widget.conversation.type == "gallery" ||
widget.conversation.type == "quick_reply" ||
widget.conversation.type == "button" ||
(widget.conversation.type == "attachment" &&
(widget.conversation.subType == "image" ||
widget.conversation.subType == "video" ||
widget.conversation.subType == "file" ||
widget.conversation.subType == "audio"));
}
double getBorderBarHeight() {
int btnCount = 0;
double heightGallery = 159;
if ((widget.conversation.subType == "audio")) {
print(widget.conversation.toString());
}
if (widget.conversation.type == "gallery")
for (int i = 0;
i < widget.conversation.data!.data!.elements!.length;
i++) {
if (widget.conversation.data!.data!.elements![i].buttons!.length >
btnCount)
btnCount =
widget.conversation.data!.data!.elements![i].buttons!.length;
}
heightGallery += btnCount * 33;
double height = widget.conversation.type == "quick_reply" ||
widget.conversation.type == "button"
? 30
: widget.conversation.type == "gallery"
? heightGallery
: (widget.conversation.type == "attachment")
? (widget.conversation.subType == "image" &&
widget.conversation.imageUrls != null &&
widget.conversation.imageUrls?.length == 1) ||
(widget.conversation.subType == "video" &&
widget.conversation.videoUrls != null &&
widget.conversation.videoUrls?.length == 1)
? 120
: (widget.conversation.subType == "image" &&
widget.conversation.imageUrls != null &&
widget.conversation.imageUrls!.length > 1) ||
(widget.conversation.subType == "video" &&
widget.conversation.videoUrls != null &&
widget.conversation.videoUrls!.length > 1)
? 70
: (widget.conversation.subType == "file")
? 32
: (widget.conversation.subType == "audio")
? 45
: 30
: 15;
if ((widget.conversation.type == "attachment" &&
widget.conversation.subType == "image")) print(height);
return height;
}
checkIsTemplate() {
String templateMsg = '';
bool isTemplate = false;
isTemplate = false;
if (widget.conversation.type == "betterdocs" ||
widget.conversation.type == "button" ||
widget.conversation.type == "quick_reply" ||
widget.conversation.type == "product_discovery" ||
widget.conversation.type == "place_order" ||
widget.conversation.type == "view_cart" ) {
templateMsg = widget.conversation.type ?? 'Unknown content' + " Was Sent";
isTemplate = true;
}
}
String templateMsg = "";
bool isTemplate = false;
@override
Widget build(BuildContext context) {
if (widget.conversation.type == "betterdocs" ||
widget.conversation.type == "product_discovery" ||
widget.conversation.type == "place_order" ||
widget.conversation.type == "view_cart" ) {
templateMsg =
(widget.conversation.type ?? 'Unknown content') + " Was Sent";
isTemplate = true;
}
return Container(
color: widget.conversation.type ==
"note" ? AliceColors.ALICE_ORANGE_100 : AliceColors.ALICE_WHITE,
padding: EdgeInsets.only(left: 8, right: 8, top: 9, bottom: 9),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
/*(object.chats.elementAt(index)!.source == "customer"
? MainAxisAlignment.start
: MainAxisAlignment.end),*/
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: EdgeInsets.only(bottom: 0),
child: imageUrl != null && imageUrl != ""
? CircleAvatar(
radius: 16,
backgroundImage:
CachedNetworkImageProvider(imageUrl),
)
: CircleAvatar(
child: widget.conversation.source == 'bot' || widget.conversation.source == 'echo'
? SvgPicture.asset(
widget.conversation.source == 'echo' ?"assets/chat_icon/echo_avatar.svg" : "assets/chat_icon/alice_logo.svg",
)
: Text(
getNameInitials(sourceNameForInitials),
maxLines: 1,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
color:
AliceColors.ALICE_WHITE),
),
radius: 16,
backgroundColor:
widget.conversation.source == 'bot'
? AliceColors.ALICE_WHITE
: AliceColors.ALICE_GREY_500,
)),
SizedBox(
width: 12,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Row(
children: [
Text(
sourceName,
textAlign: TextAlign.center,
style: widget.conversation.source ==
'customer'
? TextStyles.headerNameStyleCustomer
: widget.conversation.source == 'echo'? TextStyles.headerNameStyleEcho: widget.conversation.source == 'bot'
? TextStyles.headerNameStyleBot
: TextStyles.headerNameStyleAdmin,
),
SizedBox(
width: 8,
),
if(widget.conversation.type == 'note')
Text('Private Note', style:TextStyle(
fontWeight: FontWeight.w500,
color: AliceColors.ALICE_ORANGE_900,
fontSize: 12)) ,
if(widget.conversation.type == 'note')
SizedBox(
width: 8,
),
Text(
readTimeFromDateTime(
widget.conversation.timestamp ?? 0),
textAlign: TextAlign.center,
style: TextStyles.actionTextStyle,
),
],
),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: 4,
),
if (((widget.conversation.text ??
"")
.isNotEmpty ||
(isTemplate &&
templateMsg.isNotEmpty)))
ConstrainedBox(
constraints: BoxConstraints(
maxWidth:
MediaQuery.of(context)
.size
.width -
100),
child: InkWell(
onLongPress: () {
openCopyModal(context);
},
child: LinkableTextView(
text: !isTemplate
? widget.conversation
.text ??
""
: templateMsg,
source: (widget
.conversation
.source!),
isSuccess: widget
.conversation
.report ==
null ||
widget
.conversation
.report!
.error ==
null,
platformType: (widget
.conversation
.platformType) ==
null ||
(widget.conversation
.platformType)!
.isEmpty
? widget.platformType
: widget.conversation
.platformType,
)),
),
SizedBox(
height: ((widget.conversation
.text ??
"")
.isNotEmpty ||
(isTemplate &&
templateMsg
.isNotEmpty)) &&
isTemplateAvailable
? 4
: 0,
),
if (isTemplateAvailable)
Row( mainAxisSize: MainAxisSize.min, children: <Widget>[
Container(
width: 4,
//height: double.infinity,
height:
getBorderBarHeight(),
decoration: BoxDecoration(
color: widget
.conversation
.source !=
'customer'
? AliceColors
.ALICE_GREY_200
: AliceColors
.ALICE_GREEN_300,
borderRadius:
BorderRadius
.circular(100)),
margin:
const EdgeInsets.only(
right: 8)),
(widget.conversation.type ==
"quick_reply" ||
widget.conversation.type ==
"button")
? ConvBtnQuickReplyWidget(
buttons: widget
.conversation
.data!
.data!
.buttons!,
type: widget
.conversation.type!,
text: widget
.conversation
.data!
.data!
.text ??
"")
: (widget.conversation.type ==
"attachment")
? ((widget.conversation
.subType ==
"image")
? ConvImageView(
links: widget
.conversation
.imageUrls!)
: (widget.conversation.subType ==
"video")
? ConvVideoViewWidget(
links: widget.conversation.videoUrls!)
: (widget.conversation.subType == "file" && widget.conversation.attachedUrl != null)
? AttachedFileViewer(links: widget.conversation.attachedUrl!)
: (widget.conversation.subType == "audio")
? new ConvAudioViewWidget(
links:
widget.conversation.audioUrl!,
)
: Container())
: widget.conversation.type == "gallery"
? GalleryViewer(elements: widget.conversation.data!.data!.elements!)
: Container()
]),
if (!isTemplateAvailable &&
!((widget.conversation.text ??
"")
.isNotEmpty ||
(isTemplate &&
templateMsg
.isNotEmpty)))
Container(
child: (widget
.conversation
.type
?.isNotEmpty ??
false)
? Text(
'${widget.conversation.type} ${widget.conversation.subType ?? ''} was sent.')
: null)
]),
Visibility(
visible: widget.conversation.source !=
'customer' && widget.conversation.type !=
'note' &&
(widget.index == widget.lastMsgIndex || !(widget
.conversation
.report ==
null ||
widget
.conversation
.report!
.error ==
null)),
child: Obx(() => MyTooltip(
message: widget.conversation
.isPusherSucceeded.isFalse
? 'Sending'
: widget.conversation
.report ==
null ||
widget
.conversation
.report!
.error ==
null
? 'Delivered'
: 'Failed to deliver',
child: Container(
margin: EdgeInsets.only(
left: 6, right: 6),
child: SvgPicture.asset(
widget
.conversation
.isPusherSucceeded
.isFalse
? "assets/chat_icon/report_sending.svg"
: widget.conversation
.report ==
null ||
widget
.conversation
.report!
.error ==
null
? "assets/chat_icon/report_success.svg"
: "assets/chat_icon/report_failed.svg",
)))))
]),
]),
])
])
]));
}
openCopyModal(BuildContext context) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(6), topRight: Radius.circular(6)),
),
builder: (context) {
return ConvCopyAndBookmarkModal(
text: !isTemplate ? widget.conversation.text ?? "" : templateMsg,
);
}).whenComplete(() {});
}
}
class MyTooltip extends StatelessWidget {
final Widget child;
final String message;
MyTooltip({required this.message, required this.child});
@override
Widget build(BuildContext context) {
final key = GlobalKey<State<Tooltip>>();
return Tooltip(
key: key,
message: message,
preferBelow: false,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(3),
color: AliceColors.ALICE_GREY_TOOLTIP,
),
padding:
const EdgeInsets.only(left: 15.0, right: 15, top: 10, bottom: 10),
margin: const EdgeInsets.only(left: 15, right: 15, top: 4, bottom: 0),
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => _onTap(key),
child: child,
),
);
}
void _onTap(GlobalKey key) {
final dynamic tooltip = key.currentState;
tooltip?.ensureTooltipVisible();
Future.delayed(Duration(seconds: 2), () {
tooltip?.deactivate();
});
}
}
chatResponse.dart
copy paste below on dev_branch
import 'package:get/get_rx/src/rx_types/rx_types.dart';
class ChatResponse {
bool? success;
List<ChatDataSource>? dataSource;
List<Actions>? actions;
Data? data;
ChatResponse({this.success, this.dataSource, this.actions});
ChatResponse.fromJson(Map<String, dynamic> json) {
success = json['success'];
data = json['data'] != null ? new Data.fromJson(json['data']) : null;
if (json['dataSource'] != null) {
dataSource = <ChatDataSource>[];
json['dataSource'].forEach((v) {
dataSource!.add(new ChatDataSource.fromJson(v));
});
}
if (json['actions'] != null) {
actions = <Actions>[];
json['actions'].forEach((v) {
actions!.add(new Actions.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['success'] = this.success;
if (this.dataSource != null) {
data['dataSource'] = this.dataSource!.map((v) => v.toJson()).toList();
}
if (this.actions != null) {
data['actions'] = this.actions!.map((v) => v.toJson()).toList();
}
return data;
}
}
class ChatDataSource {
String? sId;
String? source;
String? type;
String? platformType;
//int? conversationId;
int? timestamp;
int? platformId;
int? projectId;
int? customerId;
Data? data;
bool? success;
Report? report;
int? adminId;
AdminInfo? adminInfo;
String? pusherKey;
List<String>? imageUrls;
List<String>? videoUrls;
List<String>? attachedUrl;
List<String>? audioUrl;
RxBool isPusherSucceeded = false.obs;
//For DB
String? text;
String? imageUrl;
String? subType;
dynamic? conversationId;
ChatDataSource(
{this.sId,
this.source,
this.type,
this.platformType,
this.conversationId,
this.timestamp,
this.platformId,
this.projectId,
this.customerId,
this.data,
this.success,
this.report,
this.adminId,
this.adminInfo,
this.text,
this.imageUrl,
this.subType,
this.pusherKey,
this.imageUrls,
this.videoUrls,
this.attachedUrl,
this.audioUrl});
ChatDataSource.fromJson(Map<String, dynamic> json) {
sId = json['_id'];
source = json['source'];
type = json['type'];
platformType = json['platform_type'];
conversationId = json['conversation_id'];
timestamp = json['timestamp'];
platformId = json['platform_id'];
projectId = json['project_id'];
customerId = json['customer_id'];
data = json['data'] != null ? new Data.fromJson(json['data']) : null;
success = json['success'];
report =
json['report'] != null ? new Report.fromJson(json['report']) : null;
adminId = json['admin_id'];
adminInfo = json['admin_info'] != null
? new AdminInfo.fromJson(json['admin_info'])
: null;
this.imageUrls = json['image_urls'];
this.videoUrls = json['video_urls'];
this.text = json['text'];
this.imageUrl = json['image_url'];
this.subType = json['sub_type'];
this.pusherKey = json['pusher_key'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['_id'] = this.sId;
data['source'] = this.source;
data['type'] = this.type;
data['platform_type'] = this.platformType;
data['conversation_id'] = this.conversationId;
data['timestamp'] = this.timestamp;
data['platform_id'] = this.platformId;
data['project_id'] = this.projectId;
data['customer_id'] = this.customerId;
if (this.data != null) {
data['data'] = this.data!.toJson();
}
data['success'] = this.success;
if (this.report != null) {
data['report'] = this.report!.toJson();
}
data['admin_id'] = this.adminId;
data['pusher_key'] = this.pusherKey;
if (this.adminInfo != null) {
data['admin_info'] = this.adminInfo!.toJson();
}
return data;
}
Map<String, dynamic> toJsonForDB() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['text'] =
this.source == "customer" ? this.data!.text : this.data!.data!.text;
data['image_url'] = this.source == "customer"
? this.data!.type == "attachment"
? this.data!.attachment!.type == "image"
? this.data!.attachment!.urls!.elementAt(0)
: ""
: ""
: this.data!.type == "attachment"
? this.data!.data!.subType == "image"
? this.data!.data!.urls!.elementAt(0)
: ""
: "";
data['image_urls'] = this.source == "customer"
? this.data!.type == "attachment"
? this.data!.attachment!.type == "image"
? this.data!.attachment!.urls!
: []
: []
: this.data!.type == "attachment"
? this.data!.data!.subType == "image"
? this.data!.data!.urls!
: []
: [];
data['video_urls'] = this.source == "customer"
? this.data!.type == "attachment"
? this.data!.attachment!.type == "video"
? this.data!.attachment!.urls!
: []
: []
: this.data!.type == "attachment"
? this.data!.data!.subType == "video"
? this.data!.data!.urls!
: []
: [];
data['urls'] = this.source == "customer"
? this.data!.type == "attachment"
? this.data!.attachment!.type == "file"
? this.data!.attachment!.urls!
: []
: []
: this.data!.type == "attachment"
? this.data!.data!.subType == "file"
? this.data!.data!.urls!
: []
: [];
data['source'] = this.source;
data['type'] = this.data!.type;
data['sub_type'] = this.source == "admin" || this.source == "bot"
? (this.data!.type == "attachment" ? this.data!.data!.subType : "")
: (this.data!.type == "attachment" ? this.data!.attachment!.type : "");
data['timestamp'] = this.timestamp;
return data;
}
@override
String toString() {
return 'Chats {text : ${this.source == "customer" ? this.data!.text : this.data!.data!.text}}';
}
}
class Data {
String? type;
String? text;
String? payload;
//Null extra;
Datam? data;
Attachment? attachment;
Data(
{this.type,
this.text,
this.payload,
/* this.extra, */ this.data,
this.attachment});
Data.fromJson(Map<String, dynamic> json) {
type = json['type'];
text = json['text'];
payload = json['payload'];
//extra = json['extra'];
data = json['data'] != null ? new Datam.fromJson(json['data']) : null;
attachment = json["attachment"] != null
? Attachment.fromJson(json["attachment"])
: Attachment();
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['type'] = this.type;
data['text'] = this.text;
data['payload'] = this.payload;
//data['extra'] = this.extra;
if (this.data != null) {
data['data'] = this.data!.toJson();
}
attachment = data['attachment'] != null
? new Attachment.fromJson(data['attachment'])
: null;
return data;
}
}
class Attachment {
String? type;
List<String>? urls;
Attachment({this.type, this.urls});
Attachment.fromJson(Map<String, dynamic> json) {
type = json['type'];
urls = json['urls'].cast<String>();
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['type'] = this.type;
data['urls'] = this.urls;
return data;
}
}
class Datam {
String? text;
bool? save;
//Null attribute;
List<Buttons>? buttons;
List<Elements>? elements;
//Null api;
String? subType;
List<String>? urls;
Datam(
{this.text,
this.save,
//this.attribute,
this.buttons,
//this.api,
this.subType,
this.elements});
Datam.fromJson(Map<String, dynamic> json) {
text = json['text'];
save = json['save'];
if (json['urls'] != null) {
urls = json['urls'].cast<String>();
}
if (json['elements'] != null) {
elements = <Elements>[];
json['elements'].forEach((v) {
elements!.add(new Elements.fromJson(v));
});
}
//attribute = json['attribute'];
if (json['buttons'] != null) {
buttons = <Buttons>[];
json['buttons'].forEach((v) {
buttons!.add(new Buttons.fromJson(v));
});
}
// api = json['api'];
subType = json['sub_type'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['text'] = this.text;
data['save'] = this.save;
// data['attribute'] = this.attribute;
if (this.elements != null) {
data['elements'] = this.elements!.map((v) => v.toJson()).toList();
}
if (this.buttons != null) {
data['buttons'] = this.buttons!.map((v) => v.toJson()).toList();
}
//data['api'] = this.api;
data['sub_type'] = this.subType;
data['urls'] = this.urls;
return data;
}
}
class Elements {
int? id;
String? url;
String? image;
String? title;
List<ElementButtons>? buttons;
String? subtitle;
Elements(
{this.id, this.url, this.image, this.title, this.buttons, this.subtitle});
Elements.fromJson(Map<String, dynamic> json) {
id = json['id'];
url = json['url'];
image = json['image'];
title = json['title'];
if (json['buttons'] != null) {
buttons = <ElementButtons>[];
json['buttons'].forEach((v) {
buttons!.add(new ElementButtons.fromJson(v));
});
}
subtitle = json['subtitle'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['url'] = this.url;
data['image'] = this.image;
data['title'] = this.title;
if (this.buttons != null) {
data['buttons'] = this.buttons!.map((v) => v.toJson()).toList();
}
data['subtitle'] = this.subtitle;
return data;
}
}
class ElementButtons {
int? id;
String? type;
String? extra;
String? title;
String? value;
String? payload;
String? verbose;
int? formSequence;
//Null formSequenceTitle;
bool? messengerExtensions;
String? webviewHeightRatio;
ElementButtons(
{this.id,
this.type,
this.extra,
this.title,
this.value,
this.payload,
this.verbose,
this.formSequence,
// this.formSequenceTitle,
this.messengerExtensions,
this.webviewHeightRatio});
ElementButtons.fromJson(Map<String, dynamic> json) {
id = json['id'];
type = json['type'];
extra = json['extra'];
title = json['title'];
value = json['value'].toString();
payload = json['payload'];
verbose = json['verbose'];
formSequence = json['form_sequence'];
// formSequenceTitle = json['form_sequence_title'];
messengerExtensions = json['messenger_extensions'];
webviewHeightRatio = json['webview_height_ratio'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['type'] = this.type;
data['extra'] = this.extra;
data['title'] = this.title;
data['value'] = this.value;
data['payload'] = this.payload;
data['verbose'] = this.verbose;
data['form_sequence'] = this.formSequence;
// data['form_sequence_title'] = this.formSequenceTitle;
data['messenger_extensions'] = this.messengerExtensions;
data['webview_height_ratio'] = this.webviewHeightRatio;
return data;
}
}
class Buttons {
int? columns;
int? rows;
String? text;
String? actionType;
String? actionBody;
String? title;
Buttons(
{this.columns, this.rows, this.text, this.actionType, this.actionBody, this.title});
Buttons.fromJson(Map<String, dynamic> json) {
columns = json['Columns'];
rows = json['Rows'];
text = json['Text'];
actionType = json['ActionType'];
actionBody = json['ActionBody'];
title = json['title'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['Columns'] = this.columns;
data['Rows'] = this.rows;
data['Text'] = this.text;
data['ActionType'] = this.actionType;
data['ActionBody'] = this.actionBody;
data['title'] = this.title;
return data;
}
}
class Report {
int? status;
String? statusMessage;
int? messageToken;
String? chatHostname;
dynamic? error;
Report(
{this.status, this.statusMessage, this.messageToken, this.chatHostname,});
Report.fromJson(Map<String, dynamic> json) {
status = json['status'];
statusMessage = json['status_message'];
messageToken = json['message_token'];
chatHostname = json['chat_hostname'];
error = json['error'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['status'] = this.status;
data['status_message'] = this.statusMessage;
data['message_token'] = this.messageToken;
data['chat_hostname'] = this.chatHostname;
data['error'] = this.error;
return data;
}
}
class AdminInfo {
int? id;
String? email;
String? avatar;
String? fullName;
AdminInfo({this.id, this.email, this.avatar, this.fullName});
AdminInfo.fromJson(Map<String, dynamic> json) {
id = json['id'];
email = json['email'];
avatar = json['avatar'];
fullName = json['full_name'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['email'] = this.email;
data['avatar'] = this.avatar;
data['full_name'] = this.fullName;
return data;
}
}
class Actions {
String? action;
bool? isAllowed;
Actions({this.action, this.isAllowed});
Actions.fromJson(Map<String, dynamic> json) {
action = json['action'];
isAllowed = json['is_allowed'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['action'] = this.action;
data['is_allowed'] = this.isAllowed;
return data;
}
}
chatTextingViewController.dart
copy paste below on dev_branch
import 'dart:convert';
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:get/get.dart';
import 'package:get/get_state_manager/src/simple/get_controllers.dart';
import 'package:myalice/controllers/apiControllers/inboxController.dart';
import 'package:myalice/models/responseModels/platform/whatsAppMsgTemplate.dart';
import 'package:myalice/models/responseModels/ticketsResponseModels/platform.dart';
import '../../models/responseModels/cannedResponse/data_source.dart';
import '../../models/responseModels/chatResponse.dart';
import '../apiControllers/chatApiController.dart';
class ChatTextingController extends GetxController {
int CANNED_RESPONSE_VISIBLE = 1;
int IMAGE_LIST_VISIBLE = 2;
int ONLY_TEXT_FIELD_VISIBLE = 3;
double HEIGHT_INITIAL = 0.26;
double HEIGHT_SHOW_ONE_LIST = 0.40;
double HEIGHT_SHOW_TWO_LIST = 0.60;
RxString _chatText = ''.obs;
RxString _noteText = ''.obs;
String get noteText => _noteText.value;
WhatsAppMsgTemplate? _template;
WhatsAppMsgTemplate? get template => _template;
set template(WhatsAppMsgTemplate? template) {
_template = template;
}
set noteText(String value) {
_noteText.value = value;
}
RxBool isShowChatTextingView = false.obs;
RxBool isShowNoteTextingView = false.obs;
RxBool isShowTemplateTextingView = false.obs;
RxBool showEmoji = false.obs;
String chatHintText = 'Write your reply here';
String chatNoteHintText = 'Write your note here';
List<CannedDataSource>? list = [];
RxInt _viewVisibility = 0.obs;
RxDouble _viewInitialHeight = 0.26.obs;
late ChatApiController _chatApiController;
late InboxController _inboxController;
RxString audioFilePath = ''.obs;
final controller = DraggableScrollableController();
RxList<File> imageList = <File>[].obs;
late BuildContext draggableSheetContext;
double get viewInitialHeight => _viewInitialHeight.value;
init() {
try {
_chatApiController = Get.find<ChatApiController>();
_inboxController = Get.find<InboxController>();
} catch (e) {}
}
set viewInitialHeight(double value) {
_viewInitialHeight.value = value;
}
int get viewVisibility => _viewVisibility.value;
set viewVisibility(int value) {
_viewVisibility.value = value;
}
set chatText(String text) {
this._chatText.value = text;
}
String get chatText {
return this._chatText.value;
}
int numLines = 0;
int calculateNoOfLines() {
numLines = '\n'.allMatches(chatText).length + 1;
return numLines;
}
setHeightIfImageListExist() {
if (imageList.length > 0 && viewInitialHeight < 0.36) {
viewInitialHeight = 0.36;
} else if (imageList.length == 0 && viewInitialHeight == 0.36) {
viewInitialHeight = 0.26;
}
}
int SHOW_NOTE_TESTING = 1;
int SHOW_CHAT_TESTING = 2;
int SHOW_TEMPLATE_TESTING = 3;
int HIDE_NOTE_CHAT = 0;
Platform? getPlatform() {
return _chatApiController.customer.platform;
}
toggleTextingView(int setView) {
if (setView == SHOW_CHAT_TESTING) {
isShowChatTextingView.value = true;
isShowNoteTextingView.value = false;
isShowTemplateTextingView.value = false;
} else if (setView == SHOW_NOTE_TESTING) {
isShowChatTextingView.value = false;
isShowNoteTextingView.value = true;
isShowTemplateTextingView.value = false;
} else if (setView == SHOW_TEMPLATE_TESTING) {
isShowChatTextingView.value = false;
isShowNoteTextingView.value = false;
isShowTemplateTextingView.value = true;
}
}
sendChat() {
sendText();
if (audioFilePath.isNotEmpty) {
sendChatWithAudio();
}
if (imageList.isNotEmpty) {
List<File> tempImageList = imageList.value;
imageList.value = [];
sendMultiplePhoto(tempImageList);
}
}
/*
action: "direct_message"
audio: null
image: null
template: "1708232642911371"
text: "A template named full_template_1 will be sent."
*/
Future<bool> sendText() async {
String text = chatText.trim();
chatText = '';
if (text.isNotEmpty) {
await _chatApiController
.sendChats(_chatApiController.ticketID.toString(), text, "")
.then((value) {
if (value
.success! /* && widget.conversationId == value.dataSource!.conversationId*/) {
addToChatResponse(value.dataSource!);
}
});
chatText = "";
return true;
}
return false;
}
Future<bool> sendTemplate() async {
String strText = 'Template ${template!.name} was sent';
if (template != null && template!.id.isNotEmpty) {
await _chatApiController
.sendTemplateChats(
_chatApiController.ticketID.toString(), strText, template!.id)
.then((value) {
if (value
.success! /* && widget.conversationId == value.dataSource!.conversationId*/) {
addToChatResponse(value.dataSource!);
}
});
chatText = "";
return true;
}
return false;
}
int? getChatPlatformId() {
if (_chatApiController.customer.platform != null) {
return _chatApiController.customer.platform!.id;
}
return null;
}
addToChatResponse(dynamic datasource) {
ChatDataSource response = ChatDataSource.fromJson(datasource.toJson());
response.isPusherSucceeded.value = false;
try {
response.text = response.source == "customer"
? response.data!.text
: response.data!.data!.text;
response.imageUrl = response.source == "customer"
? response.data!.type == "attachment"
? response.data!.attachment!.type == "image"
? response.data!.attachment!.urls!.elementAt(0)
: ""
: ""
: response.data!.type == "attachment"
? response.data!.data!.subType == "image"
? response.data!.data!.urls!.elementAt(0)
: ""
: "";
response.imageUrls = response.source == "customer"
? response.data!.type == "attachment"
? response.data!.attachment!.type == "image"
? response.data!.attachment!.urls!
: []
: []
: response.data!.type == "attachment"
? response.data!.data!.subType == "image"
? response.data!.data!.urls!
: []
: [];
response.videoUrls = response.source == "customer"
? response.data!.type == "attachment"
? response.data!.attachment!.type == "video"
? response.data!.attachment!.urls!
: []
: []
: response.data!.type == "attachment"
? response.data!.data!.subType == "video"
? response.data!.data!.urls!
: []
: [];
response.attachedUrl = response.source == "customer"
? response.data!.type == "attachment"
? response.data!.attachment!.type == "file"
? response.data!.attachment!.urls!
: []
: []
: response.data!.type == "attachment"
? response.data!.data!.subType == "file"
? response.data!.data!.urls!
: []
: [];
response.audioUrl = response.source == "customer"
? response.data!.type == "attachment"
? response.data!.attachment!.type == "audio"
? response.data!.attachment!.urls!
: []
: []
: response.data!.type == "attachment"
? response.data!.data!.subType == "audio"
? response.data!.data!.urls!
: []
: [];
response.type = response.data!.type;
response.subType = response.source == "admin" || response.source == "bot"
? (response.data!.type == "attachment"
? response.data!.data!.subType
: "")
: (response.data!.type == "attachment"
? response.data!.attachment!.type
: "");
} catch (e) {
print(e);
}
_chatApiController.addIfPusherKeyNotExist(response);
}
sendMultiplePhoto(List<File> list) async {
for (int i = 0; i < list.length; i++) {
sendChatWithImage(list[i]);
}
}
sendChatWithImage(File file) {
_chatApiController.uploadPhoto(file).then((value) {
File(file.path).delete();
if (value.success!) {
String convId = '' + _chatApiController.conversationId.toString();
Get.find<ChatApiController>()
.sendChats(_chatApiController.ticketID.toString(), "",
value.dataSource!.s3Url!)
.then((value) {
if (value.success! &&
convId == Get.find<ChatApiController>().conversationId) {
addToChatResponse(value.dataSource!);
}
});
}
});
}
sendChatWithAudio() {
String filePath = audioFilePath.value;
audioFilePath.value = '';
_chatApiController.uploadAudio(filePath).then((value) {
File(filePath).delete();
audioFilePath.value = '';
if (value.statusCode == 200) {
String convId = '' + _chatApiController.conversationId.toString();
String url = value.data['url'];
Get.find<ChatApiController>()
.sendChatsAudio(
_chatApiController.ticketID.toString(), "", value.data['url'])
.then((value) {
if (value.success! &&
convId == Get.find<ChatApiController>().conversationId) {
addToChatResponse(value.dataSource!);
}
});
}
});
}
sendNote() {
String convId = '' + _chatApiController.conversationId;
String note = noteText.trim();
noteText = '';
if (note.isNotEmpty)
_chatApiController.saveNote(note).then((value) {
if (value.success! && convId == _chatApiController.conversationId) {
addToChatResponse(value.dataSource);
}
});
noteText = '';
}
List<String> supportedPlatformTypeList = [
"facebook_messenger",
"whatsapp_bsp",
"webchat",
"livechat_messenger",
"app_messenger"
];
bool isSupportedPlatformType() {
if (_chatApiController.customer.platform != null)
return supportedPlatformTypeList
.contains(_chatApiController.customer.platform!.type);
return false;
}
bool hasSubscriptionPlan() {
return _inboxController.getSubscriptionPlan() != null &&
_inboxController.getSubscriptionPlan() != 'free';
}
}
conflict 1 dev_branch
revamp
dev_branch
revamp
pull
origin revamp
revamp_features
git pull origin revamp_features
Manual copy paste conflicted files
to fixAuto-merging lib/controllers/apiControllers/chatApiController.dart
CONFLICT (content): Merge conflict in lib/controllers/apiControllers/chatApiController.dart
Auto-merging lib/controllers/pusherController/pusherController.dart
CONFLICT (content): Merge conflict in lib/controllers/pusherController/pusherController.dart
Auto-merging lib/controllers/viewController/chatTextingViewController.dart
CONFLICT (content): Merge conflict in lib/controllers/viewController/chatTextingViewController.dart
Auto-merging lib/models/responseModels/chatResponse.dart
CONFLICT (content): Merge conflict in lib/models/responseModels/chatResponse.dart
Auto-merging lib/models/responseModels/sendChats/data_source.dart
CONFLICT (content): Merge conflict in lib/models/responseModels/sendChats/data_source.dart
Auto-merging lib/screens/chatDetails/customWidgets/chats/linkableTextView.dart
Auto-merging lib/screens/chatDetails/customWidgets/conversationWidgets/conversationBaseWidget.dart
CONFLICT (content): Merge conflict in lib/screens/chatDetails/customWidgets/conversationWidgets/conversationBaseWidget.dart
Automatic merge failed; fix conflicts and then commit the result.
chatApiController.dart
copy paste below on revamp_features
import 'dart:io';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:dio/dio.dart';
import 'package:get/get.dart' as pref;
import 'package:get/get_core/src/get_main.dart';
import 'package:get/get_navigation/src/snackbar/snackbar.dart';
import 'package:get/get_rx/src/rx_types/rx_types.dart';
import 'package:image_picker/image_picker.dart';
import 'package:myalice/controllers/apiControllers/baseApiController.dart';
import 'package:myalice/controllers/pusherController/pusherController.dart';
import 'package:myalice/db/tableChatResolve.dart';
import 'package:myalice/models/feedModels/feed_parent.dart';
import 'package:myalice/models/jsonSchema.dart';
import 'package:myalice/models/responseModels/chatResponse.dart';
import 'package:myalice/models/responseModels/createTag/create_tags_response.dart';
import 'package:myalice/models/responseModels/createTag/data_source.dart';
import 'package:myalice/models/responseModels/imageUpload/image_upload.dart';
import 'package:myalice/models/responseModels/noteResponse/note_response.dart';
import 'package:myalice/models/responseModels/sendChats/send_data.dart';
import 'package:myalice/models/responseModels/ticketMeta/customer_meta.dart';
import 'package:myalice/screens/chatDetails/customWidgets/modals/modalController.dart';
import 'package:myalice/utils/colors.dart';
import 'package:myalice/utils/db.dart';
import 'package:myalice/utils/shared_pref.dart';
import 'package:http_parser/http_parser.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import '../../db/dbHandler.dart';
import '../../models/responseModels/tags/data_source.dart';
import '../../models/responseModels/tags/tags.dart';
import '../../models/responseModels/ticketsResponseModels/agent.dart';
import '../../models/responseModels/ticketsResponseModels/customer.dart';
import 'interceptor.dart';
class ChatApiController extends BaseApiController {
var chatResponse = <ChatDataSource?>[].obs;
var dataAvailable = false.obs;
bool get isDataAvailable => dataAvailable.value;
String conversationId = '';
int chatCustomerId = -1;
List<ChatDataSource?> get chats => chatResponse;
final SharedPref _sharedPref = SharedPref();
late String? token;
int ticketID = 0;
int customerID = 0;
String _projectID = "";
int get getTicketId => ticketID;
int get getCustomerID => customerID;
bool isDownloading = false;
double progress = 0;
bool isResolved = false;
late Customer _customer;
late List<AssignedAgents> _assignedAgent;
late List<dynamic> _assignedGroup;
late Tags _availableTags;
RxList<TagsDataSource> _usedTags = <TagsDataSource>[].obs;
late ChatModalController chatModalController;
//for tag
RxString _tag1 = "".obs;
RxString _tag2 = "".obs;
RxString _remainTagCount = "".obs;
RxString _tagMergedText = ''.obs;
RxString _tagRemainAfterMerged = ''.obs;
Customer get customer {
return _customer;
}
set customer(Customer customer) {
this._customer = customer;
}
String get tagMergedText {
return _tagMergedText.value;
}
set tagMergedText(String tag) {
this._tagMergedText.value = tag;
}
String get tagRemainAfterMerged {
return _tagRemainAfterMerged.value;
}
set tagRemainAfterMerged(String tag) {
this._tagRemainAfterMerged.value = tag;
}
String get tag1 {
return _tag1.value;
}
set tag1(String name) {
this._tag1.value = name;
}
String get tag2 {
return _tag2.value;
}
set tag2(String name) {
this._tag2.value = name;
}
String get remainTagCount {
return _remainTagCount.value;
}
set remainTagCount(String name) {
this._remainTagCount.value = name;
}
setAvailableTags(Tags availableTags) {
this._availableTags = availableTags;
}
Tags getAvailableTags() {
return this._availableTags;
}
setAssignedAgents(List<AssignedAgents> assignedAgent) {
this._assignedAgent = assignedAgent;
}
List<AssignedAgents> getAssignedAgents() {
return _assignedAgent;
}
setAssignedGroups(List<dynamic> assignedGroups) {
this._assignedGroup = assignedGroups;
}
List<dynamic> getAssignedGroups() {
return _assignedGroup;
}
setUsedTags(List<TagsDataSource> tags) {
_usedTags.value = tags;
}
RxList<TagsDataSource> getUsedTags() {
return _usedTags;
}
addItemToUsedTags(TagsDataSource data) {
_usedTags.add(data);
}
removeItemFromUsedTags(int id) {
_usedTags.removeWhere((element) => element.id as int == id);
}
Future<void> init({bool isFeed = true}) async {
token = await _sharedPref.readString("apiToken");
_projectID = await _sharedPref.readString("projectID");
getMeta(customerID: getCustomerID.toString());
isFeed ?getFeed(id: getTicketId.toString()) : getChats(id: getTicketId.toString());
chatModalController = ChatModalController();
}
void updateID(var ticketsID) {
ticketID = ticketsID;
}
void updateCustomerID(var id) {
customerID = id;
}
setTags() {
if (_usedTags != null && _usedTags.length > 0) {
if (_usedTags.length == 1) {
tag1 = _usedTags[0].name!;
remainTagCount = "";
tag2 = "";
} else if (_usedTags.length == 2) {
tag1 = _usedTags[0].name!;
tag2 = _usedTags[1].name!;
remainTagCount = "";
} else {
tag1 = _usedTags[0].name!;
tag2 = _usedTags[1].name!;
remainTagCount = '+' + (_usedTags.length - 2).toString();
}
} else {
tag1 = "";
tag2 = "";
remainTagCount = '';
}
}
int extraLetter = 5;
setActionModalTagValues(double width) {
tagRemainAfterMerged = "";
if (_usedTags != null && _usedTags.length > 0) {
tagMergedText = _usedTags[0].name!;
if (_usedTags.length == 1) return;
if (_usedTags.length == 2) {
if (tagMergedText.length + extraLetter > width) {
tagRemainAfterMerged = "+1";
return;
}
tagMergedText += ', ' + _usedTags[1].name!;
} else if (_usedTags.length == 3) {
if (tagMergedText.length + extraLetter > width) {
tagRemainAfterMerged = "+2";
return;
}
tagMergedText += ', ' + _usedTags[1].name!;
if (tagMergedText.length + extraLetter > width) {
tagRemainAfterMerged = "+1";
return;
}
tagMergedText += ', ' + _usedTags[2].name!;
} else {
if (tagMergedText.length + extraLetter > width) {
tagRemainAfterMerged = '+' + (_usedTags.length - 1).toString();
return;
}
tagMergedText += ', ' + _usedTags[1].name!;
if (tagMergedText.length + extraLetter > width) {
tagRemainAfterMerged = '+' + (_usedTags.length - 2).toString();
return;
}
tagMergedText += ', ' + _usedTags[2].name!;
tagRemainAfterMerged = '+' + (_usedTags.length - 3).toString();
}
} else {
tagMergedText = "";
tagRemainAfterMerged = '';
}
}
Future<void> updateAlertResolve(int showAlert) async {
var projectId = await _sharedPref.readString("projectID") ?? '';
TableResolveHelper().update(projectId, showAlert);
}
Future<int> getAlertResolve() async {
var projectId = await _sharedPref.readString("projectID") ?? '';
var list = await TableResolveHelper().getProject(projectId);
if (list != null && list.length > 0) {
var value = list[0];
if (value != null && value.length == 2) {
var showAlert = value[DatabaseHelper.showResolvedAlert];
return showAlert;
}
}
return 0;
}
RxBool isGetChatCalled = false.obs;
bool isEmptyChatReturn = false;
void getChatsOnScroll() {
if (!isGetChatCalled.value && !isEmptyChatReturn) {
isGetChatCalled.value = true;
getChats(
id: getTicketId.toString(),
limit: 20,
cursor: chatResponse[chatResponse.length - 1]?.sId??'');
}
}
void addIfPusherKeyNotExist(ChatDataSource chatDataSource) {
if (chatResponse.firstWhere(
(p0) => p0!.pusherKey == chatDataSource.pusherKey,
orElse: () => null) ==
null) {
addToChatList(chatDataSource);
}
}
void addToChatList(ChatDataSource chatDataSource) {
if (chatResponse.length <= 0) {
chatResponse.add(chatDataSource);
} else {
chatResponse.insert(0, chatDataSource);
}
}
late ParentFeed _feedResponse;
ParentFeed? get feeds => _feedResponse;
void getFeed(
{required String id, int limit = 100, String cursor = ''}) async {
getDio()!
.get("api/crm/tickets/$id/get-conversation",
queryParameters: cursor.isNotEmpty
? {"limit": limit, "cursor": cursor}
: {"limit": limit},
options: Options(headers: {"Authorization": "Token $token"}))
.then((value) async {
if (value.statusCode == 200) {
_feedResponse = ParentFeed.fromJson(value.data);
}
}).whenComplete(() {
dataAvailable.value = true;
});
}
void getChats(
{required String id, int limit = 100, String cursor = ''}) async {
getDio()!
.get("api/crm/tickets/$id/get-conversation",
queryParameters: cursor.isNotEmpty
? {"limit": limit, "cursor": cursor}
: {"limit": limit},
options: Options(headers: {"Authorization": "Token $token"}))
.then((value) async {
if (value.statusCode == 200) {
_response = ChatResponse.fromJson(value.data);
isEmptyChatReturn = _response.dataSource!.length <= 0;
if (cursor.isEmpty) {
chatResponse.clear();
chats.clear();
}
for (int i = 0; i < _response.dataSource!.length; i++) {
if (_response.dataSource![i].data == null) continue;
try {
_response.dataSource![i].isPusherSucceeded.value = true;
_response.dataSource![i].text =
_response.dataSource![i].source == JKChatResponse.CUSTOMER
? _response.dataSource![i].data!.text
: _response.dataSource![i].data!.text;
_response.dataSource![i].type = _response.dataSource![i].data!.type;
_response.dataSource![i].subType =
_response.dataSource![i].source == JKChatResponse.ADMIN ||
_response.dataSource![i].source == JKChatResponse.BOT ||
_response.dataSource![i].source == "echo"
? (_response.dataSource![i].data!.type ==
JKChatResponse.ATTACHMENT
? _response.dataSource![i].data!.subType
: "")
: (_response.dataSource![i].data!.type ==
JKChatResponse.ATTACHMENT
? _response.dataSource![i].data!.subType
: "");
chatResponse.add(_response.dataSource![i]);
} catch (e) {
print(e);
}
}
}
}).whenComplete(() {
isGetChatCalled.value = false;
dataAvailable.value = true;
chatResponse.refresh();
});
}
bool checkTextingPermission(String action) {
try {
if (_response.actions != null) {
List<Actions> actions = _response.actions!
.where((element) => element.action == action && element.isAllowed!)
.toList();
if (actions.length > 0) {
return true;
} else {
Get.snackbar(
"Failed", "You don't have enough permission to send message",
snackPosition: SnackPosition.BOTTOM,
backgroundColor: AliceColors.ALICE_RED_500,
colorText: AliceColors.ALICE_WHITE);
return false;
}
}
return true;
} catch (e) {
print('Error In Texting Permission Checking: ' + e.toString());
return true;
}
}
Future<SendData> sendChats(String id, String text, String image) async {
if (!checkTextingPermission("direct_message")) {
var sendData = SendData();
sendData.success = false;
return sendData;
}
return getDio()!
.post("api/crm/tickets/$id/messenger-chat",
data: {
JKChatResponse.TEXT: text,
JKChatResponse.IMAGE: image,
JKChatResponse.ACTION: "direct_message"
},
options: Options(headers: {"Authorization": "Token $token"}))
.then((value) {
if (value.statusCode == 200) {
print("[---ok---] sendChats:");
if (value.data[JKChatResponse.SUCCESS]) {
return SendData.fromJson(value.data);
}
} else {
var tmpValue = value.statusCode;
print("[---ok---] sendChats: $tmpValue");
}
return SendData.fromJson(value.data[JKChatResponse.SUCCESS]);
});
}
late ChatResponse _response;
Future<SendData> sendChatsAudio(String id, String text, String audio) async {
if (!checkTextingPermission("direct_message")) {
var sendData = SendData();
sendData.success = false;
return sendData;
}
return getDio()!
.post("api/crm/tickets/$id/messenger-chat",
data: {
JKChatResponse.TEXT: text,
JKChatResponse.AUDIO: audio,
JKChatResponse.ACTION: "direct_message"
},
options: Options(headers: {"Authorization": "Token $token"}))
.then((value) {
if (value.statusCode == 200) {
if (value.data[JKChatResponse.SUCCESS]) {
return SendData.fromJson(value.data);
}
}
return SendData.fromJson(value.data[JKChatResponse.SUCCESS]);
});
}
Future<SendData> sendTemplateChats(
String id, String text, String template) async {
if (!checkTextingPermission("send_template")) {
var sendData = SendData();
sendData.success = false;
return sendData;
}
return getDio()!
.post("api/crm/tickets/$id/messenger-chat",
data: {
JKChatResponse.TEXT: text,
JKChatResponse.TEMPLATE: template,
JKChatResponse.ACTION: "send_template"
},
options: Options(headers: {"Authorization": "Token $token"}))
.then((value) {
if (value.statusCode == 200) {
if (value.data[JKChatResponse.SUCCESS]) {
return SendData.fromJson(value.data);
}
}
return SendData.fromJson(value.data[JKChatResponse.SUCCESS]);
});
}
Future<void> closeDB() async {
//await _chatDataBase.dbClose();
}
Future<ImageUpload> uploadPhoto(File imageFile) async {
String fileName = imageFile.path.split('/').last;
String fileExt = imageFile.path.split('.').last;
FormData formData = FormData.fromMap({
JKChatResponse.FILE: await MultipartFile.fromFile(imageFile.path,
filename: fileName,
contentType: MediaType(JKChatResponse.IMAGE, fileExt)),
});
/*
var formData = FormData();
for (var file in imageFile) {
String fileName = file.path.split('/').last;
String fileExt = file.path.split('.').last;
formData.files.addAll([
MapEntry("file", await MultipartFile.fromFile(file.path,
filename: fileName, contentType: MediaType("image", fileExt))),
]);
}
*/
Response response = await getDio()!.post(
"api/crm/projects/$_projectID/images",
options: Options(headers: {
'Authorization': 'Token $token',
}),
data: formData,
);
if (response.statusCode == 200)
return ImageUpload.fromJson(response.data);
else {
return ImageUpload.fromJson(response.data);
}
}
Future<Response> uploadAudio(String audioFilePath) async {
String fileName = audioFilePath.split('/').last;
String fileExt = audioFilePath.split('.').last;
FormData formData = FormData.fromMap({
JKChatResponse.FILE: await MultipartFile.fromFile(audioFilePath,
filename: fileName,
contentType: MediaType(JKChatResponse.AUDIO, fileExt)),
});
Response response = await getDio()!.post(
'stable/bots/upload/audio',
options: Options(headers: {
'Authorization': 'None',
}),
data: formData,
);
return response;
}
/* Future<dynamic> uploadImage(XFile file) async {
String fileName = file.path.split('/').last;
print("FileName::$fileName");
print("FilePath::${file.path}");
Uint8List uint8list = await file.readAsBytes();
var image =
XFile.fromData(uint8list, mimeType: file.mimeType, name: fileName);
FormData formData = FormData.fromMap({
"file": image,
});
print(formData.files);
var response = await getDio()!.post("crm/projects/81/images",
data: formData,
options: Options(
contentType: "multipart/form-data",
headers: {"Authorization": "Token $token"}));
return response.data;
}
*/
Future<bool> _requestPermission(Permission permission) async {
if (await permission.isGranted) {
return true;
} else {
var result = await permission.request();
if (result == PermissionStatus.granted) {
return true;
} else {
return false;
}
}
}
Future<bool?> download(String url) async {
url =
"https://s3-ap-southeast-1.amazonaws.com/stage-alice-v3/misc/5e3674725c0d11ecb8520242c0a83006.png";
final File _file = new File(url);
final _filename = basename(_file.path).split('?')[0];
final _extenion = extension(_filename);
late Directory tempDir;
try {
if (Platform.isAndroid) {
if (await _requestPermission(Permission.storage) &&
await _requestPermission(Permission.accessMediaLocation) &&
await _requestPermission(Permission.manageExternalStorage)) {
tempDir = (await getExternalStorageDirectory())!;
String newPath = "";
List<String> folders = tempDir.path.split("/");
for (int i = 1; i < folders.length; i++) {
String folder = folders[i];
if (folder != "Android") {
newPath += "/" + folder;
} else {
break;
}
}
newPath = newPath + "/MyAlice";
tempDir = Directory(newPath);
} else {
return false;
}
} else {
if (await _requestPermission(Permission.photos)) {
tempDir = await getTemporaryDirectory();
} else {
return false;
}
}
if (!await tempDir.exists()) {
await tempDir.create(recursive: true);
}
if (await tempDir.exists()) {
File saveFile = File(tempDir.path + "/" + _filename);
var l = await getDio()!.download(url, saveFile.path,
onReceiveProgress: (downloaded, totalSize) {
progress = downloaded / totalSize;
});
var result = l.data;
}
} catch (e) {
print(e);
}
/*= await getExternalStorageDirectory();
Directory? tempDir1 = await getDownloadsDirectory();
String tempPath = tempDir1!.path;
final File _file = new File(url);
final _filename = basename(_file.path).split('?')[0];
final _nameWithoutExtension = basenameWithoutExtension(_filename);
final _extenion = extension(_filename);
Directory appDocDir = await getApplicationDocumentsDirectory();
String appDocPath = appDocDir.path;
var l = await getDio()!.download(url, tempPath + _filename);
var data = l.data;*/
}
Future<bool?> addTicketTags(String action, String name, int tagId) async {
return getDio()!
.post("api/crm/tickets/$ticketID/tag",
data: {
JKChatResponse.ACTION: action,
"name": name,
JKChatResponse.ID: tagId
},
options: Options(headers: {"Authorization": "Token $token"}))
.then((response) => response.statusCode == 200
? response.data[JKChatResponse.SUCCESS]
: null);
}
Future<int?> addTagtoTicket(String action, String name, int tagId) async {
return getDio()!
.post("api/crm/tickets/$ticketID/tag",
data: {
JKChatResponse.ACTION: action,
"name": name,
JKChatResponse.ID: tagId
},
options: Options(headers: {"Authorization": "Token $token"}))
.then((response) => response.statusCode == 200 ? tagId : -1);
}
Future<bool?> removeTicketTags(
{required String action,
required String name,
required int tagId}) async {
return getDio()!
.post("api/crm/tickets/$ticketID/tag",
data: {
JKChatResponse.ACTION: action,
"name": name,
JKChatResponse.ID: tagId
},
options: Options(headers: {"Authorization": "Token $token"}))
.then((response) => response.statusCode == 200
? response.data[JKChatResponse.SUCCESS]
: null);
}
Future<int?> addNewTicketTags(String name) async {
String? nameTag = "";
int? id = -1;
int? returnid = -1;
Response response1 = await getDio()!.post(
"api/crm/projects/$_projectID/ticket-tags",
data: {"name": name},
options: Options(headers: {"Authorization": "Token $token"}));
//*return getDio()!
/*.post("crm/projects/$_projectID/ticket-tags",
data: {"name": name},
options: Options(headers: {"Authorization": "Token $token"}))
.then((value) async {*/
if (response1.statusCode == 200) {
CreateTagsResponse response = CreateTagsResponse.fromJson(response1.data);
if (response.dataSource != null) {
nameTag = response.dataSource!.name;
id = response.dataSource!.id;
if (id! > 0) {
returnid = await addTagtoTicket("add", nameTag!, id);
return returnid!;
}
return -1;
}
} else
return -1;
}
Future<bool?> reassign(String agentID, String groupID) async {
return getDio()!
.post("api/crm/tickets/$ticketID/action-assign",
data: {"agent_id": agentID, "group_id": groupID},
options: Options(headers: {"Authorization": "Token $token"}))
.then((response) => response.statusCode == 200
? response.data[JKChatResponse.SUCCESS]
: null);
}
Future<NoteResponse> saveNote(String note) async {
return getDio()!
.post("api/crm/tickets/$ticketID/messenger-chat",
data: {
JKChatResponse.TEXT: note,
JKChatResponse.IMAGE: null,
JKChatResponse.ACTION: "write_note"
},
options: Options(headers: {"Authorization": "Token $token"}))
.then((response) => response.statusCode == 200
? NoteResponse.fromJson(response.data)
: NoteResponse.fromJson(response.data));
}
Future<bool?> deleteCannedResponse(String responseID) async {
return getDio()!
.delete("api/crm/projects/$ticketID/canned-responses/$responseID",
options: Options(headers: {"Authorization": "Token $token"}))
.then((response) => response.statusCode == 200
? response.data[JKChatResponse.SUCCESS]
: null);
}
Future<bool?> editCannedResponse(
String responseID, String title, String body, bool team) async {
return getDio()!
.patch("api/crm/projects/$ticketID/canned-responses/$responseID",
data: {
JKChatResponse.TITLE: title,
JKChatResponse.TEXT: body,
"for_team": team
},
options: Options(headers: {"Authorization": "Token $token"}))
.then((response) => response.statusCode == 200
? response.data[JKChatResponse.SUCCESS]
: null);
}
Future<dynamic?> addCannedResponse(
String title, String body, bool team) async {
return getDio()!
.post("api/crm/projects/$_projectID/canned-responses",
data: {
JKChatResponse.TITLE: title,
JKChatResponse.TEXT: body,
"for_team": team,
"teamId": _projectID
},
options: Options(headers: {"Authorization": "Token $token"}))
.then((response) => response.statusCode == 200 ? response.data : null);
}
Future<bool?> resolveTicket() async {
return getDio()!
.post("api/crm/tickets/$ticketID/action-resolve",
data: {"status": true},
options: Options(headers: {"Authorization": "Token $token"}))
.then((response) => response.statusCode == 200
? response.data[JKChatResponse.SUCCESS]
: null);
}
Future<bool?> reopenTicket(int? groupId, int? agentId, String? note) async {
return getDio()!
.post("api/crm/tickets/$ticketID/action-reopen",
data: {
"note": note,
"agent_id": agentId,
"group_id": groupId,
},
options: Options(headers: {"Authorization": "Token $token"}))
.then((response) =>
response.statusCode == 200 ? response.data["success"] : null);
}
Future<bool?> actionBot(bool status) async {
return getDio()!
.post("api/crm/tickets/$ticketID/action-bot",
data: {"status": status},
options: Options(headers: {"Authorization": "Token $token"}))
.then((response) => response.statusCode == 200
? response.data[JKChatResponse.SUCCESS]
: null);
}
Future<CustomerMeta?> getMeta({String? customerID}) async {
return getDio()!
.get("api/bots/customers/$customerID/meta",
options: Options(headers: {"Authorization": "Token $token"}))
.then((response) {
if (response.statusCode == 200) {
var meta = CustomerMeta.fromJson(response.data);
SharedPref().saveBool("bot${meta.data!.id}", meta.data!.botEnabled!);
}
});
}
}
pusherController.dart
copy paste below on revamp_features
import 'dart:async';
import 'dart:convert';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:myalice/controllers/apiControllers/baseApiController.dart';
import 'package:myalice/controllers/apiControllers/chatApiController.dart';
import 'package:myalice/models/responseModels/chatResponse.dart';
import 'package:pusher_client/pusher_client.dart';
class PusherService extends ChatApiController {
PusherEvent? lastEvent;
String? lastConnectionState;
Channel? channel;
PusherClient? pusher;
String PUSHER_KEY_STAGE = '199beaccd57c0306a7e7';
String PUSHER_KEY_LIVE = '27fffd287b600557782a';
static final PusherService _instance = PusherService._internal();
factory PusherService() => _instance;
PusherService._internal() {
String key =
BaseApiController.MODE_DEVELOPMENT ? PUSHER_KEY_STAGE : PUSHER_KEY_LIVE;
var options = PusherOptions(cluster: "ap1");
pusher =
PusherClient(key, options, enableLogging: true, autoConnect: false);
}
Future<void> connectPusher() async {
try {
await pusher!.connect();
pusher!.onConnectionStateChange((state) {
print(state!.currentState);
});
} catch (e) {
print(e);
}
}
void unsubscribeMessageChannel(String channelName) {
if (channel != null) channel!.cancelEventChannelStream();
if (pusher != null) pusher!.unsubscribe(channelName);
}
Future<void> subscribeMessageChannel(String channelName, String eventName,
String converstionId, String customerId) async {
try {
await connectPusher();
channel = pusher!.subscribe(channelName);
channel!.bind(eventName, (event) async {
Map<String, dynamic> text = jsonDecode(event!.data!);
var v = Get.find<ChatApiController>().customerID;
ChatDataSource? dataSource = Get.find<ChatApiController>()
.chatResponse
.firstWhere((p0) => (text['pusher_key'] != null && p0!.pusherKey == text['pusher_key']),
orElse: () => null);
if (dataSource != null) {
dataSource.report = text['report'] != null
? new Report.fromJson(text['report'])
: null;
dataSource.data = text['dataV2'] != null &&
text['dataV2'] is Map &&
text['dataV2'].length > 0
? new Data.fromJson(text['dataV2'])
: text['data'] != null
? new Data.fromJsonData(text['data'])
: null;
if ((text['dataV2'] == null ||
(text['dataV2'] is Map && text['dataV2'].length <= 0)) &&
text['data'] != null) {
dataSource.data!.success =
dataSource.report == null && dataSource.report!.error == null;
}
dataSource.isPusherSucceeded.value = true;
if (dataSource.type == 'template') {
Get.find<ChatApiController>().chatResponse.removeWhere(
(element) => element!.pusherKey == text['pusher_key']);
dataSource = null;
}
}
bool doAdd = text['source'] == "bot"
? true
: text['customer_id'] == Get.find<ChatApiController>().customerID
? dataSource == null
: false;
if (text['source'] == "customer") {
try {
if (Get.find<ChatApiController>().chatResponse.firstWhere(
(p0) => p0!.sId == text['_id'],
orElse: () => null) ==
null) {
var chatDataSource = ChatDataSource(
sId: text['_id'],
text: text['dataV2']['text'],
source: text['source'],
timestamp: text['timestamp'],
type: text['dataV2']['type'],
subType: text['dataV2']['sub_type'],
platformId: text['platform_id'],
platformType: text['platform_type'],
customerId: text['customer_id'],
report: text['report'] != null
? Report.fromJson(text['report'])
: null,
adminId: text['admin_id'],
adminInfo: text['admin_info'] != null
? AdminInfo.fromJson(text['admin_info'])
: null,
conversationId: text['conversation_id'],
data: text['dataV2'] != null &&
text['dataV2'] is Map &&
text['dataV2'].length > 0
? new Data.fromJson(text['dataV2'])
: text['data'] != null
? new Data.fromJsonData(text['data'])
: null);
Get.find<ChatApiController>().addToChatList(chatDataSource);
}
} catch (e) {
print(e);
}
} else {
if (doAdd) {
var chatDataSource = ChatDataSource(
sId: text['_id'],
text: text['data']['data']['text'],
source: text['source'],
timestamp: text['timestamp'],
type: text['data']['type'],
subType: text['data']['type'] == "attachment"
? text['data']['data']["sub_type"]
: "",
pusherKey: text['pusher_key'],
platformId: text['platform_id'],
platformType: text['platform_type'],
customerId: text['customer_id'],
report: text['report'] != null
? Report.fromJson(text['report'])
: null,
adminId: text['admin_id'],
adminInfo: text['admin_info'] != null
? AdminInfo.fromJson(text['admin_info'])
: null,
conversationId: text['conversation_id'],
data: text['dataV2'] != null &&
text['dataV2'] is Map &&
text['dataV2'].length > 0
? new Data.fromJson(text['dataV2'])
: text['data'] != null
? new Data.fromJsonData(text['data'])
: null);
if ((text['dataV2'] == null ||
(text['dataV2'] is Map && text['dataV2'].length <= 0)) &&
text['data'] != null) {
chatDataSource.data!.success = chatDataSource.report == null ||
chatDataSource.report!.error == null;
}
chatDataSource.isPusherSucceeded.value = true;
Get.find<ChatApiController>().addToChatList(chatDataSource);
}
}
});
} on PlatformException catch (e) {
print(e.message);
}
}
Future<Channel?> subscribeTicketsPusher(String channelName) async {
try {
await connectPusher();
return pusher!.subscribe(channelName);
} catch (e) {
print(e);
return null;
}
}
Future<void> unSubscribeTicketPusher(String channelName) async {
try {
await pusher!.unsubscribe(channelName);
} catch (e) {
print(e);
return null;
}
}
/* Future<void> connectPusher(
String channelName, String eventName) async {
try {
var options = PusherOptions(cluster: "ap1");
pusher = PusherClient('199beaccd57c0306a7e7', options,
enableLogging: true, autoConnect: false);
pusher!.connect();
pusher!.onConnectionStateChange((state) {
print(state!.currentState);
});
channel = pusher!.subscribe(channelName);
channel!.bind(eventName, (event) async {
Map<String, dynamic> text = jsonDecode(event!.data!);
if (text['source'] == "customer") {
ChatApiController _controller = Get.put(ChatApiController());
_controller.chatResponse.add(ChatDataSource(
text: text['data']['text'],
source: text['source'],
type: text['data']['type'],
subType: text['data']['type'] == "attachment"
? text['data']["attachment"]["type"]
: "",
imageUrl: text['data']['type'] == "attachment"
? text['data']["attachment"]["type"] == "image"
? text['data']["attachment"]["urls"][0]
: ""
: ""));
}
});
} on PlatformException catch (e) {
print(e.message);
}
} */
/* pusherTrigger(String eventName) {
channel!.bind("test-channel", (PusherEvent event) {
channel!.trigger(eventName, {"name": "Bob"});
log(event.data);
_inEventData.add(event.data);
});
} */
}
/* void unSubscribePusher(String channelName) {
Pusher.unsubscribe(channelName);
} */
chatTextingViewController.dart
copy paste below on revamp_features
import 'dart:convert';
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:get/get.dart';
import 'package:get/get_state_manager/src/simple/get_controllers.dart';
import 'package:myalice/controllers/apiControllers/inboxController.dart';
import 'package:myalice/models/jsonSchema.dart';
import 'package:myalice/models/responseModels/platform/whatsAppMsgTemplate.dart';
import 'package:myalice/models/responseModels/ticketsResponseModels/platform.dart';
import '../../models/responseModels/cannedResponse/data_source.dart';
import '../../models/responseModels/chatResponse.dart';
import '../apiControllers/chatApiController.dart';
class ChatTextingController extends GetxController {
String lastMessage = '';
int CANNED_RESPONSE_VISIBLE = 1;
int IMAGE_LIST_VISIBLE = 2;
int ONLY_TEXT_FIELD_VISIBLE = 3;
double HEIGHT_INITIAL = 0.26;
double HEIGHT_SHOW_ONE_LIST = 0.40;
double HEIGHT_SHOW_TWO_LIST = 0.60;
RxString _chatText = ''.obs;
RxString _noteText = ''.obs;
String get noteText => _noteText.value;
WhatsAppMsgTemplate? _template;
WhatsAppMsgTemplate? get template => _template;
set template(WhatsAppMsgTemplate? template) {
_template = template;
}
set noteText(String value) {
_noteText.value = value;
}
RxBool isSending = false.obs;
RxBool isShowChatTextingView = false.obs;
RxBool isShowNoteTextingView = false.obs;
RxBool isShowTemplateTextingView = false.obs;
RxBool showEmoji = false.obs;
String chatHintText = 'Write your reply here';
String chatNoteHintText = 'Write your note here';
List<CannedDataSource>? list = [];
RxInt _viewVisibility = 0.obs;
RxDouble _viewInitialHeight = 0.26.obs;
late ChatApiController _chatApiController;
late InboxController _inboxController;
RxString audioFilePath = ''.obs;
final controller = DraggableScrollableController();
RxList<File> imageList = <File>[].obs;
late BuildContext draggableSheetContext;
double get viewInitialHeight => _viewInitialHeight.value;
init() {
try {
_chatApiController = Get.find<ChatApiController>();
_inboxController = Get.find<InboxController>();
} catch (e) {}
}
set viewInitialHeight(double value) {
_viewInitialHeight.value = value;
}
int get viewVisibility => _viewVisibility.value;
set viewVisibility(int value) {
_viewVisibility.value = value;
}
set chatText(String text) {
this._chatText.value = text;
}
String get chatText {
return this._chatText.value;
}
int numLines = 0;
int calculateNoOfLines() {
numLines = '\n'.allMatches(chatText).length + 1;
return numLines;
}
setHeightIfImageListExist() {
if (imageList.length > 0 && viewInitialHeight < 0.36) {
viewInitialHeight = 0.36;
} else if (imageList.length == 0 && viewInitialHeight == 0.36) {
viewInitialHeight = 0.26;
}
}
int SHOW_NOTE_TESTING = 1;
int SHOW_CHAT_TESTING = 2;
int SHOW_TEMPLATE_TESTING = 3;
int HIDE_NOTE_CHAT = 0;
Platform? getPlatform() {
return _chatApiController.customer.platform;
}
toggleTextingView(int setView) {
if (setView == SHOW_CHAT_TESTING) {
isShowChatTextingView.value = true;
isShowNoteTextingView.value = false;
isShowTemplateTextingView.value = false;
} else if (setView == SHOW_NOTE_TESTING) {
isShowChatTextingView.value = false;
isShowNoteTextingView.value = true;
isShowTemplateTextingView.value = false;
} else if (setView == SHOW_TEMPLATE_TESTING) {
isShowChatTextingView.value = false;
isShowNoteTextingView.value = false;
isShowTemplateTextingView.value = true;
}
}
sendChat() {
sendText();
if (audioFilePath.isNotEmpty) {
sendChatWithAudio();
}
if (imageList.isNotEmpty) {
List<File> tempImageList = imageList.value;
imageList.value = [];
sendMultiplePhoto(tempImageList);
}
}
sendConvChat() {
sendConvText();
if (audioFilePath.isNotEmpty) {
sendChatWithAudio();
}
if (imageList.isNotEmpty) {
List<File> tempImageList = imageList.value;
imageList.value = [];
sendMultiplePhoto(tempImageList);
}
}
/*
action: "direct_message"
audio: null
image: null
template: "1708232642911371"
text: "A template named full_template_1 will be sent."
*/
Future<bool> sendText() async {
String text = chatText.trim();
chatText = '';
if (text.isNotEmpty) {
await _chatApiController
.sendChats(_chatApiController.ticketID.toString(), text, "")
.then((value) {
if (value
.success! /* && widget.conversationId == value.dataSource!.conversationId*/) {
addToChatResponse(value.dataSource!);
}
});
chatText = "";
return true;
}
return false;
}
Future<bool> sendConvText() async {
String text = chatText.trim();
chatText = '';
if (text.isNotEmpty) {
lastMessage = text;
await _chatApiController
.sendChats(_chatApiController.ticketID.toString(), text, "")
.then((value) {
if (value
.success! /* && widget.conversationId == value.dataSource!.conversationId*/) {
value.dataSource?.id = _chatApiController.ticketID.toString();
print('[---ok---] sendConvText(): 1 ${value.toJson()}');
addToChatResponse(value.dataSource!);
}
});
chatText = "";
return true;
}
return false;
}
Future<bool> sendTemplate() async {
String strText = 'Template ${template!.name} was sent';
if (template != null && template!.id.isNotEmpty) {
await _chatApiController
.sendTemplateChats(
_chatApiController.ticketID.toString(), strText, template!.id)
.then((value) {
if (value
.success! /* && widget.conversationId == value.dataSource!.conversationId*/) {
addToChatResponse(value.dataSource!);
}
});
chatText = "";
return true;
}
return false;
}
int? getChatPlatformId() {
if (_chatApiController.customer.platform != null) {
return _chatApiController.customer.platform!.id;
}
return null;
}
addToChatResponse(dynamic datasource) {
ChatDataSource response = ChatDataSource.fromJson(datasource.toJson());
print('ChatDataSource123:'+response.toJson().toString());
response.isPusherSucceeded.value = false;
try {
response.text = (response.source == JKChatResponse.CUSTOMER)
? response.data!.text
: response.data!.text;
response.type = response.data!.type;
response.subType = response.source == JKChatResponse.ADMIN || response.source == JKChatResponse.BOT
? (response.data!.type == JKChatResponse.ATTACHMENT
? response.data!.subType
: "")
: (response.data!.type == JKChatResponse.ATTACHMENT
? response.data!.type
: "");
} catch (e) {
print(e);
}
_chatApiController.addIfPusherKeyNotExist(response);
}
sendMultiplePhoto(List<File> list) async {
for (int i = 0; i < list.length; i++) {
sendChatWithImage(list[i]);
}
}
sendChatWithImage(File file) {
_chatApiController.uploadPhoto(file).then((value) {
File(file.path).delete();
if (value.success!) {
String convId = '' + _chatApiController.conversationId.toString();
Get.find<ChatApiController>()
.sendChats(_chatApiController.ticketID.toString(), "",
value.dataSource!.s3Url!)
.then((value) {
if (value.success! &&
convId == Get.find<ChatApiController>().conversationId) {
addToChatResponse(value.dataSource!);
}
});
}
});
}
sendChatWithAudio() {
String filePath = audioFilePath.value;
audioFilePath.value = '';
_chatApiController.uploadAudio(filePath).then((value) {
File(filePath).delete();
audioFilePath.value = '';
if (value.statusCode == 200) {
String convId = '' + _chatApiController.conversationId.toString();
String url = value.data['url'];
Get.find<ChatApiController>()
.sendChatsAudio(
_chatApiController.ticketID.toString(), "", value.data['url'])
.then((value) {
if (value.success! &&
convId == Get.find<ChatApiController>().conversationId) {
addToChatResponse(value.dataSource!);
}
});
}
});
}
sendNote() {
String convId = '' + _chatApiController.conversationId;
String note = noteText.trim();
noteText = '';
if (note.isNotEmpty)
_chatApiController.saveNote(note).then((value) {
if (value.success! && convId == _chatApiController.conversationId) {
addToChatResponse(value.dataSource);
}
});
noteText = '';
}
List<String> supportedPlatformTypeList = [
"facebook_messenger",
"whatsapp_bsp",
"webchat",
"livechat_messenger",
"app_messenger"
];
bool isSupportedPlatformType() {
if (_chatApiController.customer.platform != null)
return supportedPlatformTypeList
.contains(_chatApiController.customer.platform!.type);
return false;
}
bool hasSubscriptionPlan() {
return _inboxController.getSubscriptionPlan() != null &&
_inboxController.getSubscriptionPlan() != 'free';
}
}
chatResponse.dart
copy paste below on revamp_features
import 'package:get/get_rx/src/rx_types/rx_types.dart';
import 'package:myalice/models/jsonSchema.dart';
class ChatResponse {
bool? success;
List<ChatDataSource>? dataSource;
List<Actions>? actions;
Data? data;
ChatResponse({this.success, this.dataSource, this.actions});
ChatResponse.fromJson(Map<String, dynamic> json) {
success = json[JKChatResponse.SUCCESS];
// data = json['data'] != null ? new Data.fromJson(json['data']) : null;
if (json[JKChatResponse.DATA_SOURCE] != null) {
dataSource = <ChatDataSource>[];
json[JKChatResponse.DATA_SOURCE].forEach((v) {
dataSource!.add(new ChatDataSource.fromJson(v));
});
}
if (json[JKChatResponse.ACTIONS] != null) {
actions = <Actions>[];
json[JKChatResponse.ACTIONS].forEach((v) {
actions!.add(new Actions.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data[JKChatResponse.SUCCESS] = this.success;
if (this.dataSource != null) {
data[JKChatResponse.DATA_SOURCE] =
this.dataSource!.map((v) => v.toJson()).toList();
}
if (this.actions != null) {
data[JKChatResponse.ACTIONS] =
this.actions!.map((v) => v.toJson()).toList();
}
return data;
}
}
class ChatDataSource {
String? sId;
String? source;
String? type;
String? event;
String? platformType;
//int? conversationId;
int? timestamp;
int? platformId;
int? projectId;
int? customerId;
bool? isSeen;
bool? isDelivered;
bool? success;
Data? data;
Report? report;
int? adminId;
AdminInfo? adminInfo;
String? pusherKey;
RxBool isPusherSucceeded = false.obs;
//For DB
String? text;
String? imageUrl;
String? subType;
dynamic? conversationId;
ChatDataSource({
this.sId,
this.text,
this.source,
this.timestamp,
this.type,
this.subType,
this.platformId,
this.platformType,
this.customerId,
this.report,
this.adminId,
this.adminInfo,
this.conversationId,
this.imageUrl,
this.data,
this.event,
this.projectId,
this.isSeen,
this.isDelivered,
this.success,
this.pusherKey,
});
ChatDataSource.fromJson(Map<String, dynamic> json) {
sId = json[JKChatResponse.ID];
source = json[JKChatResponse.SOURCE];
type = json[JKChatResponse.TYPE];
platformType = json[JKChatResponse.PLATFORM_TYPE];
event = json[JKChatResponse.EVENT];
conversationId = json[JKChatResponse.CONVERSATION_ID];
timestamp = json[JKChatResponse.TIMESTAMP];
platformId = json[JKChatResponse.PLATFORM_ID];
projectId = json[JKChatResponse.PROJECT_ID];
customerId = json[JKChatResponse.CUSTOMER_ID];
isSeen = json[JKChatResponse.IS_SEEN];
isDelivered = json[JKChatResponse.IS_DELIVERED];
data = json[JKChatResponse.DATA] != null
? new Data.fromJson(json[JKChatResponse.DATA])
: null;
success = json[JKChatResponse.SUCCESS];
report = json[JKChatResponse.REPORT] != null
? new Report.fromJson(json[JKChatResponse.REPORT])
: null;
adminId = json[JKChatResponse.ADMIN_ID];
adminInfo = json[JKChatResponse.ADMIN_INFO] != null
? new AdminInfo.fromJson(json[JKChatResponse.ADMIN_INFO])
: null;
this.text = json[JKChatResponse.TEXT];
this.subType = json[JKChatResponse.SUB_TYPE];
this.pusherKey = json[JKChatResponse.PUSHER_KEY];
// this.imageUrls = json[JKChatResponse.DATA].json[JKChatResponse.URLS];
// this.videoUrls = json[JKChatResponse.DATA].json[JKChatResponse.URLS];
this.text = json[JKChatResponse.TEXT];
this.imageUrl = json['image_url'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data[JKChatResponse.ID] = this.sId;
data[JKChatResponse.SOURCE] = this.source;
data[JKChatResponse.TYPE] = this.type;
data[JKChatResponse.PLATFORM_TYPE] = this.platformType;
data[JKChatResponse.EVENT] = this.event;
data[JKChatResponse.CONVERSATION_ID] = this.conversationId;
data[JKChatResponse.TIMESTAMP] = this.timestamp;
data[JKChatResponse.PLATFORM_ID] = this.platformId;
data[JKChatResponse.PROJECT_ID] = this.projectId;
data[JKChatResponse.CUSTOMER_ID] = this.customerId;
data[JKChatResponse.IS_SEEN] = this.isSeen;
data[JKChatResponse.IS_DELIVERED] = this.isDelivered;
if (this.data != null) {
data[JKChatResponse.DATA] = this.data!.toJson();
}
data[JKChatResponse.SUCCESS] = this.success;
if (this.report != null) {
data[JKChatResponse.REPORT] = this.report!.toJson();
}
data[JKChatResponse.ADMIN_ID] = this.adminId;
data[JKChatResponse.PUSHER_KEY] = this.pusherKey;
if (this.adminInfo != null) {
data[JKChatResponse.ADMIN_INFO] = this.adminInfo!.toJson();
}
return data;
}
Map<String, dynamic> toJsonForDB() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data[JKChatResponse.TEXT] = this.source == JKChatResponse.CUSTOMER
? this.data!.text
: this.data!.text;
data[JKChatResponse.URLS] = this.source == JKChatResponse.CUSTOMER
? this.data!.type == JKChatResponse.ATTACHMENT
? this.data!.subType == JKChatResponse.IMAGE
? this.data!.urls!.elementAt(0)
: ""
: ""
: this.data!.type == JKChatResponse.ATTACHMENT
? this.data!.subType == JKChatResponse.IMAGE
? this.data!.urls!.elementAt(0)
: ""
: "";
data[JKChatResponse.URLS] = this.source == JKChatResponse.CUSTOMER
? this.data!.type == JKChatResponse.ATTACHMENT
? this.data!.subType == JKChatResponse.IMAGE
? this.data!.urls!
: []
: []
: this.data!.type == JKChatResponse.ATTACHMENT
? this.data!.subType == JKChatResponse.IMAGE
? this.data!.urls!
: []
: [];
data[JKChatResponse.URLS] = this.source == JKChatResponse.CUSTOMER
? this.data!.type == JKChatResponse.ATTACHMENT
? this.data!.subType == JKChatResponse.VIDEO
? this.data!.urls!
: []
: []
: this.data!.type == JKChatResponse.ATTACHMENT
? this.data!.subType == JKChatResponse.VIDEO
? this.data!.urls!
: []
: [];
data[JKChatResponse.URLS] = this.source == JKChatResponse.CUSTOMER
? this.data!.type == JKChatResponse.ATTACHMENT
? this.data!.subType == JKChatResponse.FILE
? this.data!.urls!
: []
: []
: this.data!.type == JKChatResponse.ATTACHMENT
? this.data!.subType == JKChatResponse.FILE
? this.data!.urls!
: []
: [];
data[JKChatResponse.SOURCE] = this.source;
data[JKChatResponse.TYPE] = this.data!.type;
data[JKChatResponse.SUB_TYPE] = this.source == JKChatResponse.ADMIN ||
this.source == JKChatResponse.BOT
? (this.data!.type == JKChatResponse.ATTACHMENT
? this.data!.subType
: "")
: (this.data!.type == JKChatResponse.ATTACHMENT ? this.data!.type : "");
data[JKChatResponse.TIMESTAMP] = this.timestamp;
return data;
}
@override
String toString() {
return 'Chats {text : ${this.source == JKChatResponse.CUSTOMER ? this.data!.text : this.data?.text}}';
}
}
class Data {
String? type;
bool? success;
String? error;
String? text;
String? title;
String? payload;
String? subType;
List<String>? urls;
List<Elements>? elements;
// List<Buttons>? buttons;
List<ElementButtons>? buttons;
List<ElementButtons>? button;
Datam? data;
Data(
{this.type,
this.success,
this.error,
this.text,
this.title,
this.payload,
this.subType,
this.urls,
this.elements,
this.buttons,
this.button});
Data.fromJsonData(Map<String, dynamic> json) {
print('[---ok---] Data.fromJsonButton');
type = json['type'];
text = ((json['text'] ?? '') is String) ? json['text'] : '';
title = json['title'];
payload = json['payload'];
//extra = json['extra'];
data = json['data'] != null ? new Datam.fromJson(json['data']) : null;
if (data != null) {
text = data!.text;
title = json['type'] == 'quick_reply' ? data!.text : json['title'];
subType = data!.subType;
urls = data!.urls;
elements = data!.elements;
buttons = data!.buttons;
button = json['type'] == 'button' ? data!.buttons : data!.button;
success = true;
}
// attachment = json["attachment"] != null
// ? Attachment.fromJson(json["attachment"])
// : Attachment();
}
Data.fromJson(Map<String, dynamic> json) {
print('[--ok---] Data.fromJson');
type = json[JKChatResponse.TYPE];
success = json[JKChatResponse.SUCCESS] ?? false;
error = json[JKChatResponse.ERROR];
text = json[JKChatResponse.TEXT];
title = json[JKChatResponse.TITLE];
payload = json[JKChatResponse.PAYLOAD];
subType = json[JKChatResponse.SUB_TYPE];
if (json[JKChatResponse.URLS] != null) {
urls = json[JKChatResponse.URLS].cast<String>();
}
if (json[JKChatResponse.ELEMENTS] != null) {
elements = <Elements>[];
json[JKChatResponse.ELEMENTS].forEach((v) {
elements!.add(new Elements.fromJson(v));
});
}
if (json[JKChatResponse.BUTTONS] != null) {
buttons = <ElementButtons /*Buttons*/ >[];
json[JKChatResponse.BUTTONS].forEach((v) {
if ((v['title'] ?? '').toString().isNotEmpty)
buttons!.add(new ElementButtons /*Buttons*/ .fromJson(v));
});
}
if (json[JKChatResponse.BUTTON] != null) {
button = <ElementButtons /*Buttons*/ >[];
json[JKChatResponse.BUTTON].forEach((v) {
if ((v['title'] ?? '').toString().isNotEmpty)
button!.add(new ElementButtons /*Buttons*/ .fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data[JKChatResponse.TYPE] = this.type;
data[JKChatResponse.SUCCESS] = this.success;
data[JKChatResponse.ERROR] = this.error;
data[JKChatResponse.TEXT] = this.text;
data[JKChatResponse.TITLE] = this.title;
data[JKChatResponse.PAYLOAD] = this.payload;
data[JKChatResponse.SUB_TYPE] = this.subType;
data[JKChatResponse.URLS] = this.urls;
if (this.elements != null) {
data[JKChatResponse.ELEMENTS] =
this.elements!.map((v) => v.toJson()).toList();
}
if (this.buttons != null) {
data[JKChatResponse.BUTTONS] =
this.buttons!.map((v) => v.toJson()).toList();
}
if (this.button != null) {
data[JKChatResponse.BUTTON] =
this.button!.map((v) => v.toJson()).toList();
}
return data;
}
}
class Elements {
int? id;
String? url;
String? image;
String? title;
String? subtitle;
List<Buttons>? buttons;
Elements(
{this.id, this.url, this.image, this.title, this.buttons, this.subtitle});
Elements.fromJson(Map<String, dynamic> json) {
id = json[JKChatResponse.ID];
url = json[JKChatResponse.URL];
image = json[JKChatResponse.IMAGE];
title = json[JKChatResponse.TITLE];
subtitle = json[JKChatResponse.SUBTITLE];
if (json[JKChatResponse.BUTTONS] != null) {
buttons = <Buttons>[];
json[JKChatResponse.BUTTONS].forEach((v) {
if ((v['title'] ?? '').toString().isNotEmpty)
buttons!.add(new Buttons.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data[JKChatResponse.ID] = this.id;
data[JKChatResponse.URL] = this.url;
data[JKChatResponse.IMAGE] = this.image;
data[JKChatResponse.TITLE] = this.title;
data[JKChatResponse.SUBTITLE] = this.subtitle;
if (this.buttons != null) {
data[JKChatResponse.BUTTONS] =
this.buttons!.map((v) => v.toJson()).toList();
}
return data;
}
}
class Buttons {
int? id;
String? type;
String? title;
String? value;
Buttons({this.id, this.type, this.title, this.value});
Buttons.fromJson(Map<String, dynamic> json) {
id = json[JKChatResponse.ID];
type = json[JKChatResponse.TYPE];
title = json[JKChatResponse.TITLE];
value = (json[JKChatResponse.VALUE] ?? '').toString();
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data[JKChatResponse.ID] = this.id;
data[JKChatResponse.TYPE] = this.type;
data[JKChatResponse.TITLE] = this.title;
data[JKChatResponse.VALUE] = this.value;
return data;
}
}
class ElementButtons {
int? id;
String? type;
String? extra;
String? title;
String? value;
String? payload;
String? verbose;
int? formSequence;
//Null formSequenceTitle;
bool? messengerExtensions;
String? webviewHeightRatio;
ElementButtons(
{this.id,
this.type,
this.extra,
this.title,
this.value,
this.payload,
this.verbose,
this.formSequence,
// this.formSequenceTitle,
this.messengerExtensions,
this.webviewHeightRatio});
ElementButtons.fromJson(Map<String, dynamic> json) {
id = json['id'];
type = json['type'];
extra = json['extra'];
title = json['title'];
value = json['value'].toString();
payload = json['payload'];
verbose = json['verbose'];
formSequence = json['form_sequence'];
// formSequenceTitle = json['form_sequence_title'];
messengerExtensions = json['messenger_extensions'];
webviewHeightRatio = json['webview_height_ratio'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['type'] = this.type;
data['extra'] = this.extra;
data['title'] = this.title;
data['value'] = this.value;
data['payload'] = this.payload;
data['verbose'] = this.verbose;
data['form_sequence'] = this.formSequence;
// data['form_sequence_title'] = this.formSequenceTitle;
data['messenger_extensions'] = this.messengerExtensions;
data['webview_height_ratio'] = this.webviewHeightRatio;
return data;
}
}
class Report {
String? recipientId;
String? messageId;
dynamic error;
Report({this.recipientId, this.messageId});
Report.fromJson(Map<String, dynamic> json) {
recipientId = json[JKChatResponse.RECIPIENT_ID];
messageId = json[JKChatResponse.MESSAGE_ID];
error = json['error'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data[JKChatResponse.RECIPIENT_ID] = this.recipientId;
data[JKChatResponse.MESSAGE_ID] = this.messageId;
data['error'] = this.error;
return data;
}
}
class AdminInfo {
int? id;
String? email;
String? avatar;
String? fullName;
AdminInfo({this.id, this.email, this.avatar, this.fullName});
AdminInfo.fromJson(Map<String, dynamic> json) {
id = json[JKChatResponse.ID];
email = json[JKChatResponse.EMAIL];
avatar = json[JKChatResponse.AVATAR];
fullName = json[JKChatResponse.FULL_NAME];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data[JKChatResponse.ID] = this.id;
data[JKChatResponse.EMAIL] = this.email;
data[JKChatResponse.AVATAR] = this.avatar;
data[JKChatResponse.FULL_NAME] = this.fullName;
return data;
}
}
class Actions {
String? action;
bool? isAllowed;
Actions({this.action, this.isAllowed});
Actions.fromJson(Map<String, dynamic> json) {
action = json[JKChatResponse.ACTION];
isAllowed = json[JKChatResponse.IS_ALLOWED];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data[JKChatResponse.ACTION] = this.action;
data[JKChatResponse.IS_ALLOWED] = this.isAllowed;
return data;
}
}
class Datam {
String? text;
bool? save;
//Null attribute;
// List<Buttons>? buttons;
List<ElementButtons>? buttons;
List<ElementButtons>? button;
List<Elements>? elements;
//Null api;
String? subType;
List<String>? urls;
Datam(
{this.text,
this.save,
//this.attribute,
this.buttons,
//this.api,
this.subType,
this.elements});
Datam.fromJson(Map<String, dynamic> json) {
text = ((json['text'] ?? '') is String) ? json['text'] : '';
save = json['save'];
if (json['urls'] != null) {
urls = json['urls'].cast<String>();
}
if (json['elements'] != null) {
elements = <Elements>[];
json['elements'].forEach((v) {
elements!.add(new Elements.fromJson(v));
});
}
//attribute = json['attribute'];
if (json['buttons'] != null) {
buttons = <ElementButtons>[];
json['buttons'].forEach((v) {
if ((v['title'] ?? '').toString().isNotEmpty)
buttons!.add(new ElementButtons.fromJson(v));
});
}
// api = json['api'];
subType = json['sub_type'];
if (json['button'] != null) {
button = <ElementButtons>[];
json['button'].forEach((v) {
if ((v['title'] ?? '').toString().isNotEmpty)
button!.add(new ElementButtons.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['text'] = this.text;
data['save'] = this.save;
// data['attribute'] = this.attribute;
if (this.elements != null) {
data['elements'] = this.elements!.map((v) => v.toJson()).toList();
}
if (this.buttons != null) {
data['buttons'] = this.buttons!.map((v) => v.toJson()).toList();
}
if (this.button != null) {
data['button'] = this.button!.map((v) => v.toJson()).toList();
}
//data['api'] = this.api;
data['sub_type'] = this.subType;
data['urls'] = this.urls;
return data;
}
}
data_source.dart
copy paste below on revamp_features
import 'package:myalice/models/responseModels/sendChats/data.dart';
import 'admin_info.dart';
class SendDataSource {
dynamic id;
String? type;
String? source;
String? platformType;
int? platformId;
int? customerId;
int? projectId;
Data? data;
String? conversationId;
int? timestamp;
int? adminId;
AdminInfo? adminInfo;
dynamic success;
String? pusherKey;
SendDataSource({
this.id,
this.type,
this.source,
this.platformType,
this.platformId,
this.customerId,
this.projectId,
this.data,
this.conversationId,
this.timestamp,
this.adminId,
this.adminInfo,
this.success,
this.pusherKey,
});
factory SendDataSource.fromJson(Map<String, dynamic> json) => SendDataSource(
// id: json['_id'],
type: json['type'] as String?,
source: json['source'] as String?,
platformType: json['platform_type'] as String?,
platformId: json['platform_id'] as int?,
customerId: json['customer_id'] as int?,
projectId: json['project_id'] as int?,
data: json['dataV2'] != null &&
json['dataV2'] is Map &&
json['dataV2'].length > 0
? new Data.fromJson(json['dataV2']):json['data'] == null
? null
: Data.fromJsonData(json['data'] as Map<String, dynamic>),
conversationId: json['conversation_id'] as String?,
timestamp: json['timestamp'] as int?,
adminId: json['admin_id'] as int?,
adminInfo: json['admin_info'] == null
? null
: AdminInfo.fromJson(json['admin_info'] as Map<String, dynamic>),
success: json['success'],
pusherKey: json['pusher_key'] as String?,
);
Map<String, dynamic> toJson() => {
'_id': id,
'type': type,
'source': source,
'platform_type': platformType,
'platform_id': platformId,
'customer_id': customerId,
'project_id': projectId,
'dataV2': data?.toJson(),
'conversation_id': conversationId,
'timestamp': timestamp,
'admin_id': adminId,
'admin_info': adminInfo?.toJson(),
'success': success,
'pusher_key': pusherKey,
};
}
linkableTextView.dart
copy paste below on revamp_features
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:get/get_core/src/get_main.dart';
import 'package:myalice/screens/agentProfile/myAliceWebView.dart';
import 'package:myalice/utils/colors.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:validators/validators.dart';
import 'package:metadata_fetch/metadata_fetch.dart';
import 'package:http/http.dart' as http;
import '../../../../utils/routes.dart';
class Linkify {
String strText = '';
bool isUrl = false;
}
class LinkableTextView extends StatefulWidget {
final String text;
final String platformType;
final bool isFeed;
LinkableTextView({
required this.text,
required this.platformType,
this.isFeed = false,
Key? key,
}) : super(key: key);
@override
_LinkableTextView createState() => _LinkableTextView();
}
class _LinkableTextView extends State<LinkableTextView> {
//r"((https?:www\.)|(https?:\/\/)|(www\.))[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9]{1,6}(\/[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)?"
final urlRegExp = new RegExp(
r'(?:(?:https?|ftp):\/\/)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9]{1,6}(\/[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)?',
caseSensitive: false);
String text = '';
var linkify = <Linkify>[];
Metadata? data;
@override
void initState() {
// TODO: implement initState
super.initState();
text = widget.text;
getLinkableText();
}
double height = 0;
String changedUrl = '';
Future<void> getPreviewData(String url) async {
try {
if (url.contains('l.facebook.com')) {
String delimiter1 = 'u=';
String delimiter2 = '&h=';
var n = Uri.decodeComponent(url);
int firstIndex = n.indexOf(delimiter1) + 2;
int lastIndex = n.indexOf(delimiter2);
String trimmed = n.substring(firstIndex, lastIndex);
changedUrl = trimmed;
print(trimmed);
} else
changedUrl = url;
data = await MetadataFetch.extract(changedUrl);
if (data!.image != null) {
Uri u = Uri.parse(changedUrl);
var hostUrl = u.host;
if (!data!.image!.startsWith('http')) {
data!.image = 'http://' + hostUrl + data!.image!;
}
}
} catch (e) {}
if (mounted) setState(() {});
}
Future<bool> isValidUrl(String url) async {
Uri? uri = Uri.tryParse(checkForProtocol(url));
if (uri == null) return false;
final response = await http.head(uri);
return response.statusCode == 200 ? true : false;
}
void getLinkableText() {
final urlMatches = urlRegExp.allMatches(text);
int lastIndex = 0;
int startLink = 0;
int lastLinkIndex = urlMatches.length - 1;
String fistLinkUrl = '';
List<dynamic> urlsAll = urlMatches.map((urlMatch) {
if (lastIndex != urlMatch.start) {
var normalText = text.substring(lastIndex, urlMatch.start);
var notLink = Linkify();
notLink.isUrl = false;
notLink.strText = normalText;
linkify.add(notLink);
}
var url = text.substring(urlMatch.start, urlMatch.end);
if (startLink == 0) {
fistLinkUrl = url;
}
bool isPreview = false;
if (startLink == lastLinkIndex &&
(widget.platformType == 'facebook_messenger' ||
widget.isFeed)) {
if (url.contains('l.facebook.com') || widget.isFeed) {
isPreview = true;
var withProtocolUrl = checkForProtocol(url);
getPreviewData(withProtocolUrl);
}
}
if (!isPreview) {
var link = Linkify();
link.isUrl = isURL(url); //true;
link.strText = url;
linkify.add(link);
}
lastIndex = urlMatch.end;
startLink++;
return text.substring(urlMatch.start, urlMatch.end);
}).toList();
if (lastIndex < text.length) {
var normalText = text.substring(lastIndex, text.length);
var notlink = Linkify();
notlink.isUrl = false;
notlink.strText = normalText;
linkify.add(notlink);
}
}
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Padding(padding: EdgeInsets.only(
left: widget.isFeed ? 16 : 0,
right: widget.isFeed ? 16 : 0,),
child: RichText(
text: TextSpan(
children: [
for (int i = 0; i < linkify.length; i++)
TextSpan(
text:
i == linkify.length - 1 && linkify[i].strText == '\n'
? ''
: linkify[i].strText + "",
style: TextStyle(
fontSize: 14,
fontWeight: linkify[i].isUrl
? FontWeight.w500
: FontWeight.w400,
color: (linkify[i].isUrl
? AliceColors.ALICE_BLUE
: AliceColors.ALICE_GREY_900)),
recognizer: linkify[i].isUrl
? (TapGestureRecognizer()
..onTap = () {
_launchURL(
linkify[i].strText); // will not work here
})
: null),
],
),
)),
if ((widget.isFeed || widget.platformType == 'facebook_messenger') &&
data != null)
InkWell(
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (data!.image != null)
Container(
height: widget.isFeed ? 208 : 120,
width: widget.isFeed ? double.infinity : 290,
padding: EdgeInsets.only(top: 5, bottom: 5),
child: CachedNetworkImage(
imageBuilder: (context, imageProvider) => Container(
decoration: BoxDecoration(
borderRadius:
BorderRadius.all(Radius.circular(8)),
image: DecorationImage(
image: imageProvider, fit: BoxFit.fill),
),
),
fit: BoxFit.contain,
imageUrl: data!.image!,
errorWidget: (context, url, error) =>
Icon(Icons.error),
),
),
if (data!.title != null)
Container(
padding: EdgeInsets.only(
left: widget.isFeed ? 16 : 0,
right: widget.isFeed ? 16 : 0,
top: 5,
bottom: 5),
child: Text(
data!.title!,
style: TextStyle(
fontWeight: widget.isFeed ?FontWeight.w400 : FontWeight.bold,
fontSize:widget.isFeed ? 14 : 16,
color: AliceColors.ALICE_GREY_900),
)),
if (data!.description != null && !widget.isFeed )
Container(
padding: EdgeInsets.only(
left: widget.isFeed ? 16 : 0,
right: widget.isFeed ? 16 : 0,
top: 5,
bottom: 5),
child: Text(
data!.description!,
maxLines: 2,
style: TextStyle(
fontWeight: FontWeight.normal,
fontSize: 14,
overflow: TextOverflow.ellipsis,
color: AliceColors.ALICE_GREY_700),
)),
if (data!.url != null && !widget.isFeed )
Container(
padding: EdgeInsets.only(
left: widget.isFeed ? 12 : 0,
right: widget.isFeed ? 12 : 0,
top: 5,
bottom: 5),
child: Text(
data!.url!,
style: TextStyle(
fontWeight: FontWeight.normal,
fontSize: 12,
color: AliceColors.ALICE_GREY_DARK),
))
])),
onTap: () {
_launchURL(changedUrl);
},
)
]);
}
/*
*/
String checkForProtocol(String url) {
bool isAdded = false;
var s = ((url.substring(0, 4)).toLowerCase());
if (s != 'http') {
url = 'http://' + url;
isAdded = true;
}
if (!isAdded) if (((url.substring(0, 5)).toLowerCase()).contains('https')) {
url = url.replaceRange(0, 5, 'https');
} else if (((url.substring(0, 4)).toLowerCase()).contains('http')) {
url = url.replaceRange(0, 4, 'http');
}
return url;
}
void _launchURL(String urlMain) async {
String url = checkForProtocol(urlMain);
try {
await launchUrl(Uri.parse(url));
//Get.toNamed(WEB_VIEWER, arguments: [url, url]);
} catch (e) {
print(e);
}
}
}
conversationBaseWidget.dart
copy paste below on revamp_features
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:get/get.dart';
import 'package:get/get_core/src/get_main.dart';
import 'package:myalice/controllers/apiControllers/chatApiController.dart';
import 'package:myalice/controllers/apiControllers/inboxController.dart';
import 'package:myalice/controllers/viewController/chatTextingViewController.dart';
import 'package:myalice/models/jsonSchema.dart';
import 'package:myalice/screens/chatDetails/customWidgets/conversationWidgets/conversationVideoViewWidget.dart';
import 'package:myalice/utils/colors.dart';
import 'package:myalice/utils/readTimeStamp.dart';
import '../../../../controllers/pusherController/pusherController.dart';
import '../../../../models/responseModels/chatResponse.dart';
import '../../styles/textStyles.dart';
import '../modals/convCopyBookmarkModal.dart';
import 'conversationAttachedFileView.dart';
import 'conversationAudioPlayerWidget.dart';
import 'conversationGalleryViewer.dart';
import '../chats/linkableTextView.dart';
import 'conversationBtnQuickReplyWidget.dart';
import 'conversationImageViewWidget.dart';
class ConversationBaseWidget extends StatefulWidget {
final url;
final name;
final ChatDataSource conversation;
final platformType;
final index;
final lastMsgIndex;
ConversationBaseWidget({Key? key,
required this.url,
required this.name,
required this.conversation,
required this.platformType,
required this.index,
required this.lastMsgIndex})
: super(key: key);
@override
_ConversationBaseWidget createState() {
return _ConversationBaseWidget();
}
}
class _ConversationBaseWidget extends State<ConversationBaseWidget> {
String imageUrl = '';
String sourceName = '';
String fbBusinessManagerText = 'The agent has sent one text message.';
String sourceNameForInitials = '';
InboxController? _inboxController;
@override
void initState() {
// TODO: implement initState
super.initState();
init();
}
String getNameInitials(String? groupName) {
if (widget.conversation.source == JKChatResponse.BOT) {
groupName =
_inboxController != null && _inboxController?.projectName != null
? _inboxController?.projectName
: groupName;
}
List<String> list = groupName!.split(' ');
try {
if (list.length == 0) {
return "";
} else if (list.length == 1) {
if (list
.elementAt(0)
.isEmpty) {
return '';
}
return (list.elementAt(0)[0]).toUpperCase();
} else {
String s = '';
s = list
.elementAt(0)
.isNotEmpty ? list.elementAt(0)[0] : '';
s += list
.elementAt(1)
.isNotEmpty ? list.elementAt(1)[0] : '';
return (s).toUpperCase();
}
} catch (e) {
print(e);
return groupName[0];
}
}
bool isTemplateAvailable = false;
init() {
try {
_inboxController = Get.find<InboxController>();
} catch (e) {}
imageUrl = widget.conversation.source == JKChatResponse.CUSTOMER
? widget.url
: widget.conversation.source == 'echo' ? '' : widget.conversation.source == 'bot'
? _inboxController != null &&
_inboxController?.selectedProject != null &&
_inboxController?.selectedProject?.image != null
? _inboxController?.selectedProject?.image
: ''
: widget.conversation.adminInfo?.avatar;
sourceNameForInitials = widget.conversation.source == 'customer'
? widget.name.isNotEmpty
? widget.name
: 'Anonymous'
: widget.conversation.source == 'bot'
? 'Bot'
: widget.conversation.adminInfo != null
? widget.conversation.adminInfo?.fullName : '';
sourceName = widget.conversation.source == 'customer'
? widget.name.isNotEmpty
? widget.name
: 'Anonymous'
: widget.conversation.source == 'echo' ? 'FB Business Manager' : widget.conversation.source == 'bot'
? 'Bot'
: widget.conversation.adminInfo != null
? widget.conversation.data!.type != 'note' ? widget.conversation.adminInfo?.fullName : isUserCreator()
: '';
isTemplateAvailable = getIsTemplateAvailable();
fbBusinessManagerText = widget.conversation.source == 'echo'?(widget.conversation.data?.success??false) ? widget.conversation.data?.text??'' :(widget.conversation.data?.error??''):'';
}
String isUserCreator() {
return (_inboxController?.user.dataSource!.id ==
widget.conversation.adminInfo?.id ?
'You' : widget.conversation.adminInfo?.fullName) ?? '';
return '';
}
bool getIsTemplateAvailable() {
return widget.conversation.data!.type == JKChatResponse.GALLERY ||
widget.conversation.data!.type == JKChatResponse.QUICK_REPLY||
widget.conversation.data!.type == JKChatResponse.BUTTON||
widget.conversation.data!.type == JKChatResponse.BUTTONS||
(widget.conversation.data!.type == JKChatResponse.ATTACHMENT &&
(widget.conversation.data!.subType == JKChatResponse.IMAGE ||
widget.conversation.data!.subType == JKChatResponse.VIDEO ||
widget.conversation.data!.subType == JKChatResponse.FILE ||
widget.conversation.data!.subType == JKChatResponse.AUDIO));
}
double getBorderBarHeight() {
int btnCount = 0;
double heightGallery = 159;
if ((widget.conversation.subType == JKChatResponse.AUDIO)) {
print(widget.conversation.toString());
}
if (widget.conversation.data!.type == JKChatResponse.GALLERY)
for (int i = 0;
i < widget.conversation.data!.elements!.length;
i++) {
if (widget.conversation.data!.elements![i].buttons!.length >
btnCount)
btnCount =
widget.conversation.data!.elements![i].buttons!.length;
}
heightGallery += btnCount * 33;
double height = widget.conversation.data!.type == JKChatResponse.QUICK_REPLY ||
widget.conversation.data!.type == JKChatResponse.BUTTON
? 30
: widget.conversation.data!.type == JKChatResponse.GALLERY
? heightGallery
: (widget.conversation.data!.type == JKChatResponse.ATTACHMENT)
? (widget.conversation.data!.subType == JKChatResponse.IMAGE &&
widget.conversation.data!.urls != null &&
widget.conversation.data!.urls!.length == 1) ||
(widget.conversation.data!.subType == JKChatResponse.VIDEO &&
widget.conversation.data!.urls != null &&
widget.conversation.data!.urls!.length == 1)
? 120
: (widget.conversation.data!.subType == JKChatResponse.IMAGE &&
widget.conversation.data!.urls != null &&
widget.conversation.data!.urls!.length > 1) ||
(widget.conversation.data!.subType == JKChatResponse.IMAGE &&
widget.conversation.data!.urls != null &&
widget.conversation.data!.urls!.length > 1)
? 70
: (widget.conversation.data!.subType == JKChatResponse.FILE)
? 32
: (widget.conversation.data!.subType == JKChatResponse.AUDIO)
? 45
: 30
: 15;
if ((widget.conversation.data!.type == JKChatResponse.ATTACHMENT &&
widget.conversation.data!.subType == JKChatResponse.IMAGE)) print(height);
return height;
}
checkIsTemplate() {
String templateMsg = '';
bool isTemplate = false;
isTemplate = false;
if (widget.conversation.data!.type == "betterdocs" ||
widget.conversation.data!.type == JKChatResponse.BUTTON ||
widget.conversation.data!.type == JKChatResponse.QUICK_REPLY ||
widget.conversation.data!.type == "product_discovery" ||
widget.conversation.data!.type == "place_order" ||
widget.conversation.data!.type == "view_cart") {
templateMsg = widget.conversation.data!.type ?? 'Unknown content' + " Was Sent";
isTemplate = true;
}
}
String templateMsg = "";
bool isTemplate = false;
@override
Widget build(BuildContext context) {
if (widget.conversation.data!.type == "betterdocs" ||
widget.conversation.data!.type == "product_discovery" ||
widget.conversation.data!.type == "place_order" ||
widget.conversation.data!.type == "view_cart") {
templateMsg =
(widget.conversation.data!.type ?? 'Unknown content') + " Was Sent";
isTemplate = true;
}
return Container(
color: widget.conversation.data!.type ==
"note" ? AliceColors.ALICE_ORANGE_100 : AliceColors.ALICE_WHITE,
padding: EdgeInsets.only(left: 8, right: 8, top: 9, bottom: 9),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
/*(object.chats.elementAt(index)!.source == "customer"
? MainAxisAlignment.start
: MainAxisAlignment.end),*/
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: EdgeInsets.only(bottom: 0),
child: imageUrl != null && imageUrl != ""
? CircleAvatar(
radius: 16,
backgroundImage: CachedNetworkImageProvider(imageUrl),
)
: CircleAvatar(
child: widget.conversation.source == 'bot' || widget.conversation.source == 'echo'
? SvgPicture.asset(
widget.conversation.source == 'echo' ?"assets/chat_icon/echo_avatar.svg" : "assets/chat_icon/alice_logo.svg",
)
: Text(
getNameInitials(sourceNameForInitials),
maxLines: 1,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
color:
AliceColors.ALICE_WHITE),
),
radius: 16,
backgroundColor:
widget.conversation.source == 'bot'
? AliceColors.ALICE_WHITE
: AliceColors.ALICE_GREY_500,
)),
SizedBox(
width: 12,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Row(
children: [
Text(
sourceName,
textAlign: TextAlign.center,
style: widget.conversation.source ==
JKChatResponse.CUSTOMER
? TextStyles.headerNameStyleCustomer
: widget.conversation.source == 'echo'? TextStyles.headerNameStyleEcho: widget.conversation.source == 'bot'
? TextStyles.headerNameStyleBot
: TextStyles.headerNameStyleAdmin,
),
SizedBox(
width: 8,
),
if(widget.conversation.data!.type == 'note')
Text('Private Note', style: TextStyle(
fontWeight: FontWeight.w500,
color: AliceColors.ALICE_ORANGE_900,
fontSize: 12)),
if(widget.conversation.data!.type == 'note')
SizedBox(
width: 8,
),
Text(
readTimeFromDateTime(
widget.conversation.timestamp ?? 0),
textAlign: TextAlign.center,
style: TextStyles.actionTextStyle,
),
],
),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: 4,
),
if (((widget.conversation.text ?? "").isNotEmpty || (widget.conversation.data!.title ?? "").isNotEmpty
|| (isTemplate && templateMsg.isNotEmpty)))
ConstrainedBox(
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width - 100),
child: InkWell(
onLongPress: () {
openCopyModal(context);
},
child: LinkableTextView(
text: !isTemplate
? widget.conversation.data!.type == JKChatResponse.QUICK_REPLY
? widget.conversation.data!.title ?? ""
: widget.conversation.text ?? ""
: templateMsg,
platformType: (widget
.conversation
.platformType) ==
null ||
(widget.conversation
.platformType)!
.isEmpty
? widget.platformType
: widget.conversation
.platformType,
)),
),
SizedBox(
height: ((widget.conversation
.text ??
"")
.isNotEmpty ||
(isTemplate &&
templateMsg
.isNotEmpty)) &&
isTemplateAvailable
? 4
: 0,
),
if (isTemplateAvailable &&
(widget.conversation.data!.type != JKChatResponse.QUICK_REPLY && widget.conversation.data!.type != JKChatResponse.BUTTON && widget.conversation.data!.type != JKChatResponse.BUTTONS || (widget.conversation.data!.type == JKChatResponse.QUICK_REPLY && (widget.conversation.data!.buttons ?? []).isNotEmpty)||
(widget.conversation.data!.type == JKChatResponse.BUTTON && (widget.conversation.data!.button ?? []).isNotEmpty) ||
(widget.conversation.data!.type == JKChatResponse.BUTTONS && (widget.conversation.data!.buttons ?? []).isNotEmpty)))
Row(mainAxisSize: MainAxisSize
.min, children: <Widget>[
Container(
width: 4,
//height: double.infinity,
height:
getBorderBarHeight(),
decoration: BoxDecoration(
color: widget
.conversation
.source !=
JKChatResponse
.CUSTOMER
? AliceColors
.ALICE_GREY_200
: AliceColors
.ALICE_GREEN_300,
borderRadius:
BorderRadius
.circular(100)),
margin:
const EdgeInsets.only(
right: 8)),
(widget.conversation.data!.type ==
JKChatResponse
.QUICK_REPLY ||
widget.conversation.data!.type ==
JKChatResponse.BUTTON || widget.conversation.data!.type ==
JKChatResponse.BUTTONS)
?
ConvBtnQuickReplyWidget(
buttons:widget.conversation.data!.type == JKChatResponse.QUICK_REPLY
? widget.conversation.data!.buttons ?? []
: widget.conversation.data!.button ?? [],
type: widget
.conversation.data!.type!,
text: widget.conversation.data!.text ?? ""
)
: (widget.conversation.data!
.type ==
JKChatResponse.ATTACHMENT)
? ((widget.conversation.data!
.subType ==
JKChatResponse.IMAGE)
? ConvImageView(
links: widget
.conversation
.data!.urls!)
: (widget.conversation.data!
.subType ==
JKChatResponse.VIDEO)
? ConvVideoViewWidget(
links: widget.conversation
.data!.urls!)
: (widget.conversation.data!
.subType ==
JKChatResponse.FILE &&
widget.conversation.data!
.urls != null)
? AttachedFileViewer(
links: widget.conversation
.data!.urls!)
: (widget.conversation.data!
.subType ==
JKChatResponse.AUDIO)
? new ConvAudioViewWidget(
links:
widget.conversation.data!
.urls!,
)
: Container())
: widget.conversation.data!
.type ==
JKChatResponse.GALLERY
? GalleryViewer(
elements: widget
.conversation.data!
.elements!)
: Container()
]),
if (!isTemplateAvailable &&
!((widget.conversation.text ?? "").isNotEmpty ||
(isTemplate && templateMsg.isNotEmpty)))
Container(
child:Text(
widget.conversation.source == "echo"
? fbBusinessManagerText
: '${widget.conversation.type} ${widget.conversation.subType ?? ''} was sent.')
),
]),
Visibility(
visible: widget.conversation.source !=
'customer' && widget.conversation.data!
.type !=
'note' && widget.conversation.source != "echo" &&
(widget.index == widget
.lastMsgIndex || (widget
.conversation
.data !=
null && !widget
.conversation
.data!.success!)),
child: Obx(() =>
MyTooltip(
message: widget.conversation.isPusherSucceeded.isFalse
? 'Sending'
: widget.conversation.isPusherSucceeded.value && widget.conversation.data!.success!
? 'Delivered'
: 'Failed to deliver',
child: Container(
margin: EdgeInsets.only(
left: 6, right: 6),
child: SvgPicture.asset(
widget.conversation.isPusherSucceeded.isFalse
? "assets/chat_icon/report_sending.svg"
: widget.conversation.isPusherSucceeded.value && widget.conversation.data!.success!
? "assets/chat_icon/report_success.svg"
: "assets/chat_icon/report_failed.svg",
),
)
)))
]),
]),
])
])
]));
}
openCopyModal(BuildContext context) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(6), topRight: Radius.circular(6)),
),
builder: (context) {
return ConvCopyAndBookmarkModal(
text: !isTemplate ? widget.conversation.text ?? "" : templateMsg,
);
}).whenComplete(() {});
}
}
class MyTooltip extends StatelessWidget {
final Widget child;
final String message;
MyTooltip({required this.message, required this.child});
@override
Widget build(BuildContext context) {
final key = GlobalKey<State<Tooltip>>();
return Tooltip(
key: key,
message: message,
preferBelow: false,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(3),
color: AliceColors.ALICE_GREY_TOOLTIP,
),
padding:
const EdgeInsets.only(left: 15.0, right: 15, top: 10, bottom: 10),
margin: const EdgeInsets.only(left: 15, right: 15, top: 4, bottom: 0),
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => _onTap(key),
child: child,
),
);
}
void _onTap(GlobalKey key) {
final dynamic tooltip = key.currentState;
tooltip?.ensureTooltipVisible();
Future.delayed(Duration(seconds: 2), () {
tooltip?.deactivate();
});
}
}
SOLVED
] Gradle: https://github.com/sakibguy/worked-issues/issues/127Feb 8, 2023
FIXING Merge Conflicts 2
Filter
EFFORT 1: FAILED
7:20pmD:\myaliceapp_freshcopy>git clone https://github.com/alice-labs/myalice_app.git
Cloning into 'myalice_app'...
remote: Enumerating objects: 9683, done.
remote: Counting objects: 100% (1293/1293), done.
remote: Compressing objects: 100% (410/410), done.
error: RPC failed; curl 92 HTTP/2 stream 7 was not closed cleanly before end of the underlying stream
error: 744 bytes of body are still expected
fetch-pack: unexpected disconnect while reading sideband packet
fatal: early EOF
fatal: fetch-pack: invalid index-pack output
EFFORT 2: SUCCEEDED
FILTER: PR https://github.com/alice-labs/myalice_app/pull/74 is up for review.
branch: sakib_revamp
Only not getting agent data from server response for mobile. Please help me fixing your req/resp function or verify as server issue and masuma apu waiting for apk.
Merge Conflicts 2
Feature: Filter
SITUATION
cc: @kmehran1106 bro @snrahman01 apu
PROB: Raised merge conflict during Filter PR https://github.com/alice-labs/myalice_app/pull/74 where I didn't edit/change/mod other files. Only edited
filterPage.dart
,inboxController.dart
,drawerWidget.dart
,inboxScreenRevamp.dart
. But showing diff conflicted files.SOLN: Please
ACTION
RESULT
: ConflictDidn't work those/other files to complete/fix filter.
RESULT
: SolvedManually solved https://github.com/alice-labs/myalice_app/pull/78
PROB
SNaP