Chat Implementation Version 1
parent
b490cced9d
commit
b190a248bf
@ -0,0 +1,24 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="29.557" height="29.557" viewBox="0 0 29.557 29.557">
|
||||||
|
<g id="chat_3_" data-name="chat (3)" transform="translate(0 -0.008)">
|
||||||
|
<g id="Group_8672" data-name="Group 8672" transform="translate(0 0.007)">
|
||||||
|
<g id="Group_8671" data-name="Group 8671" transform="translate(0 0)">
|
||||||
|
<path id="Path_13684" data-name="Path 13684" d="M29.348,17.685l-2.039-2.379A8.6,8.6,0,0,0,29.556,9.59c0-5.369-5.209-9.583-11.315-9.583-6.207,0-11.257,4.3-11.257,9.583,0,.133,0,.266.011.4-4.131,1.641-7,5.18-7,9.271a9.341,9.341,0,0,0,2.483,6.276L.214,28.127a.866.866,0,0,0,.652,1.436H13.046c.1,0,.4-.055.462-.061A12.95,12.95,0,0,0,21,26.322a9.481,9.481,0,0,0,3.3-7.207H28.69A.867.867,0,0,0,29.348,17.685ZM13.219,27.79c-.076.006-.337.029-.39.042H2.774l1.538-1.758a.866.866,0,0,0-.056-1.2,7.739,7.739,0,0,1-2.525-5.616c0-4.694,4.687-8.515,10.449-8.515,5.73,0,10.391,3.82,10.391,8.515C22.571,23.649,18.482,27.36,13.219,27.79ZM24.1,17.383h0C23.057,12.566,18.06,9.013,12.18,9.013a14.281,14.281,0,0,0-3.461.422c.1-4.258,4.333-7.7,9.523-7.7,5.284,0,9.583,3.522,9.583,7.851a7.016,7.016,0,0,1-2.284,5.058.866.866,0,0,0-.059,1.189l1.326,1.547Z" transform="translate(0 -0.007)" fill="#989898"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="Group_8674" data-name="Group 8674" transform="translate(5.253 17.383)">
|
||||||
|
<g id="Group_8673" data-name="Group 8673">
|
||||||
|
<path id="Path_13685" data-name="Path 13685" d="M92.729,301a1.732,1.732,0,1,0,1.732,1.732A1.734,1.734,0,0,0,92.729,301Z" transform="translate(-90.997 -300.999)" fill="#989898"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="Group_8676" data-name="Group 8676" transform="translate(10.448 17.383)">
|
||||||
|
<g id="Group_8675" data-name="Group 8675">
|
||||||
|
<path id="Path_13686" data-name="Path 13686" d="M182.726,301a1.732,1.732,0,1,0,1.732,1.732A1.734,1.734,0,0,0,182.726,301Z" transform="translate(-180.994 -300.999)" fill="#989898"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="Group_8678" data-name="Group 8678" transform="translate(15.644 17.383)">
|
||||||
|
<g id="Group_8677" data-name="Group 8677">
|
||||||
|
<path id="Path_13687" data-name="Path 13687" d="M272.723,301a1.732,1.732,0,1,0,1.732,1.732A1.734,1.734,0,0,0,272.723,301Z" transform="translate(-270.991 -300.999)" fill="#989898"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
@ -0,0 +1,14 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="29.559" height="29.557" viewBox="0 0 29.559 29.557">
|
||||||
|
<g id="chat_2_" data-name="chat (2)" transform="translate(0 -0.006)">
|
||||||
|
<g id="Group_8680" data-name="Group 8680" transform="translate(0 8.954)">
|
||||||
|
<g id="Group_8679" data-name="Group 8679" transform="translate(0)">
|
||||||
|
<path id="Path_13688" data-name="Path 13688" d="M12.181,155C5.5,155,0,159.595,0,165.245a9.543,9.543,0,0,0,2.483,6.333L.214,174.171a.866.866,0,0,0,.652,1.436H13.046c4.936,0,11.315-4.25,11.315-10.362C24.361,159.595,18.865,155,12.181,155ZM6.927,166.832A1.732,1.732,0,1,1,8.659,165.1,1.734,1.734,0,0,1,6.927,166.832Zm5.253,0a1.732,1.732,0,1,1,1.732-1.732A1.734,1.734,0,0,1,12.181,166.832Zm5.253,0a1.732,1.732,0,1,1,1.732-1.732A1.734,1.734,0,0,1,17.434,166.832Z" transform="translate(0 -154.998)" fill="#2e303a"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="Group_8682" data-name="Group 8682" transform="translate(7.077 0.006)">
|
||||||
|
<g id="Group_8681" data-name="Group 8681" transform="translate(0 0)">
|
||||||
|
<path id="Path_13689" data-name="Path 13689" d="M144.854,17.626l-2.039-2.379a8.6,8.6,0,0,0,2.248-5.716c0-5.252-5.05-9.525-11.257-9.525-5.613,0-10.381,3.5-11.223,8.05a16.022,16.022,0,0,1,5.1-.835c7.583,0,13.813,5.3,13.9,11.834H144.2A.867.867,0,0,0,144.854,17.626Z" transform="translate(-122.583 -0.006)" fill="#2e303a"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="26.298" height="26.298" viewBox="0 0 26.298 26.298">
|
||||||
|
<path id="send" d="M26.3,0,0,14.793l8.4,3.113L21.367,5.753l-9.86,13.3.008,0-.01,0V26.3l4.713-5.5,5.97,2.211Z" fill="#2bb8a6"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 236 B |
@ -0,0 +1,213 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
|
import 'package:http/http.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:mohem_flutter_app/api/api_client.dart';
|
||||||
|
import 'package:mohem_flutter_app/classes/consts.dart';
|
||||||
|
import 'package:mohem_flutter_app/classes/utils.dart';
|
||||||
|
import 'package:mohem_flutter_app/models/chat/get_search_user_chat_model.dart';
|
||||||
|
import 'package:mohem_flutter_app/models/chat/get_single_user_chat_list_Model.dart';
|
||||||
|
import 'package:signalr_netcore/hub_connection.dart';
|
||||||
|
import 'package:signalr_netcore/signalr_client.dart';
|
||||||
|
import 'package:logger/logger.dart' as L;
|
||||||
|
|
||||||
|
class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
|
||||||
|
List<SingleUserChatModel> userChatHistory = [];
|
||||||
|
List<ChatUser>? pChatHistory, searchedChats;
|
||||||
|
late HubConnection hubConnection;
|
||||||
|
L.Logger logger = L.Logger();
|
||||||
|
TextEditingController message = TextEditingController();
|
||||||
|
ScrollController scrollController = ScrollController();
|
||||||
|
static String token =
|
||||||
|
"eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJuYW1laWQiOiI0MjA2MiIsImVtYWlsIjoiYWFtaXIubXVoYW1tYWRAY2xvdWRzb2x1dGlvbnMuY29tLnNhIiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy91c2VyZGF0YSI6ImFhbWlyLm11aGFtbWFkIiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvbW9iaWxlcGhvbmUiOiI5NjY1MzA4OTYwMTgiLCJuYmYiOjE2NjU2NDE4MTIsImV4cCI6MTY2NTcyODIxMiwiaWF0IjoxNjY1NjQxODEyfQ.dLMt_c0zXYYLvZtvK_5F2NrdgERlVXZSQpjZwMUWl7rsjZWMs03n1lVU_bWxvs5C_4luSlumwFNq1jSV9iunbQ";
|
||||||
|
|
||||||
|
bool isLoading = true;
|
||||||
|
|
||||||
|
void getChatMemberFromSearch(String sName, int cUserId) async {
|
||||||
|
isLoading = true;
|
||||||
|
notifyListeners();
|
||||||
|
Response response = await ApiClient().getJsonForResponse(
|
||||||
|
"${ApiConsts.chatSearchMember}$sName/$cUserId",
|
||||||
|
token: token,
|
||||||
|
);
|
||||||
|
isLoading = false;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void getUserRecentChats() async {
|
||||||
|
Response response = await ApiClient().getJsonForResponse(
|
||||||
|
"${ApiConsts.chatServerBaseApiUrl}${ApiConsts.chatRecentUrl}",
|
||||||
|
token: token,
|
||||||
|
);
|
||||||
|
ChatUserModel recentChat = userToList(response.body);
|
||||||
|
pChatHistory = recentChat.response;
|
||||||
|
searchedChats = pChatHistory;
|
||||||
|
isLoading = false;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void getSingleUserChatHistory({required String senderUID, required int receiverUID, required String pagination}) async {
|
||||||
|
isLoading = true;
|
||||||
|
Response response = await ApiClient().getJsonForResponse(
|
||||||
|
"${ApiConsts.chatServerBaseApiUrl}${ApiConsts.chatSingleUserHistoryUrl}/$senderUID/$receiverUID/$pagination",
|
||||||
|
token: token,
|
||||||
|
);
|
||||||
|
userChatHistory = getSingleUserChatintoModel(response.body);
|
||||||
|
isLoading = false;
|
||||||
|
logger.d(jsonEncode(userChatHistory));
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<SingleUserChatModel> getSingleUserChatintoModel(String str) => List<SingleUserChatModel>.from(json.decode(str).map((x) => SingleUserChatModel.fromJson(x)));
|
||||||
|
|
||||||
|
ChatUserModel userToList(String str) => ChatUserModel.fromJson(json.decode(str));
|
||||||
|
|
||||||
|
void buildHubConnection() async {
|
||||||
|
HttpConnectionOptions httpOp = HttpConnectionOptions(skipNegotiation: false, logMessageContent: true);
|
||||||
|
hubConnection = await HubConnectionBuilder()
|
||||||
|
.withUrl(ApiConsts.chatHubConnectionUrl + "?UserId=42062&source=Web&access_token=$token", options: httpOp)
|
||||||
|
.withAutomaticReconnect(retryDelays: [2000, 5000, 10000, 20000])
|
||||||
|
.configureLogging(Logger("Logs Enabled"))
|
||||||
|
.build();
|
||||||
|
hubConnection.onclose(
|
||||||
|
({Exception? error}) {
|
||||||
|
logger.d(error);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
hubConnection.onreconnecting(
|
||||||
|
({Exception? error}) {
|
||||||
|
logger.d(error);
|
||||||
|
logger.d("Reconnecting");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
hubConnection.onreconnected(
|
||||||
|
({String? connectionId}) {
|
||||||
|
logger.d("Reconnected");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (hubConnection.state != HubConnectionState.Connected) {
|
||||||
|
await hubConnection.start();
|
||||||
|
hubConnection.on("OnUpdateUserStatusAsync", changeStatus);
|
||||||
|
hubConnection.on("OnDeliveredChatUserAsync", onMsgReceived);
|
||||||
|
// hubConnection.on("OnUserTypingAsync", onUserTyping);
|
||||||
|
//hubConnection.on("OnUserTypingAsync", changeTypingStatus);
|
||||||
|
} else {
|
||||||
|
hubConnection.on("OnUpdateUserStatusAsync", changeStatus);
|
||||||
|
hubConnection.on("OnDeliveredChatUserAsync", onMsgReceived);
|
||||||
|
// hubConnection.on("OnUserTypingAsync", onUserTyping);
|
||||||
|
//hubConnection.on("OnUserTypingAsync", changeTypingStatus);
|
||||||
|
}
|
||||||
|
isLoading = false;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void changeStatus(List<Object?>? args) {
|
||||||
|
List items = args!.toList();
|
||||||
|
for (var user in searchedChats!) {
|
||||||
|
if (user.id == items.first["id"]) {
|
||||||
|
user.userStatus = items.first["userStatus"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void filter(String value) async {
|
||||||
|
List<ChatUser>? tmp = [];
|
||||||
|
if (value.isEmpty || value == "") {
|
||||||
|
tmp = pChatHistory;
|
||||||
|
} else {
|
||||||
|
for (var element in pChatHistory!) {
|
||||||
|
if (element.userName!.toLowerCase().contains(value.toLowerCase())) {
|
||||||
|
tmp.add(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
searchedChats = tmp;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> onMsgReceived(List<Object?>? parameters) async {
|
||||||
|
List<SingleUserChatModel> data = [];
|
||||||
|
for (dynamic msg in parameters!) {
|
||||||
|
data = getSingleUserChatintoModel(jsonEncode(msg));
|
||||||
|
logger.d(msg);
|
||||||
|
}
|
||||||
|
userChatHistory.add(data.first);
|
||||||
|
notifyListeners();
|
||||||
|
scrollDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
void onUserTyping(List<Object?>? parameters) {
|
||||||
|
print("==================== Typing Active ==================");
|
||||||
|
logger.d(parameters);
|
||||||
|
for (ChatUser user in searchedChats!) {
|
||||||
|
if (user.id == parameters![1] && parameters[0] == true) {
|
||||||
|
user.isTyping = parameters[0] as bool?;
|
||||||
|
} else {
|
||||||
|
Future.delayed(
|
||||||
|
const Duration(milliseconds: 500),
|
||||||
|
() {
|
||||||
|
user.isTyping = false;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendChatMessage(String chatMessage, int targetUserId, String targetUserName) async {
|
||||||
|
if (chatMessage == null || chatMessage.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String chatData =
|
||||||
|
'{"contant":"$chatMessage","contantNo":"8a129295-36d7-7185-5d34-cc4eec7bcba4","chatEventId":1,"fileTypeId":null,"currentUserId":42062,"chatSource":1,"userChatHistoryLineRequestList":[{"isSeen":false,"isDelivered":false,"targetUserId":$targetUserId,"targetUserStatus":1}],"conversationId":"715f8b13-96ee-cd36-cb07-5a982a219982"}';
|
||||||
|
await hubConnection.invoke("AddChatUserAsync", args: <Object>[json.decode(chatData)]);
|
||||||
|
userChatHistory.add(
|
||||||
|
SingleUserChatModel(
|
||||||
|
chatEventId: 1,
|
||||||
|
chatSource: 1,
|
||||||
|
contant: chatMessage,
|
||||||
|
contantNo: "8a129295-36d7-7185-5d34-cc4eec7bcba4",
|
||||||
|
conversationId: "715f8b13-96ee-cd36-cb07-5a982a219982",
|
||||||
|
createdDate: DateTime.now(),
|
||||||
|
currentUserId: 42062,
|
||||||
|
currentUserName: "aamir.muhammad",
|
||||||
|
targetUserId: targetUserId,
|
||||||
|
targetUserName: targetUserName,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
message.clear();
|
||||||
|
notifyListeners();
|
||||||
|
scrollDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
void scrollDown() {
|
||||||
|
scrollController.animateTo(
|
||||||
|
scrollController.position.maxScrollExtent + 100,
|
||||||
|
curve: Curves.easeOut,
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
);
|
||||||
|
|
||||||
|
// scrollController.animateTo(double.parse(userChatHistory.length.toString()), duration: Duration(milliseconds: 500), curve: Curves.fastOutSlowIn);
|
||||||
|
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
// void _scrollListener() {
|
||||||
|
// if (scrollController.position.extentAfter.toInt() <= 0 && canCallApi) {
|
||||||
|
// if (userChatHistory.length < _ayatTangheemTypeMapped.totalItemsCount) {
|
||||||
|
// currentPageNo++;
|
||||||
|
// if (widget.tangheemQuery == null) {
|
||||||
|
// getTangheemData();
|
||||||
|
// } else {
|
||||||
|
// getTangheemDataByKeyword();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// canCallApi = false;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
class ChatUserModel {
|
||||||
|
ChatUserModel({
|
||||||
|
this.response,
|
||||||
|
this.errorResponses,
|
||||||
|
});
|
||||||
|
|
||||||
|
List<ChatUser>? response;
|
||||||
|
dynamic errorResponses;
|
||||||
|
|
||||||
|
factory ChatUserModel.fromJson(Map<String, dynamic> json) => ChatUserModel(
|
||||||
|
response: json["response"] == null ? null : List<ChatUser>.from(json["response"].map((x) => ChatUser.fromJson(x))),
|
||||||
|
errorResponses: json["errorResponses"],
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
"response": response == null ? null : List<dynamic>.from(response!.map((x) => x.toJson())),
|
||||||
|
"errorResponses": errorResponses,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class ChatUser {
|
||||||
|
ChatUser({
|
||||||
|
this.id,
|
||||||
|
this.userName,
|
||||||
|
this.email,
|
||||||
|
this.phone,
|
||||||
|
this.title,
|
||||||
|
this.userStatus,
|
||||||
|
this.image,
|
||||||
|
this.unreadMessageCount,
|
||||||
|
this.userAction,
|
||||||
|
this.isPin,
|
||||||
|
this.isFav,
|
||||||
|
this.isAdmin,
|
||||||
|
this.isTyping,
|
||||||
|
});
|
||||||
|
|
||||||
|
int? id;
|
||||||
|
String? userName;
|
||||||
|
String? email;
|
||||||
|
dynamic? phone;
|
||||||
|
dynamic? title;
|
||||||
|
int? userStatus;
|
||||||
|
dynamic? image;
|
||||||
|
int? unreadMessageCount;
|
||||||
|
dynamic? userAction;
|
||||||
|
bool? isPin;
|
||||||
|
bool? isFav;
|
||||||
|
bool? isAdmin;
|
||||||
|
bool? isTyping;
|
||||||
|
|
||||||
|
factory ChatUser.fromJson(Map<String, dynamic> json) => ChatUser(
|
||||||
|
id: json["id"] == null ? null : json["id"],
|
||||||
|
userName: json["userName"] == null ? null : json["userName"],
|
||||||
|
email: json["email"] == null ? null : json["email"],
|
||||||
|
phone: json["phone"],
|
||||||
|
title: json["title"],
|
||||||
|
userStatus: json["userStatus"] == null ? null : json["userStatus"],
|
||||||
|
image: json["image"],
|
||||||
|
unreadMessageCount: json["unreadMessageCount"] == null ? null : json["unreadMessageCount"],
|
||||||
|
userAction: json["userAction"],
|
||||||
|
isPin: json["isPin"] == null ? null : json["isPin"],
|
||||||
|
isFav: json["isFav"] == null ? null : json["isFav"],
|
||||||
|
isAdmin: json["isAdmin"] == null ? null : json["isAdmin"],
|
||||||
|
isTyping: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
"id": id == null ? null : id,
|
||||||
|
"userName": userName == null ? null : userName,
|
||||||
|
"email": email == null ? null : email,
|
||||||
|
"phone": phone,
|
||||||
|
"title": title,
|
||||||
|
"userStatus": userStatus == null ? null : userStatus,
|
||||||
|
"image": image,
|
||||||
|
"unreadMessageCount": unreadMessageCount == null ? null : unreadMessageCount,
|
||||||
|
"userAction": userAction,
|
||||||
|
"isPin": isPin == null ? null : isPin,
|
||||||
|
"isFav": isFav == null ? null : isFav,
|
||||||
|
"isAdmin": isAdmin == null ? null : isAdmin,
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,119 @@
|
|||||||
|
class SingleUserChatModel {
|
||||||
|
SingleUserChatModel({
|
||||||
|
this.userChatHistoryId,
|
||||||
|
this.userChatHistoryLineId,
|
||||||
|
this.contant,
|
||||||
|
this.contantNo,
|
||||||
|
this.currentUserId,
|
||||||
|
this.currentUserName,
|
||||||
|
this.targetUserId,
|
||||||
|
this.targetUserName,
|
||||||
|
this.encryptedTargetUserId,
|
||||||
|
this.encryptedTargetUserName,
|
||||||
|
this.chatEventId,
|
||||||
|
this.fileTypeId,
|
||||||
|
this.isSeen,
|
||||||
|
this.isDelivered,
|
||||||
|
this.createdDate,
|
||||||
|
this.chatSource,
|
||||||
|
this.conversationId,
|
||||||
|
this.fileTypeResponse,
|
||||||
|
this.userChatReplyResponse,
|
||||||
|
});
|
||||||
|
|
||||||
|
int? userChatHistoryId;
|
||||||
|
int? userChatHistoryLineId;
|
||||||
|
String? contant;
|
||||||
|
String? contantNo;
|
||||||
|
int? currentUserId;
|
||||||
|
String? currentUserName;
|
||||||
|
int? targetUserId;
|
||||||
|
String? targetUserName;
|
||||||
|
dynamic encryptedTargetUserId;
|
||||||
|
dynamic encryptedTargetUserName;
|
||||||
|
int? chatEventId;
|
||||||
|
dynamic fileTypeId;
|
||||||
|
bool? isSeen;
|
||||||
|
bool? isDelivered;
|
||||||
|
DateTime? createdDate;
|
||||||
|
int? chatSource;
|
||||||
|
String? conversationId;
|
||||||
|
FileTypeResponse? fileTypeResponse;
|
||||||
|
dynamic userChatReplyResponse;
|
||||||
|
|
||||||
|
factory SingleUserChatModel.fromJson(Map<String, dynamic> json) => SingleUserChatModel(
|
||||||
|
userChatHistoryId: json["userChatHistoryId"] == null ? null : json["userChatHistoryId"],
|
||||||
|
userChatHistoryLineId: json["userChatHistoryLineId"] == null ? null : json["userChatHistoryLineId"],
|
||||||
|
contant: json["contant"] == null ? null : json["contant"],
|
||||||
|
contantNo: json["contantNo"] == null ? null : json["contantNo"],
|
||||||
|
currentUserId: json["currentUserId"] == null ? null : json["currentUserId"],
|
||||||
|
currentUserName: json["currentUserName"] == null ? null : json["currentUserName"],
|
||||||
|
targetUserId: json["targetUserId"] == null ? null : json["targetUserId"],
|
||||||
|
targetUserName: json["targetUserName"] == null ? null : json["targetUserName"],
|
||||||
|
encryptedTargetUserId: json["encryptedTargetUserId"],
|
||||||
|
encryptedTargetUserName: json["encryptedTargetUserName"],
|
||||||
|
chatEventId: json["chatEventId"] == null ? null : json["chatEventId"],
|
||||||
|
fileTypeId: json["fileTypeId"],
|
||||||
|
isSeen: json["isSeen"] == null ? null : json["isSeen"],
|
||||||
|
isDelivered: json["isDelivered"] == null ? null : json["isDelivered"],
|
||||||
|
createdDate: json["createdDate"] == null ? null : DateTime.parse(json["createdDate"]),
|
||||||
|
chatSource: json["chatSource"] == null ? null : json["chatSource"],
|
||||||
|
conversationId: json["conversationId"] == null ? null : json["conversationId"],
|
||||||
|
fileTypeResponse: json["fileTypeResponse"] == null ? null : FileTypeResponse.fromJson(json["fileTypeResponse"]),
|
||||||
|
userChatReplyResponse: json["userChatReplyResponse"],
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
"userChatHistoryId": userChatHistoryId == null ? null : userChatHistoryId,
|
||||||
|
"userChatHistoryLineId": userChatHistoryLineId == null ? null : userChatHistoryLineId,
|
||||||
|
"contant": contant == null ? null : contant,
|
||||||
|
"contantNo": contantNo == null ? null : contantNo,
|
||||||
|
"currentUserId": currentUserId == null ? null : currentUserId,
|
||||||
|
"currentUserName": currentUserName == null ? null : currentUserName,
|
||||||
|
"targetUserId": targetUserId == null ? null : targetUserId,
|
||||||
|
"targetUserName": targetUserName == null ? null : targetUserName,
|
||||||
|
"encryptedTargetUserId": encryptedTargetUserId,
|
||||||
|
"encryptedTargetUserName": encryptedTargetUserName,
|
||||||
|
"chatEventId": chatEventId == null ? null : chatEventId,
|
||||||
|
"fileTypeId": fileTypeId,
|
||||||
|
"isSeen": isSeen == null ? null : isSeen,
|
||||||
|
"isDelivered": isDelivered == null ? null : isDelivered,
|
||||||
|
"createdDate": createdDate == null ? null : createdDate!.toIso8601String(),
|
||||||
|
"chatSource": chatSource == null ? null : chatSource,
|
||||||
|
"conversationId": conversationId == null ? null : conversationId,
|
||||||
|
"fileTypeResponse": fileTypeResponse == null ? null : fileTypeResponse!.toJson(),
|
||||||
|
"userChatReplyResponse": userChatReplyResponse,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class FileTypeResponse {
|
||||||
|
FileTypeResponse({
|
||||||
|
this.fileTypeId,
|
||||||
|
this.fileTypeName,
|
||||||
|
this.fileTypeDescription,
|
||||||
|
this.fileKind,
|
||||||
|
this.fileName,
|
||||||
|
});
|
||||||
|
|
||||||
|
int? fileTypeId;
|
||||||
|
dynamic fileTypeName;
|
||||||
|
dynamic fileTypeDescription;
|
||||||
|
dynamic fileKind;
|
||||||
|
dynamic fileName;
|
||||||
|
|
||||||
|
factory FileTypeResponse.fromJson(Map<String, dynamic> json) => FileTypeResponse(
|
||||||
|
fileTypeId: json["fileTypeId"] == null ? null : json["fileTypeId"],
|
||||||
|
fileTypeName: json["fileTypeName"],
|
||||||
|
fileTypeDescription: json["fileTypeDescription"],
|
||||||
|
fileKind: json["fileKind"],
|
||||||
|
fileName: json["fileName"],
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
"fileTypeId": fileTypeId == null ? null : fileTypeId,
|
||||||
|
"fileTypeName": fileTypeName,
|
||||||
|
"fileTypeDescription": fileTypeDescription,
|
||||||
|
"fileKind": fileKind,
|
||||||
|
"fileName": fileName,
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,88 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:mohem_flutter_app/classes/colors.dart';
|
||||||
|
import 'package:mohem_flutter_app/extensions/int_extensions.dart';
|
||||||
|
import 'package:mohem_flutter_app/extensions/string_extensions.dart';
|
||||||
|
|
||||||
|
class ChatBubble extends StatelessWidget {
|
||||||
|
const ChatBubble(
|
||||||
|
{Key? key,
|
||||||
|
required this.text,
|
||||||
|
required this.isCurrentUser,
|
||||||
|
required this.isSeen,
|
||||||
|
required this.isDelivered,
|
||||||
|
required this.dateTime})
|
||||||
|
: super(key: key);
|
||||||
|
final String text;
|
||||||
|
final bool isCurrentUser;
|
||||||
|
final bool isSeen;
|
||||||
|
final bool isDelivered;
|
||||||
|
final String dateTime;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
// asymmetric padding
|
||||||
|
padding: EdgeInsets.fromLTRB(
|
||||||
|
isCurrentUser ? 64.0 : 16.0,
|
||||||
|
4,
|
||||||
|
isCurrentUser ? 16.0 : 64.0,
|
||||||
|
4,
|
||||||
|
),
|
||||||
|
child: Align(
|
||||||
|
// align the child within the container
|
||||||
|
alignment: isCurrentUser ? Alignment.centerRight : Alignment.centerLeft,
|
||||||
|
child: DecoratedBox(
|
||||||
|
// chat bubble decoration
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
gradient: isCurrentUser
|
||||||
|
? null
|
||||||
|
: LinearGradient(
|
||||||
|
transform: GradientRotation(.46),
|
||||||
|
begin: Alignment.topRight,
|
||||||
|
end: Alignment.bottomLeft,
|
||||||
|
colors: [
|
||||||
|
MyColors.gradiantEndColor,
|
||||||
|
MyColors.gradiantStartColor,
|
||||||
|
]),
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
text.toText12(
|
||||||
|
color:
|
||||||
|
isCurrentUser ? MyColors.grey57Color : MyColors.white),
|
||||||
|
8.height,
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
dateTime.toText12(
|
||||||
|
color: isCurrentUser
|
||||||
|
? MyColors.grey41Color.withOpacity(.5)
|
||||||
|
: Colors.white.withOpacity(0.7)),
|
||||||
|
if (isCurrentUser) 5.width,
|
||||||
|
if (isCurrentUser)
|
||||||
|
Icon(
|
||||||
|
isDelivered
|
||||||
|
? Icons.done_all
|
||||||
|
: Icons.done_all,
|
||||||
|
color: isSeen
|
||||||
|
? MyColors.textMixColor
|
||||||
|
: MyColors.grey9DColor,
|
||||||
|
size: 14,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
import 'package:mohem_flutter_app/api/chat/chat_provider_model.dart';
|
||||||
|
import 'package:mohem_flutter_app/classes/colors.dart';
|
||||||
|
import 'package:mohem_flutter_app/ui/chat/chat_bubble.dart';
|
||||||
|
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
|
||||||
|
import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class ChatDetailScreen extends StatelessWidget {
|
||||||
|
dynamic userDetails;
|
||||||
|
late ChatProviderModel data;
|
||||||
|
|
||||||
|
ChatDetailScreen({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
userDetails = ModalRoute.of(context)!.settings.arguments;
|
||||||
|
data = Provider.of<ChatProviderModel>(context, listen: false);
|
||||||
|
data.getSingleUserChatHistory(senderUID: "42062", receiverUID: userDetails["targetUser"].id, pagination: "0");
|
||||||
|
Timer(const Duration(seconds: 1), () => data.scrollDown());
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: const Color(0xFFF8F8F8),
|
||||||
|
appBar: AppBarWidget(context, title: userDetails["targetUser"].userName, showHomeButton: false, image: userDetails["targetUser"].image),
|
||||||
|
body: Consumer<ChatProviderModel>(
|
||||||
|
builder: (BuildContext context, ChatProviderModel m, Widget? child) {
|
||||||
|
return (m.isLoading
|
||||||
|
? ChatHomeShimmer()
|
||||||
|
: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Expanded(
|
||||||
|
child: ListView.builder(
|
||||||
|
controller: m.scrollController,
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: m.userChatHistory.length,
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||||
|
itemBuilder: (BuildContext context, int i) {
|
||||||
|
return ChatBubble(
|
||||||
|
text: m.userChatHistory[i].contant.toString(),
|
||||||
|
isSeen: m.userChatHistory[i].isSeen == true ? true : false,
|
||||||
|
isCurrentUser: m.userChatHistory[i].currentUserId == 42062 ? true : false,
|
||||||
|
isDelivered: m.userChatHistory[i].currentUserId == 42062 && m.userChatHistory[i].isDelivered == true ? true : false,
|
||||||
|
dateTime: m.userChatHistory[i].createdDate.toString(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Card(
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||||
|
child: TextField(
|
||||||
|
controller: m.message,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: 'Type here to reply',
|
||||||
|
hintStyle: const TextStyle(color: MyColors.grey98Color),
|
||||||
|
border: InputBorder.none,
|
||||||
|
focusedBorder: InputBorder.none,
|
||||||
|
enabledBorder: InputBorder.none,
|
||||||
|
errorBorder: InputBorder.none,
|
||||||
|
disabledBorder: InputBorder.none,
|
||||||
|
contentPadding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
|
||||||
|
suffixIcon: IconButton(
|
||||||
|
icon: SvgPicture.asset(
|
||||||
|
"assets/icons/chat/chat_send_icon.svg",
|
||||||
|
height: 26,
|
||||||
|
width: 35,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
m.sendChatMessage(m.message.text, userDetails["targetUser"].id, userDetails["targetUser"].userName);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,175 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
import 'package:mohem_flutter_app/api/chat/chat_provider_model.dart';
|
||||||
|
import 'package:mohem_flutter_app/classes/colors.dart';
|
||||||
|
import 'package:mohem_flutter_app/config/routes.dart';
|
||||||
|
import 'package:mohem_flutter_app/extensions/string_extensions.dart';
|
||||||
|
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
|
||||||
|
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
|
||||||
|
import 'package:mohem_flutter_app/widgets/bottom_sheet.dart';
|
||||||
|
import 'package:mohem_flutter_app/widgets/bottom_sheets/search_employee_bottom_sheet.dart';
|
||||||
|
import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class ChatHomeScreen extends StatefulWidget {
|
||||||
|
const ChatHomeScreen({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ChatHomeScreen> createState() => _ChatHomeScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ChatHomeScreenState extends State<ChatHomeScreen> {
|
||||||
|
TextEditingController search = new TextEditingController();
|
||||||
|
late ChatProviderModel data;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
data = Provider.of<ChatProviderModel>(context, listen: false);
|
||||||
|
data.buildHubConnection();
|
||||||
|
data.getUserRecentChats();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
appBar: AppBarWidget(context, title: "My Chats", showHomeButton: false),
|
||||||
|
body: Consumer<ChatProviderModel>(builder: (BuildContext context, ChatProviderModel m, Widget? child) {
|
||||||
|
return m.isLoading
|
||||||
|
? ChatHomeShimmer()
|
||||||
|
: ListView(
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 0, horizontal: 20),
|
||||||
|
child: TextField(
|
||||||
|
onChanged: (String val) {
|
||||||
|
m.filter(val);
|
||||||
|
},
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
focusedBorder: InputBorder.none,
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(5),
|
||||||
|
borderSide: const BorderSide(
|
||||||
|
color: Color(0xFFE5E5E5),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
errorBorder: InputBorder.none,
|
||||||
|
disabledBorder: InputBorder.none,
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
|
||||||
|
hintText: "Search from chat",
|
||||||
|
hintStyle: const TextStyle(color: MyColors.lightTextColor, fontStyle: FontStyle.italic),
|
||||||
|
filled: true,
|
||||||
|
fillColor: const Color(0xFFF7F7F7),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (m.searchedChats != null)
|
||||||
|
ListView.separated(
|
||||||
|
itemCount: m.searchedChats!.length,
|
||||||
|
padding: const EdgeInsets.only(top: 0),
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
return ListTile(
|
||||||
|
leading: Stack(
|
||||||
|
children: [
|
||||||
|
SvgPicture.asset(
|
||||||
|
"assets/images/user.svg",
|
||||||
|
height: 48,
|
||||||
|
width: 48,
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
right: 5,
|
||||||
|
bottom: 1,
|
||||||
|
child: Container(
|
||||||
|
width: 10,
|
||||||
|
height: 10,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: m.searchedChats![index].userStatus == 1 ? MyColors.green2DColor : Colors.red,
|
||||||
|
borderRadius: const BorderRadius.all(
|
||||||
|
Radius.circular(10),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
title: (m.searchedChats![index].userName ?? "").toText14(color: MyColors.darkTextColor),
|
||||||
|
subtitle: (m.searchedChats![index].isTyping == true ? "Something is Typing" : "Last message text").toText11(color: MyColors.normalTextColor),
|
||||||
|
trailing: ("Today").toText10(color: MyColors.lightTextColor),
|
||||||
|
minVerticalPadding: 0,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.pushNamed(
|
||||||
|
context,
|
||||||
|
AppRoutes.chatDetailed,
|
||||||
|
arguments: {"currentUser": "42062", "targetUser": m.searchedChats![index]},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
separatorBuilder: (BuildContext context, int index) => const Padding(
|
||||||
|
padding: EdgeInsets.only(right: 10, left: 70),
|
||||||
|
child: Divider(
|
||||||
|
color: Color(0xFFE5E5E5),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// if (searchedUsersList == null) Utils.getNoChatWidget(context),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
floatingActionButton: FloatingActionButton(
|
||||||
|
child: Container(
|
||||||
|
width: 60,
|
||||||
|
height: 60,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
gradient: LinearGradient(
|
||||||
|
transform: GradientRotation(.46),
|
||||||
|
begin: Alignment.topRight,
|
||||||
|
end: Alignment.bottomLeft,
|
||||||
|
colors: [
|
||||||
|
MyColors.gradiantEndColor,
|
||||||
|
MyColors.gradiantStartColor,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: const Icon(
|
||||||
|
Icons.add,
|
||||||
|
size: 30,
|
||||||
|
color: MyColors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onPressed: () async {
|
||||||
|
// var userData = await ChatApiClient()
|
||||||
|
// .getChatMemberFromSearch("aamir.muhammad", 36239);
|
||||||
|
showMyBottomSheet(
|
||||||
|
context,
|
||||||
|
child: SearchEmployeeBottomSheet(
|
||||||
|
title: LocaleKeys.searchForEmployee.tr(),
|
||||||
|
apiMode: LocaleKeys.delegate.tr(),
|
||||||
|
onSelectEmployee: (_selectedEmployee) {
|
||||||
|
// Navigator.pop(context);
|
||||||
|
// selectedReplacementEmployee = _selectedEmployee;
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue