Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

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

Closed
sakibguy opened this issue Oct 10, 2022 · 15 comments
Closed

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

sakibguy opened this issue Oct 10, 2022 · 15 comments

Comments

@sakibguy
Copy link
Owner

sakibguy commented Oct 10, 2022

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
Copy link
Owner Author

Conflicted files

Screenshot (325)

@sakibguy
Copy link
Owner Author

sakibguy commented Oct 10, 2022

Fixed

No major conflict just new fun added chatApiController.dart

SNaP

1

@sakibguy
Copy link
Owner Author

Fixed

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

SNaP

1

@sakibguy
Copy link
Owner Author

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
Copy link
Owner Author

sakibguy commented Oct 10, 2022

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

#85

#84

SNaP

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

@sakibguy sakibguy changed the title Merge conflicts MYALICE: Merge conflicts Dec 14, 2022
@sakibguy sakibguy changed the title MYALICE: Merge conflicts SG-MYALICE: Merge conflicts Dec 14, 2022
@sakibguy sakibguy reopened this Dec 29, 2022
@sakibguy
Copy link
Owner Author

sakibguy commented Dec 29, 2022

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
Copy link
Owner Author

sakibguy commented Jan 2, 2023

Merge conflict: revamp vs revamp_features

Screenshot (699)

@sakibguy sakibguy reopened this Jan 2, 2023
@sakibguy
Copy link
Owner Author

sakibguy commented Jan 2, 2023

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
Copy link
Owner Author

sakibguy commented Jan 2, 2023

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
Copy link
Owner Author

sakibguy commented Jan 3, 2023

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
Copy link
Owner Author

sakibguy commented Jan 4, 2023

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 sakibguy closed this as completed Jan 4, 2023
@sakibguy
Copy link
Owner Author

sakibguy commented Jan 25, 2023

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
Copy link
Owner Author

sakibguy commented Jan 26, 2023

@sakibguy sakibguy reopened this Jan 26, 2023
@sakibguy
Copy link
Owner Author

sakibguy commented Feb 8, 2023

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
Copy link
Owner Author

sakibguy commented Feb 27, 2023

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

@sakibguy sakibguy changed the title SG-MYALICE: Merge conflicts SG-MYALICE: [UNSOLVED (1) SOLVED(n+1)] Merge conflicts Feb 27, 2023
@sakibguy sakibguy changed the title SG-MYALICE: [UNSOLVED (1) SOLVED(n+1)] Merge conflicts SG-MYALICE: [UNSOLVED (1) SOLVED(n+1)] Merge Conflicts Feb 27, 2023
@sakibguy sakibguy changed the title SG-MYALICE: [UNSOLVED (1) SOLVED(n+1)] Merge Conflicts SG-MYALICE: [UNSOLVED (0) SOLVED(n+2)] Merge Conflicts Feb 27, 2023
@sakibguy sakibguy changed the title SG-MYALICE: [UNSOLVED (0) SOLVED(n+2)] Merge Conflicts [ORG] SG-MYALICE: [UNSOLVED (0) SOLVED(n+2)] Merge Conflicts Mar 22, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant