sakibguy / worked-issues

arget medi a[rpescriptionmedicie] &ssets[dr] (reeeverse._ax:p)
2 stars 0 forks source link

[ORG] SG-MYALICE: [UNSOLVED (0) SOLVED(n+2)] Merge Conflicts #85

Closed sakibguy closed 1 year ago

sakibguy commented 1 year ago

PROB

PR https://github.com/alice-labs/myalice_app/pull/55

@snrahman01 Apu, getting merge conflicts. I pulled master & dev_branch as latest, but now if I merge master/dev_branch with my sakib_conv branch then merge conflict will raise. I don't know what code/business logic changes you did... and how to consistently go ahead without breaking your changes.

SNaP

merge-conflict

sakibguy commented 1 year ago

Conflicted files

Screenshot (325)

sakibguy commented 1 year ago

Fixed

No major conflict just new fun added chatApiController.dart

SNaP

1

sakibguy commented 1 year ago

Fixed

No major conflict just new way soln lib/screens/chatDetails/customWidgets/chats/linkableTextView.dart

SNaP

1

sakibguy commented 1 year ago

Fixed

No major conflict just constant vs hand coded json key so keep it as it was lib/screens/chatDetails/customWidgets/conversationWidgets/conversationBaseWidget.dart

SNaP

1

sakibguy commented 1 year ago

Solved

Fixed merge conflicts very easily

reasons

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

SNaP

screencapture-github-alice-labs-myalice-app-pull-55-2022-10-10-20_32_48

sakibguy commented 1 year ago

Branches

New design https://www.figma.com/file/s9tClQg76bzje69MJsJU6g/Mobile-App?node-id=3903%3A3031&t=nTR6mA6OmyGMUEh4-0

Conflict

revamp vs revamp_features

SNaP

1 2

SOLn

So, closed issue.

3

sakibguy commented 1 year ago

Merge conflict: revamp vs revamp_features

Screenshot (699)

sakibguy commented 1 year ago

New major issue

Not able to stash or switch other branch without fixing merge conflict.

Screenshot (699) Screenshot (700) Screenshot (701) Screenshot (702) Screenshot (703)

sakibguy commented 1 year ago

But Merge Conflicts

  1. Staged & commit on revamp branch
  2. Switched to sakib_conv
  3. Created new branch sakib_conv_revamp_features
  4. git pull origin revamp_features

1

Screenshot (705)

sakibguy commented 1 year ago

SOLn

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 ?? [],

SNaP

1 2

sakibguy commented 1 year ago

Merge Conflicts

  1. From dev_branch created revamp
  2. Fixed merge conflicts by manual copy pasting each files from revamp branch
  3. Created revamp_features branch from revamp branch
  4. git pull origin revamp_features then getting below conflicts. Doing step 2 to fix.

SNaP

Screenshot (713)

Fixed

Did step 2 to fix.

sakibguy commented 1 year ago

Merge Conflicts 1

Feature: Filter

sakib_revamp_features vs revamp_features https://github.com/alice-labs/myalice_app/pull/68

SOLn

parent -> child branch sequence

  1. revamp from dev_branch
  2. revamp_featutes from revamp

1 2

Conflict 1: dev_branch

git pull origin dev_branch

1

Soln: Manual copy paste conflicted files to fix

NEW DEPs ERRR

FIX 1: chatApiController.dart

1

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;
  }
}

FIX 2: conversationBaseWidget.dart

1

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();
    });
  }
}

FIX 3: 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;
  }
}

FIX 4: 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';
  }
}

1

SOLVED: conflict 1 dev_branch

1

NO CONFLICT: revamp

  1. git checkout dev_branch
  2. git checkout -b revamp
  3. git pull origin revamp

1 2 3 4 5

Conflict 2: revamp_features

git pull origin revamp_features

1 2

Soln: Manual copy paste conflicted files to fix

NEW DEPs ERRR

Auto-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.

FIX 1: 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!);
      }
    });
  }
}

FIX 2: 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);
} */

NEW DEPS ISSUE

1

FIX 3: 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';
  }
}

FIX 4: 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;
  }
}

FIX 5: 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,
      };
}

FIX 6: 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);
    }
  }
}

FIX 7: 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();
    });
  }
}
sakibguy commented 1 year ago

New Issues

  1. [SOLVED] Gradle: https://github.com/sakibguy/worked-issues/issues/127
  2. [INPROGRESS] Ticket list: https://github.com/sakibguy/worked-issues/issues/128
sakibguy commented 1 year ago

Feb 8, 2023

FIXING Merge Conflicts 2

Feature: Filter

EFFORT 1: FAILED 7:20pm

D:\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

SNaP

1

EFFORT 2: SUCCEEDED

1

SOLVED: on meeting with Nargis Apu

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.

SNaP

1

sakibguy commented 1 year ago

Merge Conflicts 2

Feb 27, 2023

Feature: Filter

EFFORT 1: FAILED 12:40pm

1- 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

SNaP

1 1

2- ACTION

2

3- RESULT: Conflict

Didn't work those/other files to complete/fix filter.

3 4 1

3- RESULT: Solved

Manually solved https://github.com/alice-labs/myalice_app/pull/78