Chat Fixes & Favorite Api Implementation

fatima
Aamir Muhammad 2 years ago
parent 77a56759dc
commit 8ba3dcf43b

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<path style="fill:#E2E5E7;" d="M128,0c-17.6,0-32,14.4-32,32v448c0,17.6,14.4,32,32,32h320c17.6,0,32-14.4,32-32V128L352,0H128z"/>
<path style="fill:#B0B7BD;" d="M384,128h96L352,0v96C352,113.6,366.4,128,384,128z"/>
<polygon style="fill:#CAD1D8;" points="480,224 384,128 480,128 "/>
<path style="fill:#50BEE8;" d="M416,416c0,8.8-7.2,16-16,16H48c-8.8,0-16-7.2-16-16V256c0-8.8,7.2-16,16-16h352c8.8,0,16,7.2,16,16
V416z"/>
<g>
<path style="fill:#FFFFFF;" d="M92.576,384c-4.224,0-8.832-2.32-8.832-7.936v-72.656c0-4.608,4.608-7.936,8.832-7.936h29.296
c58.464,0,57.168,88.528,1.136,88.528H92.576z M100.64,311.072v57.312h21.232c34.544,0,36.064-57.312,0-57.312H100.64z"/>
<path style="fill:#FFFFFF;" d="M228,385.28c-23.664,1.024-48.24-14.72-48.24-46.064c0-31.472,24.56-46.944,48.24-46.944
c22.384,1.136,45.792,16.624,45.792,46.944C273.792,369.552,250.384,385.28,228,385.28z M226.592,308.912
c-14.336,0-29.936,10.112-29.936,30.32c0,20.096,15.616,30.336,29.936,30.336c14.72,0,30.448-10.24,30.448-30.336
C257.04,319.008,241.312,308.912,226.592,308.912z"/>
<path style="fill:#FFFFFF;" d="M288.848,339.088c0-24.688,15.488-45.92,44.912-45.92c11.136,0,19.968,3.328,29.296,11.392
c3.456,3.184,3.84,8.816,0.384,12.4c-3.456,3.056-8.704,2.688-11.776-0.384c-5.232-5.504-10.608-7.024-17.904-7.024
c-19.696,0-29.152,13.952-29.152,29.552c0,15.872,9.328,30.448,29.152,30.448c7.296,0,14.08-2.96,19.968-8.192
c3.952-3.072,9.456-1.552,11.76,1.536c2.048,2.816,3.056,7.552-1.408,12.016c-8.96,8.336-19.696,10-30.336,10
C302.8,384.912,288.848,363.776,288.848,339.088z"/>
</g>
<path style="fill:#CAD1D8;" d="M400,432H96v16h304c8.8,0,16-7.2,16-16v-16C416,424.8,408.8,432,400,432z"/>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<path style="fill:#E2E5E7;" d="M128,0c-17.6,0-32,14.4-32,32v448c0,17.6,14.4,32,32,32h320c17.6,0,32-14.4,32-32V128L352,0H128z"/>
<path style="fill:#B0B7BD;" d="M384,128h96L352,0v96C352,113.6,366.4,128,384,128z"/>
<polygon style="fill:#CAD1D8;" points="480,224 384,128 480,128 "/>
<path style="fill:#F15642;" d="M416,416c0,8.8-7.2,16-16,16H48c-8.8,0-16-7.2-16-16V256c0-8.8,7.2-16,16-16h352c8.8,0,16,7.2,16,16
V416z"/>
<g>
<path style="fill:#FFFFFF;" d="M105.456,303.152c0-4.224,3.328-8.832,8.688-8.832h29.552c16.64,0,31.616,11.136,31.616,32.48
c0,20.224-14.976,31.488-31.616,31.488h-21.36v16.896c0,5.632-3.568,8.816-8.176,8.816c-4.224,0-8.688-3.184-8.688-8.816v-72.032
H105.456z M122.336,310.432v31.872h21.36c8.576,0,15.36-7.568,15.36-15.504c0-8.944-6.784-16.368-15.36-16.368H122.336z"/>
<path style="fill:#FFFFFF;" d="M191.616,303.152c0-4.224,3.328-8.832,8.704-8.832h29.552c16.64,0,31.616,11.136,31.616,32.48
c0,20.224-14.976,31.488-31.616,31.488h-21.36v16.896c0,5.632-3.584,8.816-8.192,8.816c-4.224,0-8.704-3.184-8.704-8.816V303.152z
M208.496,310.432v31.872h21.36c8.576,0,15.36-7.568,15.36-15.504c0-8.944-6.784-16.368-15.36-16.368H208.496z"/>
<path style="fill:#FFFFFF;" d="M301.68,311.472h-22.368c-11.136,0-11.136-16.368,0-16.368h60.496c11.392,0,11.392,16.368,0,16.368
h-21.232v64.608c0,11.12-16.896,11.392-16.896,0V311.472z"/>
</g>
<path style="fill:#CAD1D8;" d="M400,432H96v16h304c8.8,0,16-7.2,16-16v-16C416,424.8,408.8,432,400,432z"/>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<path style="fill:#E2E5E7;" d="M128,0c-17.6,0-32,14.4-32,32v448c0,17.6,14.4,32,32,32h320c17.6,0,32-14.4,32-32V128L352,0H128z"/>
<path style="fill:#B0B7BD;" d="M384,128h96L352,0v96C352,113.6,366.4,128,384,128z"/>
<polygon style="fill:#CAD1D8;" points="480,224 384,128 480,128 "/>
<path style="fill:#576D7E;" d="M416,416c0,8.8-7.2,16-16,16H48c-8.8,0-16-7.2-16-16V256c0-8.8,7.2-16,16-16h352c8.8,0,16,7.2,16,16
V416z"/>
<g>
<path style="fill:#FFFFFF;" d="M132.784,311.472H110.4c-11.136,0-11.136-16.368,0-16.368h60.512c11.392,0,11.392,16.368,0,16.368
h-21.248v64.592c0,11.12-16.896,11.392-16.896,0v-64.592H132.784z"/>
<path style="fill:#FFFFFF;" d="M224.416,326.176l22.272-27.888c6.656-8.688,19.568,2.432,12.288,10.752
c-7.68,9.088-15.728,18.944-23.424,29.024l26.112,32.496c7.024,9.6-7.04,18.816-13.952,9.344l-23.536-30.192l-23.152,30.832
c-6.528,9.328-20.992-1.152-13.68-9.856l25.696-32.624c-8.048-10.096-15.856-19.936-23.664-29.024
c-8.064-9.6,6.912-19.44,12.784-10.48L224.416,326.176z"/>
<path style="fill:#FFFFFF;" d="M298.288,311.472H275.92c-11.136,0-11.136-16.368,0-16.368h60.496c11.392,0,11.392,16.368,0,16.368
h-21.232v64.592c0,11.12-16.896,11.392-16.896,0V311.472z"/>
</g>
<path style="fill:#CAD1D8;" d="M400,432H96v16h304c8.8,0,16-7.2,16-16v-16C416,424.8,408.8,432,400,432z"/>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<path style="fill:#E2E5E7;" d="M128,0c-17.6,0-32,14.4-32,32v448c0,17.6,14.4,32,32,32h320c17.6,0,32-14.4,32-32V128L352,0H128z"/>
<path style="fill:#B0B7BD;" d="M384,128h96L352,0v96C352,113.6,366.4,128,384,128z"/>
<polygon style="fill:#CAD1D8;" points="480,224 384,128 480,128 "/>
<path style="fill:#84BD5A;" d="M416,416c0,8.8-7.2,16-16,16H48c-8.8,0-16-7.2-16-16V256c0-8.8,7.2-16,16-16h352c8.8,0,16,7.2,16,16
V416z"/>
<g>
<path style="fill:#FFFFFF;" d="M144.336,326.192l22.256-27.888c6.656-8.704,19.584,2.416,12.288,10.736
c-7.664,9.088-15.728,18.944-23.408,29.04l26.096,32.496c7.04,9.6-7.024,18.8-13.936,9.328l-23.552-30.192l-23.152,30.848
c-6.528,9.328-20.992-1.152-13.696-9.856l25.712-32.624c-8.064-10.112-15.872-19.952-23.664-29.04
c-8.048-9.6,6.912-19.44,12.8-10.464L144.336,326.192z"/>
<path style="fill:#FFFFFF;" d="M197.36,303.152c0-4.224,3.584-7.808,8.064-7.808c4.096,0,7.552,3.6,7.552,7.808v64.096h34.8
c12.528,0,12.8,16.752,0,16.752H205.44c-4.48,0-8.064-3.184-8.064-7.792v-73.056H197.36z"/>
<path style="fill:#FFFFFF;" d="M272.032,314.672c2.944-24.832,40.416-29.296,58.08-15.728c8.704,7.024-0.512,18.16-8.192,12.528
c-9.472-6-30.96-8.816-33.648,4.464c-3.456,20.992,52.192,8.976,51.296,43.008c-0.896,32.496-47.968,33.248-65.632,18.672
c-4.24-3.456-4.096-9.072-1.792-12.544c3.328-3.312,7.024-4.464,11.392-0.88c10.48,7.152,37.488,12.528,39.392-5.648
C321.28,339.632,268.064,351.008,272.032,314.672z"/>
</g>
<path style="fill:#CAD1D8;" d="M400,432H96v16h304c8.8,0,16-7.2,16-16v-16C416,424.8,408.8,432,400,432z"/>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<path style="fill:#E2E5E7;" d="M128,0c-17.6,0-32,14.4-32,32v448c0,17.6,14.4,32,32,32h320c17.6,0,32-14.4,32-32V128L352,0H128z"/>
<path style="fill:#B0B7BD;" d="M384,128h96L352,0v96C352,113.6,366.4,128,384,128z"/>
<polygon style="fill:#CAD1D8;" points="480,224 384,128 480,128 "/>
<path style="fill:#84BD5A;" d="M416,416c0,8.8-7.2,16-16,16H48c-8.8,0-16-7.2-16-16V256c0-8.8,7.2-16,16-16h352c8.8,0,16,7.2,16,16
V416z"/>
<g>
<path style="fill:#FFFFFF;" d="M132.64,384c-8.064,0-11.264-7.792-6.656-13.296l45.552-60.512h-37.76
c-11.12,0-10.224-15.712,0-15.712h51.568c9.712,0,12.528,9.184,5.632,16.624l-43.632,56.656h41.584
c10.24,0,11.52,16.256-1.008,16.256h-55.28V384z"/>
<path style="fill:#FFFFFF;" d="M212.048,303.152c0-10.496,16.896-10.88,16.896,0v73.04c0,10.608-16.896,10.88-16.896,0V303.152z"/>
<path style="fill:#FFFFFF;" d="M251.616,303.152c0-4.224,3.328-8.832,8.704-8.832h29.552c16.64,0,31.616,11.136,31.616,32.48
c0,20.224-14.976,31.488-31.616,31.488h-21.36v16.896c0,5.632-3.584,8.816-8.192,8.816c-4.224,0-8.704-3.184-8.704-8.816
L251.616,303.152L251.616,303.152z M268.496,310.432v31.872h21.36c8.576,0,15.36-7.568,15.36-15.504
c0-8.944-6.784-16.368-15.36-16.368H268.496z"/>
</g>
<path style="fill:#CAD1D8;" d="M400,432H96v16h304c8.8,0,16-7.2,16-16v-16C416,424.8,408.8,432,400,432z"/>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

@ -1,8 +1,9 @@
import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'package:easy_localization/easy_localization.dart';
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';
@ -10,11 +11,14 @@ import 'package:mohem_flutter_app/app_state/app_state.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:mohem_flutter_app/models/chat/get_single_user_chat_list_model.dart';
import 'package:mohem_flutter_app/models/chat/get_user_login_token_model.dart' as login;
import 'package:shared_preferences/shared_preferences.dart';
import 'package:mohem_flutter_app/models/chat/make_user_favotire_unfavorite_chat_model.dart' as fav;
import 'package:mohem_flutter_app/widgets/image_picker.dart';
import 'package:path_provider/path_provider.dart';
import 'package:signalr_netcore/signalr_client.dart';
import 'package:logger/logger.dart' as L;
import 'package:uuid/uuid.dart';
class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
List<SingleUserChatModel> userChatHistory = [];
@ -22,8 +26,14 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
late HubConnection hubConnection;
L.Logger logger = L.Logger();
TextEditingController message = TextEditingController();
ScrollController scrollController = ScrollController();
bool isLoading = true;
bool isChatScreenActive = false;
late File selectedFile;
bool isFileSelected = false;
String sFileType = "";
bool isMsgReply = false;
List<SingleUserChatModel> repliedMsg = [];
int paginationVal = 0;
Future<void> getUserAutoLoginToken() async {
String userName = AppState().memberInformationList!.eMPLOYEEEMAILADDRESS!.split("@").first.toString();
@ -40,9 +50,6 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
token: AppState().chatDetails!.response!.token,
);
return searchUserJsonModel(response.body);
logger.d(response.body);
isLoading = false;
notifyListeners();
}
List<ChatUser> searchUserJsonModel(String str) => List<ChatUser>.from(json.decode(str).map((x) => ChatUser.fromJson(x)));
@ -52,26 +59,53 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
"${ApiConsts.chatServerBaseApiUrl}${ApiConsts.chatRecentUrl}",
token: AppState().chatDetails!.response!.token,
);
logger.d(AppState().chatDetails!.response!.token);
ChatUserModel recentChat = userToList(response.body);
Response favRes = await ApiClient().getJsonForResponse(
"${ApiConsts.chatServerBaseApiUrl}${ApiConsts.chatFavoriteUsers}${AppState().chatDetails!.response!.id}",
token: AppState().chatDetails!.response!.token,
);
print("============================== Fav Response =====================================");
ChatUserModel favUsersList = userToList(favRes.body);
for (var user in recentChat.response!) {
for (var favUser in favUsersList.response!) {
logger.d(favUser.isFav);
if (user.id == favUser.id) {
user.isFav = favUser.isFav;
}
}
}
pChatHistory = recentChat.response;
searchedChats = pChatHistory;
isLoading = false;
notifyListeners();
}
void getSingleUserChatHistory({required String senderUID, required int receiverUID, required String pagination}) async {
void getSingleUserChatHistory({required String senderUID, required int receiverUID, required bool loadMore}) async {
isLoading = true;
if (!loadMore) paginationVal = 0;
logger.d(paginationVal);
isChatScreenActive = true;
Response response = await ApiClient().getJsonForResponse(
"${ApiConsts.chatServerBaseApiUrl}${ApiConsts.chatSingleUserHistoryUrl}/$senderUID/$receiverUID/$pagination",
"${ApiConsts.chatServerBaseApiUrl}${ApiConsts.chatSingleUserHistoryUrl}/$senderUID/$receiverUID/$paginationVal",
token: AppState().chatDetails!.response!.token,
);
logger.d(response.statusCode);
print(response.body);
logger.d(response.body);
if (response.statusCode == 204) {
userChatHistory = [];
if (!loadMore) userChatHistory = [];
Utils.showToast("No More Data To Load");
} else {
if (loadMore) {
List<SingleUserChatModel> temp = getSingleUserChatModel(response.body);
userChatHistory.insertAll(0, temp);
} else {
userChatHistory = getSingleUserChatModel(response.body);
}
}
isLoading = false;
notifyListeners();
}
@ -80,6 +114,23 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
ChatUserModel userToList(String str) => ChatUserModel.fromJson(json.decode(str));
Future<dynamic> uploadAttachments(String userId, File file) async {
dynamic result;
dynamic request = MultipartRequest('POST', Uri.parse('${ApiConsts.chatServerBaseApiUrl}${ApiConsts.chatMediaImageUploadUrl}'));
request.fields.addAll({'userId': userId, 'fileSource': '1'});
request.files.add(await MultipartFile.fromPath('files', file.path));
request.headers.addAll({'Authorization': 'Bearer ${AppState().chatDetails!.response!.token}'});
try {
StreamedResponse response = await request.send();
if (response.statusCode == 200) {
result = jsonDecode(await response.stream.bytesToString());
} else {
result = [];
}
} catch (e) {}
return result;
}
Future<void> buildHubConnection() async {
HttpConnectionOptions httpOp = HttpConnectionOptions(skipNegotiation: false, logMessageContent: true);
hubConnection = await HubConnectionBuilder()
@ -113,7 +164,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
hubConnection.on("OnUserTypingAsync", onUserTyping);
// hubConnection.on("OnUserCountAsync", userCountAsync);
// hubConnection.on("OnUpdateUserChatHistoryWindowsAsync", updateChatHistoryWindow);
// hubConnection.on("OnGetUserChatHistoryNotDeliveredAsync", chatNotDelivered);
hubConnection.on("OnGetUserChatHistoryNotDeliveredAsync", chatNotDelivered);
hubConnection.on("OnUpdateUserChatHistoryStatusAsync", updateUserChatStatus);
} else {
hubConnection.on("OnUpdateUserStatusAsync", changeStatus);
@ -219,7 +270,8 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
}
userChatHistory.add(data.first);
notifyListeners();
scrollDown();
logger.d(isChatScreenActive);
// if (isChatScreenActive) scrollDown();
}
void onUserTyping(List<Object?>? parameters) {
@ -228,7 +280,6 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
for (ChatUser user in searchedChats!) {
if (user.id == parameters![1] && parameters[0] == true) {
user.isTyping = parameters[0] as bool?;
Future.delayed(
const Duration(seconds: 2),
() {
@ -241,64 +292,356 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
notifyListeners();
}
void sendChatMessage(String chatMessage, int targetUserId, String targetUserName) async {
if (chatMessage == null || chatMessage.isEmpty) {
return;
int getFileType(String value) {
switch (value) {
case ".pdf":
return 1;
case ".png":
return 3;
case ".txt":
return 5;
case ".jpg":
return 12;
case ".jpeg":
return 4;
case ".xls":
return 7;
case ".xlsx":
return 7;
case ".doc":
return 6;
case ".docx":
return 6;
case ".ppt":
return 8;
case ".pptx":
return 8;
case ".zip":
return 2;
case ".rar":
return 2;
default:
return 0;
}
}
var contain = searchedChats!.where((ChatUser element) => element.id == targetUserId);
if (contain.isEmpty) {
searchedChats!.add(
ChatUser(
id: targetUserId,
userName: targetUserName,
),
);
String getFileTypeDescription(String value) {
switch (value) {
case ".pdf":
return "application/pdf";
case ".png":
return "image/png";
case ".txt":
return "text/plain";
case ".jpg":
return "image/jpg";
case ".jpeg":
return "image/jpeg";
case ".ppt":
return "application/vnd.openxmlformats-officedocument.presentationml.presentation";
case ".pptx":
return "application/vnd.openxmlformats-officedocument.presentationml.presentation";
case ".doc":
return "application/vnd.openxmlformats-officedocument.wordprocessingm";
case ".docx":
return "application/vnd.openxmlformats-officedocument.wordprocessingm";
case ".xls":
return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
case ".xlsx":
return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
case ".zip":
return "application/octet-stream";
case ".rar":
return "application/octet-stream";
default:
return "";
}
}
String chatData =
'{"contant":"$chatMessage","contantNo":"8a129295-36d7-7185-5d34-cc4eec7bcba4","chatEventId":1,"fileTypeId":null,"currentUserId":${AppState().chatDetails!.response!.id},"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,
// void scrollDown() {
// scrollController.animateTo(
// scrollController.position.maxScrollExtent + 100,
// curve: Curves.easeOut,
// duration: const Duration(milliseconds: 300),
// );
// notifyListeners();
// }
Future<void> sendChatToServer(
{required int chatEventId, required fileTypeId, required int targetUserId, required String targetUserName, required chatReplyId, required bool isAttachment, required bool isReply}) async {
Uuid uuid = const Uuid();
SingleUserChatModel data = SingleUserChatModel(
chatEventId: chatEventId,
chatSource: 1,
contant: chatMessage,
contantNo: "8a129295-36d7-7185-5d34-cc4eec7bcba4",
conversationId: "715f8b13-96ee-cd36-cb07-5a982a219982",
contant: message.text,
contantNo: uuid.v4(),
conversationId: uuid.v4(),
createdDate: DateTime.now(),
currentUserId: AppState().chatDetails!.response!.id,
currentUserName: AppState().chatDetails!.response!.userName,
targetUserId: targetUserId,
targetUserName: targetUserName,
),
isReplied: false,
fileTypeId: fileTypeId,
userChatReplyResponse: isReply ? UserChatReplyResponse.fromJson(repliedMsg.first.toJson()) : null,
fileTypeResponse: isAttachment
? FileTypeResponse(
fileTypeId: fileTypeId,
fileTypeName: getFileType(getFileExtension(selectedFile.path).toString()),
fileKind: getFileExtension(selectedFile.path),
fileName: selectedFile.path.split("/").last,
fileTypeDescription: getFileTypeDescription(getFileExtension(selectedFile.path).toString()),
)
: null,
);
String chatData =
'{"contant":"${message.text}","contantNo":"${uuid.v4()}","chatEventId":$chatEventId,"fileTypeId": $fileTypeId,"currentUserId":${AppState().chatDetails!.response!.id},"chatSource":1,"userChatHistoryLineRequestList":[{"isSeen":false,"isDelivered":false,"targetUserId":$targetUserId,"targetUserStatus":1}],"chatReplyId":$chatReplyId,"conversationId":"${uuid.v4()}"}';
await hubConnection.invoke("AddChatUserAsync", args: <Object>[json.decode(chatData)]);
userChatHistory.add(data);
isFileSelected = false;
isMsgReply = false;
sFileType = "";
message.clear();
notifyListeners();
scrollDown();
// scrollDown();
}
void scrollDown() {
scrollController.animateTo(
scrollController.position.maxScrollExtent + 100,
curve: Curves.easeOut,
duration: const Duration(milliseconds: 300),
void sendChatMessage(int targetUserId, String targetUserName) async {
dynamic contain = searchedChats!.where((ChatUser element) => element.id == targetUserId);
if (contain.isEmpty) {
searchedChats!.add(
ChatUser(
id: targetUserId,
userName: targetUserName,
),
);
notifyListeners();
}
if (!isFileSelected && !isMsgReply) {
logger.d("Normal Text Message");
if (message.text == null || message.text.isEmpty) {
return;
}
sendChatToServer(chatEventId: 1, fileTypeId: null, targetUserId: targetUserId, targetUserName: targetUserName, isAttachment: false, chatReplyId: null, isReply: false);
}
if (isFileSelected && !isMsgReply) {
logger.d("Normal Attachment Message");
dynamic value = await uploadAttachments(AppState().chatDetails!.response!.id.toString(), selectedFile);
String? ext = getFileExtension(selectedFile.path);
sendChatToServer(chatEventId: 2, fileTypeId: getFileType(ext.toString()), targetUserId: targetUserId, targetUserName: targetUserName, isAttachment: true, chatReplyId: null, isReply: false);
}
if (!isFileSelected && isMsgReply) {
logger.d("Normal Text Message With Reply");
if (message.text == null || message.text.isEmpty) {
return;
}
sendChatToServer(
chatEventId: 1, fileTypeId: null, targetUserId: targetUserId, targetUserName: targetUserName, chatReplyId: repliedMsg.first.userChatHistoryId, isAttachment: false, isReply: true);
//chatReplyId
}
if (isFileSelected && isMsgReply) {
logger.d("Attachment Message With Reply");
logger.d(repliedMsg.first.userChatHistoryId);
dynamic value = await uploadAttachments(AppState().chatDetails!.response!.id.toString(), selectedFile);
String? ext = getFileExtension(selectedFile.path);
sendChatToServer(
chatEventId: 2,
fileTypeId: getFileType(ext.toString()),
targetUserId: targetUserId,
targetUserName: targetUserName,
isAttachment: true,
chatReplyId: repliedMsg.first.userChatHistoryId,
isReply: true);
}
// dynamic contain = searchedChats!.where((ChatUser element) => element.id == targetUserId);
// if (contain.isEmpty) {
// searchedChats!.add(
// ChatUser(
// id: targetUserId,
// userName: targetUserName,
// ),
// );
// notifyListeners();
// }
// Uuid uuid = const Uuid();
// dynamic fileID = fileUploadResponse.isEmpty ? null : getFileType(chatMessage);
// SingleUserChatModel data = SingleUserChatModel(
// chatEventId: fileUploadResponse.isEmpty ? 1 : 2,
// chatSource: 1,
// contant: chatMessage,
// contantNo: uuid.v4(),
// conversationId: uuid.v4(),
// createdDate: DateTime.now(),
// currentUserId: AppState().chatDetails!.response!.id,
// currentUserName: AppState().chatDetails!.response!.userName,
// targetUserId: targetUserId,
// targetUserName: targetUserName,
// fileTypeId: fileID,
// isReplied: false,
// // fileTypeResponse: FileTypeResponse(
// // fileTypeId: 0,
// // fileTypeDescription: fileUploadResponse.isEmpty ? null : fileUploadResponse.first["filePath"],
// // fileName: fileUploadResponse.isEmpty ? null : fileUploadResponse.first["filePath"],
// // fileKind: "image",
// // fileTypeName: fileUploadResponse.isEmpty ? null : fileUploadResponse.first["filePath"].toString().split(".").last),
// );
//
// String chatData =
// '{"contant":"$chatMessage","contantNo":"${uuid.v4()}","chatEventId":${fileUploadResponse.isEmpty ? 1 : 2},"fileTypeId": $fileID,"currentUserId":${AppState().chatDetails!.response!.id},"chatSource":1,"userChatHistoryLineRequestList":[{"isSeen":false,"isDelivered":false,"targetUserId":$targetUserId,"targetUserStatus":1}],"conversationId":"${uuid.v4()}"}';
// await hubConnection.invoke("AddChatUserAsync", args: <Object>[json.decode(chatData)]);
// userChatHistory.add(data);
// message.clear();
// notifyListeners();
// scrollDown();
}
void scrollListener() {
if (userChatHistory.length < paginationVal) {
print("Get New Data");
}
}
void selectImageToUpload(BuildContext context) {
ImageOptions.showImageOptionsNew(context, true, (String image, File file) async {
if (checkFileSize(file.path)) {
selectedFile = file;
isFileSelected = true;
sFileType = getFileExtension(file.path)!;
message.text = file.path.split("/").last;
Navigator.of(context).pop();
} else {
Utils.showToast("Max 1 mb size is allowed to upload");
}
//Utils.showLoading(context);
notifyListeners();
//Utils.hideLoading(context);
// Utils.showLoading(context);
// await m.uploadAttachments(AppState().chatDetails!.response!.id.toString(), file).then((value) {
// if (value == null) {
// m.logger.d("Returned EMPTY");
// } else {
// m.sendChatMessage(value.isEmpty ? m.message.text : value.first["filePath"], userDetails["targetUser"].id, userDetails["targetUser"].userName, value);
// }
// });
// Utils.hideLoading(context);
});
}
void removeAttachment() {
isFileSelected = false;
sFileType = "";
message.text = '';
notifyListeners();
}
String? getFileExtension(String fileName) {
try {
return "." + fileName.split('.').last;
} catch (e) {
return null;
}
}
bool checkFileSize(String path) {
int fileSizeLimit = 1024;
File f = File(path);
double fileSizeInKB = f.lengthSync() / 1024;
double fileSizeInMB = fileSizeInKB / 1024;
if (fileSizeInKB > fileSizeLimit) {
return false;
} else {
return true;
}
}
String getType(String type) {
switch (type) {
case ".pdf":
return "assets/images/pdf.svg";
case ".png":
return "assets/images/png.svg";
case ".txt":
return "assets/icons/chat/txt.svg";
case ".jpg":
return "assets/images/jpg.svg";
case ".jpeg":
return "assets/images/jpg.svg";
case ".xls":
return "assets/icons/chat/xls.svg";
case ".xlsx":
return "assets/icons/chat/xls.svg";
case ".doc":
return "assets/icons/chat/doc.svg";
case ".docx":
return "assets/icons/chat/doc.svg";
case ".ppt":
return "assets/icons/chat/ppt.svg";
case ".pptx":
return "assets/icons/chat/ppt.svg";
case ".zip":
return "assets/icons/chat/zip.svg";
case ".rar":
return "assets/icons/chat/zip.svg";
default:
return "assets/images/thumb.svg";
}
}
void chatReply(SingleUserChatModel data) {
repliedMsg = [];
data.isReplied = true;
isMsgReply = true;
repliedMsg.add(data);
notifyListeners();
}
void closeMe() {
repliedMsg = [];
isMsgReply = false;
notifyListeners();
}
String dateFormte(DateTime data) {
DateFormat f = new DateFormat('hh:mm a dd MMM yyyy');
f.format(data);
return f.format(data);
}
// void _scrollListener() {
// if (scrollController.position.extentAfter.toInt() <= 0 && canCallApi) {
// if (userChatHistory.length < _ayatTangheemTypeMapped.totalItemsCount) {
// currentPageNo++;
// if (widget.tangheemQuery == null) {
// getTangheemData();
// } else {
// getTangheemDataByKeyword();
// }
// }
// canCallApi = false;
// }
// }
Future<void> favoriteUser({required int userID, required int targetUserID}) async {
Response response =
await ApiClient().postJsonForResponse("${ApiConsts.chatServerBaseApiUrl}FavUser/addFavUser", {"targetUserId": targetUserID, "userId": userID}, token: AppState().chatDetails!.response!.token);
fav.FavoriteChatUser favoriteChatUser = fav.FavoriteChatUser.fromRawJson(response.body);
if (favoriteChatUser.response != null) {
for (var user in searchedChats!) {
if (user.id == favoriteChatUser.response!.targetUserId!) {
user.isFav = favoriteChatUser.response!.isFav;
}
}
}
notifyListeners();
}
Future<void> unFavoriteUser({required int userID, required int targetUserID}) async {
Response response = await ApiClient()
.postJsonForResponse("${ApiConsts.chatServerBaseApiUrl}FavUser/deleteFavUser", {"targetUserId": targetUserID, "userId": userID}, token: AppState().chatDetails!.response!.token);
fav.FavoriteChatUser favoriteChatUser = fav.FavoriteChatUser.fromRawJson(response.body);
if (favoriteChatUser.response != null) {
for (var user in searchedChats!) {
if (user.id == favoriteChatUser.response!.targetUserId!) {
user.isFav = favoriteChatUser.response!.isFav;
}
}
}
notifyListeners();
}
void clearSelections() {
isChatScreenActive = false;
paginationVal = 0;
message.text = '';
isFileSelected = false;
repliedMsg = [];
sFileType = "";
}
}

@ -58,4 +58,5 @@ class MyColors {
static const Color greyC4Color = Color(0xffC4C4C4);
static const Color grey35Color = Color(0xff535353);
static const Color grey9DColor = Color(0xff9D9D9D);
static const Color grey71Color = Color(0xff717171);
}

@ -1,7 +1,7 @@
class ApiConsts {
//static String baseUrl = "http://10.200.204.20:2801/"; // Local server
static String baseUrl = "https://uat.hmgwebservices.com"; // UAT server
// static String baseUrl = "https://hmgwebservices.com"; // Live server
//static String baseUrl = "https://uat.hmgwebservices.com"; // UAT server
static String baseUrl = "https://hmgwebservices.com"; // Live server
static String baseUrlServices = baseUrl + "/Services/"; // server
// static String baseUrlServices = "https://api.cssynapses.com/tangheem/"; // Live server
static String utilitiesRest = baseUrlServices + "Utilities.svc/REST/";
@ -16,7 +16,12 @@ class ApiConsts {
static String chatSearchMember = "user/getUserWithStatusAndFavAsync/";
static String chatRecentUrl = "UserChatHistory/getchathistorybyuserid"; //For a Mem
static String chatSingleUserHistoryUrl = "UserChatHistory/GetUserChatHistory";
static String chatMediaImageUploadUrl = "shared/upload";
static String chatFavoriteUsers = "FavUser/getFavUserById/";
//https://apiderichat.hmg.com/api/FavUser/getFavUserById/42062
//https://apiderichat.hmg.com/api/shared/upload
// 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";
}

@ -18,7 +18,7 @@ extension EmailValidator on String {
Widget toText10({Color? color, bool isBold = false, int? maxlines, FontStyle? fontStyle}) => Text(
this,
//maxLines: maxlines,
maxLines: maxlines,
style: TextStyle(fontSize: 10, fontStyle: fontStyle ?? FontStyle.normal, fontWeight: isBold ? FontWeight.bold : FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -0.4),
);

@ -1,3 +1,9 @@
import 'dart:convert';
List<SingleUserChatModel> singleUserChatModelFromJson(String str) => List<SingleUserChatModel>.from(json.decode(str).map((x) => SingleUserChatModel.fromJson(x)));
String singleUserChatModelToJson(List<SingleUserChatModel> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class SingleUserChatModel {
SingleUserChatModel({
this.userChatHistoryId,
@ -19,6 +25,7 @@ class SingleUserChatModel {
this.conversationId,
this.fileTypeResponse,
this.userChatReplyResponse,
this.isReplied,
});
int? userChatHistoryId;
@ -29,17 +36,18 @@ class SingleUserChatModel {
String? currentUserName;
int? targetUserId;
String? targetUserName;
dynamic encryptedTargetUserId;
dynamic encryptedTargetUserName;
String? encryptedTargetUserId;
String? encryptedTargetUserName;
int? chatEventId;
dynamic fileTypeId;
dynamic? fileTypeId;
bool? isSeen;
bool? isDelivered;
DateTime? createdDate;
int? chatSource;
String? conversationId;
FileTypeResponse? fileTypeResponse;
dynamic userChatReplyResponse;
UserChatReplyResponse? userChatReplyResponse;
bool? isReplied;
factory SingleUserChatModel.fromJson(Map<String, dynamic> json) => SingleUserChatModel(
userChatHistoryId: json["userChatHistoryId"] == null ? null : json["userChatHistoryId"],
@ -50,8 +58,8 @@ class SingleUserChatModel {
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"],
encryptedTargetUserId: json["encryptedTargetUserId"] == null ? null : json["encryptedTargetUserId"],
encryptedTargetUserName: json["encryptedTargetUserName"] == null ? null : json["encryptedTargetUserName"],
chatEventId: json["chatEventId"] == null ? null : json["chatEventId"],
fileTypeId: json["fileTypeId"],
isSeen: json["isSeen"] == null ? null : json["isSeen"],
@ -60,7 +68,8 @@ class SingleUserChatModel {
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"],
userChatReplyResponse: json["userChatReplyResponse"] == null ? null : UserChatReplyResponse.fromJson(json["userChatReplyResponse"]),
isReplied: false,
);
Map<String, dynamic> toJson() => {
@ -72,8 +81,8 @@ class SingleUserChatModel {
"currentUserName": currentUserName == null ? null : currentUserName,
"targetUserId": targetUserId == null ? null : targetUserId,
"targetUserName": targetUserName == null ? null : targetUserName,
"encryptedTargetUserId": encryptedTargetUserId,
"encryptedTargetUserName": encryptedTargetUserName,
"encryptedTargetUserId": encryptedTargetUserId == null ? null : encryptedTargetUserId,
"encryptedTargetUserName": encryptedTargetUserName == null ? null : encryptedTargetUserName,
"chatEventId": chatEventId == null ? null : chatEventId,
"fileTypeId": fileTypeId,
"isSeen": isSeen == null ? null : isSeen,
@ -82,7 +91,7 @@ class SingleUserChatModel {
"chatSource": chatSource == null ? null : chatSource,
"conversationId": conversationId == null ? null : conversationId,
"fileTypeResponse": fileTypeResponse == null ? null : fileTypeResponse!.toJson(),
"userChatReplyResponse": userChatReplyResponse,
"userChatReplyResponse": userChatReplyResponse == null ? null : userChatReplyResponse!.toJson(),
};
}
@ -117,3 +126,51 @@ class FileTypeResponse {
"fileName": fileName,
};
}
class UserChatReplyResponse {
UserChatReplyResponse({
this.userChatHistoryId,
this.chatEventId,
this.contant,
this.contantNo,
this.fileTypeId,
this.createdDate,
this.targetUserId,
this.targetUserName,
this.fileTypeResponse,
});
int? userChatHistoryId;
int? chatEventId;
String? contant;
String? contantNo;
dynamic? fileTypeId;
DateTime? createdDate;
int? targetUserId;
String? targetUserName;
FileTypeResponse? fileTypeResponse;
factory UserChatReplyResponse.fromJson(Map<String, dynamic> json) => UserChatReplyResponse(
userChatHistoryId: json["userChatHistoryId"] == null ? null : json["userChatHistoryId"],
chatEventId: json["chatEventId"] == null ? null : json["chatEventId"],
contant: json["contant"] == null ? null : json["contant"],
contantNo: json["contantNo"] == null ? null : json["contantNo"],
fileTypeId: json["fileTypeId"],
createdDate: json["createdDate"] == null ? null : DateTime.parse(json["createdDate"]),
targetUserId: json["targetUserId"] == null ? null : json["targetUserId"],
targetUserName: json["targetUserName"] == null ? null : json["targetUserName"],
fileTypeResponse: json["fileTypeResponse"] == null ? null : FileTypeResponse.fromJson(json["fileTypeResponse"]),
);
Map<String, dynamic> toJson() => {
"userChatHistoryId": userChatHistoryId == null ? null : userChatHistoryId,
"chatEventId": chatEventId == null ? null : chatEventId,
"contant": contant == null ? null : contant,
"contantNo": contantNo == null ? null : contantNo,
"fileTypeId": fileTypeId,
"createdDate": createdDate == null ? null : createdDate!.toIso8601String(),
"targetUserId": targetUserId == null ? null : targetUserId,
"targetUserName": targetUserName == null ? null : targetUserName,
"fileTypeResponse": fileTypeResponse == null ? null : fileTypeResponse!.toJson(),
};
}

@ -0,0 +1,53 @@
// To parse this JSON data, do
//
// final favoriteChatUser = favoriteChatUserFromJson(jsonString);
import 'dart:convert';
class FavoriteChatUser {
FavoriteChatUser({
this.response,
this.errorResponses,
});
Response? response;
dynamic? errorResponses;
factory FavoriteChatUser.fromRawJson(String str) => FavoriteChatUser.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory FavoriteChatUser.fromJson(Map<String, dynamic> json) => FavoriteChatUser(
response: json["response"] == null ? null : Response.fromJson(json["response"]),
errorResponses: json["errorResponses"],
);
Map<String, dynamic> toJson() => {
"response": response == null ? null : response!.toJson(),
"errorResponses": errorResponses,
};
}
class Response {
Response({
this.targetUserId,
this.isFav,
});
int? targetUserId;
bool? isFav;
factory Response.fromRawJson(String str) => Response.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory Response.fromJson(Map<String, dynamic> json) => Response(
targetUserId: json["targetUserId"] == null ? null : json["targetUserId"],
isFav: json["isFav"] == null ? null : json["isFav"],
);
Map<String, dynamic> toJson() => {
"targetUserId": targetUserId == null ? null : targetUserId,
"isFav": isFav == null ? null : isFav,
};
}

@ -2,82 +2,105 @@ 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';
import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
import 'package:sizer/sizer.dart';
class ChatBubble extends StatelessWidget {
const ChatBubble(
{Key? key,
required this.text,
required this.replyText,
required this.isCurrentUser,
required this.isSeen,
required this.isDelivered,
required this.dateTime})
required this.dateTime,
required this.isReplied,
required this.userName})
: super(key: key);
final String text;
final String replyText;
final bool isCurrentUser;
final bool isSeen;
final bool isDelivered;
final String dateTime;
final bool isReplied;
final String userName;
@override
Widget build(BuildContext context) {
return Padding(
// asymmetric padding
padding: EdgeInsets.fromLTRB(
isCurrentUser ? 64.0 : 16.0,
4,
isCurrentUser ? 16.0 : 64.0,
4,
),
// padding: EdgeInsets.zero,
padding: EdgeInsets.only(left: isCurrentUser ? 110 : 20, right: isCurrentUser ? 20 : 110, bottom: 9),
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(
: const LinearGradient(
transform: GradientRotation(.46),
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [
colors: <Color>[
MyColors.gradiantEndColor,
MyColors.gradiantStartColor,
]),
],
),
borderRadius: BorderRadius.circular(10),
),
child: Padding(
padding: const EdgeInsets.all(12),
padding: EdgeInsets.only(top: isReplied ? 8 : 5, right:8, left: 8, bottom: 5),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
text.toText12(
color:
isCurrentUser ? MyColors.grey57Color : MyColors.white),
8.height,
if (isReplied)
ClipRRect(
borderRadius: BorderRadius.circular(5.0),
child: Container(
decoration: BoxDecoration(
border: Border(
left: BorderSide(width: 6, color: isCurrentUser ? MyColors.gradiantStartColor : MyColors.white),
),
color: isCurrentUser ? MyColors.black.withOpacity(0.10) : MyColors.black.withOpacity(0.30),
),
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
(userName).toText12(color: MyColors.gradiantStartColor, isBold: false).paddingOnly(right: 5, top: 5, bottom: 0, left: 5),
replyText
.toText10(color: isCurrentUser ? MyColors.grey71Color : MyColors.white.withOpacity(0.5), isBold: false, maxlines: 4)
.paddingOnly(right: 5, top: 5, bottom: 8, left: 5),
],
),
),
],
),
),
),
if (isReplied) 8.height,
text.toText12(color: isCurrentUser ? MyColors.grey57Color : MyColors.white),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: [
dateTime.toText12(
color: isCurrentUser
? MyColors.grey41Color.withOpacity(.5)
: Colors.white.withOpacity(0.7)),
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,
isDelivered ? Icons.done_all : Icons.done_all,
color: isSeen ? MyColors.textMixColor : MyColors.grey9DColor,
size: 14,
)
),
],
),
],
),
),

@ -1,4 +1,6 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
@ -6,27 +8,53 @@ import 'package:flutter_svg/flutter_svg.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/classes/colors.dart';
import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/classes/utils.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';
import 'package:mohem_flutter_app/generated/locale_keys.g.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/image_picker.dart';
import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart';
import 'package:provider/provider.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'package:sizer/sizer.dart';
import 'package:swipe_to/swipe_to.dart';
class ChatDetailScreen extends StatelessWidget {
ChatDetailScreen({Key? key}) : super(key: key);
dynamic userDetails;
late ChatProviderModel data;
ScrollController scrollController = ScrollController();
RefreshController _refreshController = RefreshController(initialRefresh: false);
ChatDetailScreen({Key? key}) : super(key: key);
void onRefresh() async {
print("Refresh Triggered");
await Future.delayed(Duration(milliseconds: 1000));
if (userDetails != null) {
data.paginationVal = data.paginationVal + 10;
data.getSingleUserChatHistory(senderUID: AppState().chatDetails!.response!.id.toString(), receiverUID: userDetails["targetUser"].id, loadMore: true);
}
_refreshController.refreshCompleted();
}
@override
Widget build(BuildContext context) {
userDetails = ModalRoute
.of(context)!
.settings
.arguments;
userDetails = ModalRoute.of(context)!.settings.arguments;
data = Provider.of<ChatProviderModel>(context, listen: false);
data.getSingleUserChatHistory(senderUID: AppState().chatDetails!.response!.id.toString(), receiverUID: userDetails["targetUser"].id, pagination: "0");
Timer(const Duration(seconds: 1), () => data.scrollDown());
if (userDetails != null) data.getSingleUserChatHistory(senderUID: AppState().chatDetails!.response!.id.toString(), receiverUID: userDetails["targetUser"].id, loadMore: false);
//
// if (userDetails != null) {
// scrollController.addListener(() {
// if (scrollController.position.minScrollExtent == scrollController.offset) {
// data.paginationVal++;
// data.getSingleUserChatHistory(senderUID: AppState().chatDetails!.response!.id.toString(), receiverUID: userDetails["targetUser"].id, loadMore: true);
// }
// });
// }
return Scaffold(
backgroundColor: const Color(0xFFF8F8F8),
appBar: AppBarWidget(context, title: userDetails["targetUser"].userName, showHomeButton: false, image: userDetails["targetUser"].image),
@ -37,66 +65,210 @@ class ChatDetailScreen extends StatelessWidget {
: Column(
children: <Widget>[
Expanded(
flex: 2,
child: SmartRefresher(
enablePullDown: true,
enablePullUp: false,
header: const MaterialClassicHeader(
color: MyColors.gradiantEndColor,
),
controller: _refreshController,
onRefresh: onRefresh,
child: ListView.builder(
controller: m.scrollController,
controller: scrollController,
shrinkWrap: true,
itemCount: m.userChatHistory.length,
padding: const EdgeInsets.symmetric(vertical: 10),
padding: EdgeInsets.zero,
itemBuilder: (BuildContext context, int i) {
i == 0 ? m.logger.d(m.userChatHistory.length) : "";
return ChatBubble(
return GestureDetector(
onTap: () {
m.logger.d(jsonEncode(m.userChatHistory[i]));
m.logger.d(jsonEncode(m.userChatHistory.length));
},
child: SwipeTo(
iconColor: MyColors.lightGreenColor,
child: ChatBubble(
text: m.userChatHistory[i].contant.toString(),
replyText: m.userChatHistory[i].userChatReplyResponse != null ? m.userChatHistory[i].userChatReplyResponse!.contant.toString() : "",
isSeen: m.userChatHistory[i].isSeen == true ? true : false,
isCurrentUser: m.userChatHistory[i].currentUserId == AppState().chatDetails!.response!.id ? true : false,
isDelivered: m.userChatHistory[i].currentUserId == AppState().chatDetails!.response!.id && m.userChatHistory[i].isDelivered == true ? true : false,
dateTime: m.userChatHistory[i].createdDate.toString(),
dateTime: m.dateFormte(m.userChatHistory[i].createdDate!),
isReplied: m.userChatHistory[i].userChatReplyResponse != null ? true : false,
userName: AppState().chatDetails!.response!.userName == m.userChatHistory[i].currentUserName.toString() ? "You" : m.userChatHistory[i].currentUserName.toString(),
),
onRightSwipe: () {
m.chatReply(m.userChatHistory[i]);
},
),
);
},
),
),
),
if (m.isMsgReply)
Row(
children: <Widget>[
Container(
height: 80,
color: MyColors.textMixColor,
width: 6,
),
Expanded(
child: Container(
height: 80,
color: MyColors.black.withOpacity(0.10),
child: ListTile(
title: (AppState().chatDetails!.response!.userName == m.repliedMsg.first.currentUserName.toString() ? "You" : m.repliedMsg.first.currentUserName.toString())
.toText14(color: MyColors.lightGreenColor),
subtitle: (m.repliedMsg.isNotEmpty ? m.repliedMsg.first.contant! : "").toText12(color: MyColors.white, maxLine: 2),
trailing: GestureDetector(
onTap: m.closeMe,
child: Container(
decoration: BoxDecoration(
color: MyColors.white.withOpacity(0.5),
borderRadius: const BorderRadius.all(
Radius.circular(20),
),
),
child: const Icon(
Icons.close,
size: 23,
color: MyColors.white,
),
),
),
),
),
),
],
),
if (m.isFileSelected && m.sFileType == ".png" || m.sFileType == ".jpeg" || m.sFileType == ".jpg")
Card(
margin: EdgeInsets.zero,
elevation: 0,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
padding: const EdgeInsets.only(left: 20.0, right: 20, top: 20, bottom: 0),
child: Card(
margin: EdgeInsets.zero,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(0.0),
),
elevation: 0,
child: Container(
height: 200,
decoration: BoxDecoration(
image: DecorationImage(
image: FileImage(
m.selectedFile,
),
fit: BoxFit.cover),
borderRadius: const BorderRadius.all(
Radius.circular(0),
),
),
child: const SizedBox(
width: double.infinity,
height: 200,
),
),
),
),
),
Card(
margin: EdgeInsets.zero,
child: TextField(
controller: m.message,
decoration: InputDecoration(
hintText: LocaleKeys.typeheretoreply.tr(),
hintStyle: const TextStyle(color: MyColors.grey98Color),
hintText: m.isFileSelected ? m.selectedFile.path.split("/").last : LocaleKeys.typeheretoreply.tr(),
hintStyle: TextStyle(color: m.isFileSelected ? MyColors.darkTextColor : MyColors.grey98Color, fontSize: 14),
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
errorBorder: InputBorder.none,
disabledBorder: InputBorder.none,
contentPadding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
suffixIcon: SizedBox(
width: 100,
contentPadding: EdgeInsets.only(left: m.sFileType.isNotEmpty ? 10 : 20, right: m.sFileType.isNotEmpty ? 0 : 5, top: 20, bottom: 20),
prefixIcon: m.sFileType.isNotEmpty
? Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
SvgPicture.asset(
m.getType(m.sFileType),
height: 30,
width: 25,
alignment: Alignment.center,
fit: BoxFit.cover,
).paddingOnly(left: 20),
],
)
: null,
suffixIcon: Container(
width: 96,
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: [
crossAxisAlignment: CrossAxisAlignment.center, // added line
children: <Widget>[
if (m.sFileType.isNotEmpty)
IconButton(
padding: EdgeInsets.zero,
alignment: Alignment.centerRight,
icon: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Container(
decoration: const BoxDecoration(
color: MyColors.redA3Color,
borderRadius: BorderRadius.all(
Radius.circular(20),
),
),
child: const Icon(
Icons.close,
size: 15,
color: MyColors.white,
),
),
("Clear").toText11(color: MyColors.redA3Color).paddingOnly(left: 4),
],
),
onPressed: () async {
m.removeAttachment();
},
),
if (m.sFileType.isEmpty)
RotationTransition(
turns: const AlwaysStoppedAnimation(45 / 360),
child: IconButton(
padding: EdgeInsets.zero,
alignment: Alignment.topRight,
icon: const Icon(
Icons.attach_file_rounded,
size: 27,
color: MyColors.lightGreenColor,
size: 26,
color: MyColors.grey3AColor,
),
onPressed: () async {
m.selectImageToUpload(context);
},
),
onPressed: () {},
),
IconButton(
alignment: Alignment.centerRight,
padding: EdgeInsets.zero,
icon: SvgPicture.asset(
"assets/icons/chat/chat_send_icon.svg",
height: 26,
width: 35,
width: 26,
),
onPressed: () {
m.sendChatMessage(m.message.text, userDetails["targetUser"].id, userDetails["targetUser"].userName);
m.sendChatMessage(userDetails["targetUser"].id, userDetails["targetUser"].userName);
},
),
)
],
),
),
),
).paddingOnly(right: 20),
),
),
),

@ -8,13 +8,16 @@ 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/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';
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';
import 'package:sizer/sizer.dart';
class ChatHomeScreen extends StatefulWidget {
const ChatHomeScreen({Key? key}) : super(key: key);
@ -81,11 +84,13 @@ class _ChatHomeScreenState extends State<ChatHomeScreen> {
if (m.searchedChats != null)
ListView.separated(
itemCount: m.searchedChats!.length,
padding: const EdgeInsets.only(top: 0),
padding: const EdgeInsets.only(top: 20),
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (BuildContext context, int index) {
return ListTile(
return SizedBox(
height: 55,
child: ListTile(
leading: Stack(
children: [
SvgPicture.asset(
@ -110,16 +115,61 @@ class _ChatHomeScreenState extends State<ChatHomeScreen> {
],
),
title: (m.searchedChats![index].userName ?? "").toText14(color: MyColors.darkTextColor),
subtitle: (m.searchedChats![index].isTyping == true ? "Typing ..." : "").toText11(color: MyColors.normalTextColor),
trailing: ("Today").toText10(color: MyColors.lightTextColor),
// subtitle: (m.searchedChats![index].isTyping == true ? "Typing ..." : "").toText11(color: MyColors.normalTextColor),
trailing: SizedBox(
width: 60,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
if (m.searchedChats![index].unreadMessageCount! > 0)
Flexible(
child: Container(
padding: EdgeInsets.zero,
alignment: Alignment.centerRight,
width: 18,
height: 18,
decoration: const BoxDecoration(
color: MyColors.redColor,
borderRadius: BorderRadius.all(
Radius.circular(20),
),
),
child: (m.searchedChats![index].unreadMessageCount!.toString())
.toText10(
color: MyColors.white,
)
.center,
),
),
Flexible(
child: IconButton(
alignment: Alignment.centerRight,
padding: EdgeInsets.zero,
icon: Icon(m.searchedChats![index].isFav! ? Icons.star : Icons.star_border),
color: m.searchedChats![index].isFav! ? MyColors.yellowColor : MyColors.grey35Color,
onPressed: () {
if (m.searchedChats![index].isFav!) m.unFavoriteUser(userID: AppState().chatDetails!.response!.id!, targetUserID: m.searchedChats![index].id!);
if (!m.searchedChats![index].isFav!) m.favoriteUser(userID: AppState().chatDetails!.response!.id!, targetUserID: m.searchedChats![index].id!);
},
),
)
],
),
),
minVerticalPadding: 0,
onTap: () {
Navigator.pushNamed(
context,
AppRoutes.chatDetailed,
arguments: {"targetUser": m.searchedChats![index]},
);
).then((value) {
m.clearSelections();
});
},
onLongPress: () {},
),
);
},
separatorBuilder: (BuildContext context, int index) => const Padding(
@ -129,7 +179,6 @@ class _ChatHomeScreenState extends State<ChatHomeScreen> {
),
),
),
// if (searchedUsersList == null) Utils.getNoChatWidget(context),
],
);
}),
@ -156,11 +205,9 @@ class _ChatHomeScreenState extends State<ChatHomeScreen> {
),
),
onPressed: () async {
// var userData = await ChatApiClient()
// .getChatMemberFromSearch("aamir.muhammad", 36239);
showMyBottomSheet(
context,
callBackFunc: (){},
callBackFunc: () {},
child: SearchEmployeeBottomSheet(
title: LocaleKeys.searchForEmployee.tr(),
apiMode: LocaleKeys.delegate.tr(),

@ -183,16 +183,66 @@ class _SearchEmployeeBottomSheetState extends State<SearchEmployeeBottomSheet> {
if (widget.fromChat)
if (chatUsersList != null && widget.fromChat)
chatUsersList!.isEmpty
? Utils.getNoDataWidget(context)
? Column(
children: [
20.height,
Utils.getNoDataWidget(context),
],
)
: ListView(
physics: const BouncingScrollPhysics(),
padding: EdgeInsets.only(top: 0, bottom: 8),
children: [
padding: const EdgeInsets.only(top: 15,),
children: <Widget>[
ListView.separated(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: (cxt, index) {
return ListTile(
itemBuilder: (BuildContext cxt, int index) {
// return ListTile(
// leading: Stack(
// children: <Widget>[
// SvgPicture.asset(
// "assets/images/user.svg",
// height: 48,
// width: 48,
// ),
// Positioned(
// right: 5,
// bottom: 1,
// child: Container(
// width: 10,
// height: 10,
// decoration: BoxDecoration(
// color: chatUsersList![index].userStatus == 1 ? MyColors.green2DColor : Colors.red,
// borderRadius: const BorderRadius.all(
// Radius.circular(10),
// ),
// ),
// ),
// )
// ],
// ),
// title: (chatUsersList![index].userName ?? "").toText14(color: MyColors.darkTextColor),
// // subtitle: (chatUsersList![index].isTyping == true ? "Something is Typing" : "Last message text").toText11(color: MyColors.normalTextColor),
// // trailing: ("Today").toText10(color: MyColors.lightTextColor),
// minVerticalPadding: 0,
// onTap: () {
// Navigator.pop(context);
// Navigator.pushNamed(
// context,
// AppRoutes.chatDetailed,
// arguments: {"targetUser": chatUsersList![index]},
// );
// },
// );
return SizedBox(
height: 55,
child: ListTile(
leading: Stack(
children: [
SvgPicture.asset(
@ -217,8 +267,6 @@ class _SearchEmployeeBottomSheetState extends State<SearchEmployeeBottomSheet> {
],
),
title: (chatUsersList![index].userName ?? "").toText14(color: MyColors.darkTextColor),
// subtitle: (chatUsersList![index].isTyping == true ? "Something is Typing" : "Last message text").toText11(color: MyColors.normalTextColor),
// trailing: ("Today").toText10(color: MyColors.lightTextColor),
minVerticalPadding: 0,
onTap: () {
Navigator.pop(context);
@ -228,13 +276,21 @@ class _SearchEmployeeBottomSheetState extends State<SearchEmployeeBottomSheet> {
arguments: {"targetUser": chatUsersList![index]},
);
},
onLongPress: () {},
),
);
},
separatorBuilder: (BuildContext cxt, int index) => Container(
height: 1,
color: MyColors.borderE3Color,
separatorBuilder: (BuildContext context, int index) => const Padding(
padding: EdgeInsets.only(right: 10, left: 70, bottom: 0,top: 0),
child: Divider(
color: Color(0xFFE5E5E5),
),
),
itemCount: chatUsersList?.length ?? 0,
),
itemCount: chatUsersList?.length ?? 0),
12.height,
],
).expanded,

@ -13,7 +13,7 @@ class ImageOptions {
static void showImageOptionsNew(BuildContext context, bool showFilesOption, Function(String, File) image) {
showMyBottomSheet(
context,
callBackFunc: (){},
callBackFunc: () {},
child: AttachmentOptions(
showFilesOption: showFilesOption,
onCameraTap: () async {
@ -43,7 +43,12 @@ class ImageOptions {
}
},
onFilesTap: () async {
FilePickerResult? result = await FilePicker.platform.pickFiles();
FilePickerResult? result = await FilePicker.platform.pickFiles(
type: FileType.custom,
allowedExtensions: ['jpg', 'jpeg ', 'pdf', 'txt', 'docx', 'doc', 'pptx', 'xlsx', 'png', 'rar', 'zip', 'xls'],
);
List<File> files = result!.paths.map((path) => File(path!)).toList();
image(result!.files.first.path.toString(), files.first);
},
),
);

@ -85,9 +85,13 @@ dependencies:
appinio_swiper: ^1.1.1
expandable: ^5.0.1
#Chat
signalr_netcore: ^1.3.3
logging: ^1.0.1
swipe_to: ^1.0.2

Loading…
Cancel
Save