Merge branch 'master' into development_sikander

# Conflicts:
#	lib/classes/consts.dart
#	lib/generated/locale_keys.g.dart
#	lib/ui/landing/dashboard_screen.dart
faiz_marathon_signalR_critical
Sikander Saleem 2 years ago
commit d200624ac2

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

@ -394,7 +394,7 @@
"uploadFromGallery": "تحميل من\nملفات الجهاز",
"name": "الأسم",
"email": "ايميل",
"noHistoryAvailable":"لايوجد سجل بيانات سابقة ",
"noHistoryAvailable": "لايوجد سجل بيانات سابقة ",
"purchaseRequisition": "طلب شراء",
"moveOrder": "طلب تغيير",
"humanResource": "الموارد البشريه",
@ -405,7 +405,7 @@
"addFavoriteList": "هل تريد اضافة {name} لقائمة المفضله",
"feedbackUserExperience": "هذا للحصول على تعليقات حول تجربة المستخدم",
"rateUI": ".1 كيف تريد تقييم التطبيق",
"submitSurvey":"ارسال الاستبيان",
"submitSurvey": "ارسال الاستبيان",
"typeHere": "اكتب هنا",
"info_detail": "تفاصيل المعلومات",
"amount_detail": "تفاصيل المبلغ",
@ -451,5 +451,8 @@
"female": "Hello girl :) {}"
}
},
"reset_locale": "Reset Language"
"reset_locale": "Reset Language",
"chat": "دردشة",
"mychats": "دردشاتي",
"createNewChat": "Create New Chat"
}

@ -451,5 +451,8 @@
"female": "Hello girl :) {}"
}
},
"reset_locale": "Reset Language"
"reset_locale": "Reset Language",
"chat": "Chat",
"mychats": "My Chats",
"createNewChat": "Create New Chat"
}

@ -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.eyJuYW1laWQiOiI0MjA2MiIsImVtYWlsIjoiYWFtaXIubXVoYW1tYWRAY2xvdWRzb2x1dGlvbnMuY29tLnNhIiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy91c2VyZGF0YSI6ImFhbWlyLm11aGFtbWFkIiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvbW9iaWxlcGhvbmUiOiI5NjY1MzA4OTYwMTgiLCJuYmYiOjE2NjU5MjA2NDEsImV4cCI6MTY2NjAwNzA0MSwiaWF0IjoxNjY1OTIwNjQxfQ.70tXWdpXtQ20PNBO3WF9ScWNWSyECpFfrW7_iuOmNfWmA63PCZzlTO0E6I3q3K9Kg2CWvOT9-dSDLjlRuXuC2w";
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;
// }
// }
}

@ -9,6 +9,7 @@ class MyColors {
static const Color gradiantEndColor = Color(0xff259db7);
static const Color textMixColor = Color(0xff2BB8A6);
static const Color backgroundColor = Color(0xffF8F8F8);
static const Color grey41Color = Color(0xff414141);
static const Color grey57Color = Color(0xff575757);
static const Color grey67Color = Color(0xff676767);
static const Color grey77Color = Color(0xff777777);
@ -54,4 +55,5 @@ class MyColors {
static const Color green2DColor = Color(0xff32D892);
static const Color greyC4Color = Color(0xffC4C4C4);
static const Color grey35Color = Color(0xff535353);
static const Color grey9DColor = Color(0xff9D9D9D);
}

@ -9,6 +9,16 @@ class ApiConsts {
static String swpRest = baseUrlServices + "SWP.svc/REST/";
static String user = baseUrlServices + "api/User/";
static String cocRest = baseUrlServices + "COCWS.svc/REST/";
static String chatServerBaseUrl = "https://apiderichat.hmg.com";
static String chatServerBaseApiUrl = "https://apiderichat.hmg.com/api/";
static String chatHubConnectionUrl = chatServerBaseUrl + "/ConnectionChatHub";
static String chatSearchMember = "user/getUserWithStatusAndFavAsync/";
static String chatRecentUrl = "UserChatHistory/getchathistorybyuserid"; //For a Mem
static String chatSingleUserHistoryUrl = "UserChatHistory/GetUserChatHistory";
// 42062 is CurrentUserID and 36745 is targetUserID and 0 is For Pagination
// static String chatSearchMember = "https://apiderichat.hmg.com/api/user/getUserWithStatusAndFavAsync/aamir.muhammad/36239";
}
class SharedPrefsConsts {

@ -137,6 +137,17 @@ class Utils {
).center;
}
static Widget getNoChatWidget(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SvgPicture.asset('assets/images/not_found.svg', width: 110.0, height: 110.0),
LocaleKeys.noDataAvailable.tr().toText16().paddingOnly(top: 15),
],
).center;
}
static Uint8List getPostBytes(img) {
try {
var b64 = img.replaceFirst('data:image/png;base64,', '');

@ -3,6 +3,8 @@ import 'package:mohem_flutter_app/ui/attendance/add_vacation_rule_screen.dart';
import 'package:mohem_flutter_app/ui/attendance/monthly_attendance_screen.dart';
import 'package:mohem_flutter_app/ui/attendance/vacation_rule_screen.dart';
import 'package:mohem_flutter_app/ui/bottom_sheets/attendence_details_bottom_sheet.dart';
import 'package:mohem_flutter_app/ui/chat/chat_detailed_screen.dart';
import 'package:mohem_flutter_app/ui/chat/chat_home.dart';
import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart';
import 'package:mohem_flutter_app/ui/landing/survey_screen.dart';
import 'package:mohem_flutter_app/ui/landing/today_attendance_screen.dart';
@ -166,6 +168,10 @@ class AppRoutes {
static const String changePassword = "/changePassword";
//Chat
static const String chat = "/chat";
static const String chatDetailed = "/chatDetailed";
static final Map<String, WidgetBuilder> routes = {
login: (context) => LoginScreen(),
verifyLogin: (context) => VerifyLoginScreen(),
@ -262,5 +268,9 @@ class AppRoutes {
subordinateLeave: (context) => SubordinateLeave(),
changePassword: (context) => ChangePasswordScreen(),
//Chat
chat: (context) => ChatHomeScreen(),
chatDetailed: (context) => ChatDetailScreen(),
};
}

@ -438,4 +438,8 @@ abstract class LocaleKeys {
static const gender_with_arg = 'gender.with_arg';
static const gender = 'gender';
static const reset_locale = 'reset_locale';
static const chat = 'chat';
static const mychats = 'mychats';
static const createNewChat = 'createNewChat';
}

@ -3,6 +3,7 @@ import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:logger/logger.dart';
import 'package:mohem_flutter_app/api/chat/chat_provider_model.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/config/routes.dart';
import 'package:mohem_flutter_app/generated/codegen_loader.g.dart';
@ -43,6 +44,7 @@ Future<void> main() async {
providers: [
ChangeNotifierProvider(create: (_) => DashboardProviderModel()),
ChangeNotifierProvider(create: (_) => EITProviderModel()),
ChangeNotifierProvider(create: (_) => ChatProviderModel())
],
child: MyApp(),
),

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

@ -414,7 +414,16 @@ class _DashboardScreenState extends State<DashboardScreen> {
"assets/icons/item_for_sale.svg",
color: currentIndex == 3 ? MyColors.grey3AColor : MyColors.grey98Color,
).paddingAll(4),
label: LocaleKeys.itemsForSale.tr(),
label: LocaleKeys.chat.tr(),
),
BottomNavigationBarItem(
icon: SvgPicture.asset(
"assets/icons/chat/chat.svg",
color: currentIndex == 4
? MyColors.grey3AColor
: MyColors.grey98Color,
).paddingAll(4),
label: LocaleKeys.chat.tr(),
),
],
currentIndex: currentIndex,
@ -432,6 +441,8 @@ class _DashboardScreenState extends State<DashboardScreen> {
Navigator.pushNamed(context, AppRoutes.workList);
} else if (index == 3) {
Navigator.pushNamed(context, AppRoutes.itemsForSale);
}else if (index == 4) {
Navigator.pushNamed(context, AppRoutes.chat);
}
},
),

@ -1,11 +1,17 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/config/routes.dart';
import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart';
import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
AppBar AppBarWidget(BuildContext context, {required String title, bool showHomeButton = true, bool showNotificationButton = false, bool showMemberButton = false}) {
AppBar AppBarWidget(BuildContext context,
{required String title,
bool showHomeButton = true,
bool showNotificationButton = false,
bool showMemberButton = false,
String? image}) {
return AppBar(
leadingWidth: 0,
// leading: GestureDetector(
@ -18,10 +24,18 @@ AppBar AppBarWidget(BuildContext context, {required String title, bool showHomeB
children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: Feedback.wrapForTap(() => Navigator.maybePop(context), context),
child: const Icon(Icons.arrow_back_ios, color: MyColors.darkIconColor),
onTap:
Feedback.wrapForTap(() => Navigator.maybePop(context), context),
child:
const Icon(Icons.arrow_back_ios, color: MyColors.darkIconColor),
),
4.width,
if (image != null) SvgPicture.asset(
image,
height: 40,
width: 40,
),
if (image != null) 14.width,
title.toText24(color: MyColors.darkTextColor, isBold: true).expanded,
],
),
@ -32,7 +46,8 @@ AppBar AppBarWidget(BuildContext context, {required String title, bool showHomeB
if (showHomeButton)
IconButton(
onPressed: () {
Navigator.popUntil(context, ModalRoute.withName(AppRoutes.dashboard));
Navigator.popUntil(
context, ModalRoute.withName(AppRoutes.dashboard));
},
icon: const Icon(Icons.home, color: MyColors.darkIconColor),
),

@ -22,10 +22,16 @@ class SearchEmployeeBottomSheet extends StatefulWidget {
List<GetActionHistoryList>? actionHistoryList;
Function(ReplacementList) onSelectEmployee;
SearchEmployeeBottomSheet({required this.title, required this.apiMode, this.notificationID, this.actionHistoryList, required this.onSelectEmployee});
SearchEmployeeBottomSheet(
{required this.title,
required this.apiMode,
this.notificationID,
this.actionHistoryList,
required this.onSelectEmployee});
@override
State<SearchEmployeeBottomSheet> createState() => _SearchEmployeeBottomSheetState();
State<SearchEmployeeBottomSheet> createState() =>
_SearchEmployeeBottomSheetState();
}
class _SearchEmployeeBottomSheetState extends State<SearchEmployeeBottomSheet> {
@ -53,8 +59,12 @@ class _SearchEmployeeBottomSheetState extends State<SearchEmployeeBottomSheet> {
userId: _selectedSearchIndex == 1 ? searchText : "",
email: _selectedSearchIndex == 2 ? searchText : "",
);
favouriteUserList = replacementList?.where((element) => element.isFavorite ?? false).toList();
nonFavouriteUserList = replacementList?.where((element) => !(element.isFavorite ?? false)).toList();
favouriteUserList = replacementList
?.where((element) => element.isFavorite ?? false)
.toList();
nonFavouriteUserList = replacementList
?.where((element) => !(element.isFavorite ?? false))
.toList();
Utils.hideLoading(context);
setState(() {});
} catch (e) {
@ -104,7 +114,8 @@ class _SearchEmployeeBottomSheetState extends State<SearchEmployeeBottomSheet> {
IconButton(
constraints: const BoxConstraints(),
onPressed: () async {
await SystemChannels.textInput.invokeMethod('TextInput.hide');
await SystemChannels.textInput
.invokeMethod('TextInput.hide');
fetchUserByInput();
},
icon: Icon(Icons.search))
@ -123,7 +134,8 @@ class _SearchEmployeeBottomSheetState extends State<SearchEmployeeBottomSheet> {
ListView.separated(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: (cxt, index) => employeeItemView(favouriteUserList![index]),
itemBuilder: (cxt, index) =>
employeeItemView(favouriteUserList![index]),
separatorBuilder: (cxt, index) => Container(
height: 1,
color: MyColors.borderE3Color,
@ -137,7 +149,8 @@ class _SearchEmployeeBottomSheetState extends State<SearchEmployeeBottomSheet> {
ListView.separated(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: (cxt, index) => employeeItemView(nonFavouriteUserList![index]),
itemBuilder: (cxt, index) => employeeItemView(
nonFavouriteUserList![index]),
separatorBuilder: (cxt, index) => Container(
height: 1,
color: MyColors.borderE3Color,
@ -148,7 +161,10 @@ class _SearchEmployeeBottomSheetState extends State<SearchEmployeeBottomSheet> {
).expanded
],
).paddingOnly(left: 21, right: 21, bottom: 0, top: 21).expanded,
Container(width: double.infinity, height: 1, color: MyColors.lightGreyEFColor),
Container(
width: double.infinity,
height: 1,
color: MyColors.lightGreyEFColor),
DefaultButton(
LocaleKeys.cancel.tr(),
() {
@ -185,7 +201,11 @@ class _SearchEmployeeBottomSheetState extends State<SearchEmployeeBottomSheet> {
Expanded(
child: (replacement.employeeDisplayName ?? "").toText12(),
),
Icon(Icons.star, size: 16, color: replacement.isFavorite! ? MyColors.yellowFavColor : MyColors.borderCEColor),
Icon(Icons.star,
size: 16,
color: replacement.isFavorite!
? MyColors.yellowFavColor
: MyColors.borderCEColor),
],
),
),
@ -208,7 +228,9 @@ class _SearchEmployeeBottomSheetState extends State<SearchEmployeeBottomSheet> {
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration(
color: value == groupValue ? MyColors.grey3AColor : Colors.transparent,
color: value == groupValue
? MyColors.grey3AColor
: Colors.transparent,
borderRadius: BorderRadius.all(const Radius.circular(100)),
),
),

@ -187,3 +187,70 @@ class ServicesMenuShimmer extends StatelessWidget {
);
}
}
class ChatHomeShimmer extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0),
child: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Expanded(
child: Shimmer.fromColors(
baseColor: Colors.white,
highlightColor: Colors.grey.shade100,
child: ListView.builder(
itemBuilder: (_, __) => Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
width: 48.0,
height: 48.0,
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(40))),
),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 8.0),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
width: double.infinity,
height: 8.0,
color: Colors.white,
),
const Padding(
padding: EdgeInsets.symmetric(vertical: 2.0),
),
Container(
width: double.infinity,
height: 8.0,
color: Colors.white,
),
const Padding(
padding: EdgeInsets.symmetric(vertical: 2.0),
),
Container(
width: 40.0,
height: 8.0,
color: Colors.white,
),
],
),
)
],
),
),
itemCount: 6,
),
),
),
],
));
}
}

@ -78,6 +78,12 @@ dependencies:
auto_size_text: ^3.0.0
pull_to_refresh: ^2.0.0
#Chat
signalr_netcore: ^1.3.3
logging: ^1.0.1
dev_dependencies:
flutter_test:
sdk: flutter
@ -110,6 +116,7 @@ flutter:
- assets/icons/
- assets/images/
- assets/images/login/
- assets/icons/chat/
- assets/images/logos/
- assets/images/drawer/
- assets/icons/nfc/ic_nfc.png

Loading…
Cancel
Save