diff --git a/assets/images/chat-group.svg b/assets/images/chat-group.svg new file mode 100644 index 0000000..127a793 --- /dev/null +++ b/assets/images/chat-group.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 4178d17..dd7c997 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -524,5 +524,7 @@ "fakeLocation": ".لقد تتبعنا أنك تحاول استخدام موقع مزيف! يعتبر هذا مخالفة وقد تم إخطار الموارد البشرية", "noWinner": "حزين! لم يفز أحد اليوم.", "myTeam" : "فريقي", - "youCanPlayDemo": "لكن يمكنك لعب العرض" + "youCanPlayDemo": "لكن يمكنك لعب العرض", + "group" : "مجموعة", + "searchGroup": "مجموعة البحث" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index e4a4266..182dde6 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -524,5 +524,7 @@ "fakeLocation": "We traced out that you try to use a fake location! This is considered a violation, and HR has been notified.", "noWinner": "Sad! No one won today.", "myTeam" : "My Team", - "youCanPlayDemo": "But you can play demo" + "youCanPlayDemo": "But you can play demo", + "group": "Groups", + "searchGroup": "Search Group" } \ No newline at end of file diff --git a/lib/api/chat/chat_api_client.dart b/lib/api/chat/chat_api_client.dart index b4c0a2c..2ef616d 100644 --- a/lib/api/chat/chat_api_client.dart +++ b/lib/api/chat/chat_api_client.dart @@ -13,6 +13,7 @@ import 'package:mohem_flutter_app/exceptions/api_exception.dart'; import 'package:mohem_flutter_app/main.dart'; import 'package:mohem_flutter_app/models/chat/chat_user_image_model.dart'; import 'package:mohem_flutter_app/models/chat/get_search_user_chat_model.dart'; +import 'package:mohem_flutter_app/models/chat/get_user_groups_by_id.dart' as groups; import 'package:mohem_flutter_app/models/chat/get_user_login_token_model.dart' as user; import 'package:mohem_flutter_app/models/chat/make_user_favotire_unfavorite_chat_model.dart' as fav; @@ -187,4 +188,24 @@ class ChatApiClient { } return imagesData; } + + //group chat apis start here. + Future getGroupsByUserId() async { + try { + Response response = await ApiClient().getJsonForResponse( + "${ApiConsts.getGroupByUserId}${AppState().chatDetails!.response!.id}", + token: AppState().chatDetails!.response!.token, + ); + if (!kReleaseMode) { + logger.i("res: " + response.body); + } + return groups.GetUserGroups.fromRawJson(response.body); + } catch (e) { + //if fail api returning 500 hence just printing here + print(e); + + throw e; + } + } + } diff --git a/lib/app_state/app_state.dart b/lib/app_state/app_state.dart index 2b05306..c434ea6 100644 --- a/lib/app_state/app_state.dart +++ b/lib/app_state/app_state.dart @@ -77,7 +77,7 @@ class AppState { bool get getIsDemoMarathon => _isDemoMarathon; - final PostParamsModel _postParamsInitConfig = PostParamsModel(channel: 31, versionID: 3.9, mobileType: Platform.isAndroid ? "android" : "ios"); + final PostParamsModel _postParamsInitConfig = PostParamsModel(channel: 31, versionID: 5.2, mobileType: Platform.isAndroid ? "android" : "ios"); void setPostParamsInitConfig() { isAuthenticated = false; @@ -180,5 +180,4 @@ class AppState { } bool cancelRequestTrancsection = true; - } diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index 96fd4b0..647c38b 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -3,8 +3,8 @@ import 'package:mohem_flutter_app/ui/marathon/widgets/question_card.dart'; class ApiConsts { //static String baseUrl = "http://10.200.204.20:2801/"; // Local server // static String baseUrl = "https://erptstapp.srca.org.sa"; // SRCA 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/"; @@ -19,6 +19,10 @@ class ApiConsts { static String chatLoginTokenUrl = chatServerBaseApiUrl + "user/"; static String chatHubConnectionUrl = chatServerBaseUrl + "ConnectionChatHub"; + //Groups + static String getGroupByUserId = chatServerBaseApiUrl + "group/getgroupsbyuserid/"; + + // static String chatSearchMember = chatLoginTokenUrl + "user/"; static String chatRecentUrl = chatServerBaseApiUrl + "UserChatHistory/"; //For a Mem static String chatSingleUserHistoryUrl = chatServerBaseApiUrl + "UserChatHistory/"; diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index 184748a..bb05818 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -511,4 +511,6 @@ abstract class LocaleKeys { static const noWinner = 'noWinner'; static const myTeam = 'myTeam'; static const youCanPlayDemo = 'youCanPlayDemo'; + static const group = 'group'; + static const searchGroup = 'searchGroup'; } diff --git a/lib/generated_plugin_registrant.dart b/lib/generated_plugin_registrant.dart index 642c8ec..b59eec2 100644 --- a/lib/generated_plugin_registrant.dart +++ b/lib/generated_plugin_registrant.dart @@ -16,7 +16,7 @@ import 'package:geolocator_web/geolocator_web.dart'; import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; import 'package:image_picker_for_web/image_picker_for_web.dart'; import 'package:just_audio_web/just_audio_web.dart'; -import 'package:record_web/record_web.dart'; +// import 'package:record_web/record_web.dart'; import 'package:shared_preferences_web/shared_preferences_web.dart'; import 'package:url_launcher_web/url_launcher_web.dart'; import 'package:video_player_web/video_player_web.dart'; @@ -35,7 +35,7 @@ void registerPlugins(Registrar registrar) { GoogleMapsPlugin.registerWith(registrar); ImagePickerPlugin.registerWith(registrar); JustAudioPlugin.registerWith(registrar); - RecordPluginWeb.registerWith(registrar); + //RecordPluginWeb.registerWith(registrar); SharedPreferencesPlugin.registerWith(registrar); UrlLauncherPlugin.registerWith(registrar); VideoPlayerPlugin.registerWith(registrar); diff --git a/lib/models/chat/get_user_groups_by_id.dart b/lib/models/chat/get_user_groups_by_id.dart new file mode 100644 index 0000000..ee5bbd9 --- /dev/null +++ b/lib/models/chat/get_user_groups_by_id.dart @@ -0,0 +1,260 @@ +import 'dart:convert'; + +class GetUserGroups { + List? response; + Null? errorResponses; + + GetUserGroups({this.response, this.errorResponses}); + factory GetUserGroups.fromRawJson(String str) => GetUserGroups.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + GetUserGroups.fromJson(Map json) { + if (json['response'] != null) { + response = []; + json['response'].forEach((v) { + response!.add(new Response.fromJson(v)); + }); + } + errorResponses = json['errorResponses']; + } + + Map toJson() { + Map data = new Map(); + if (this.response != null) { + data['response'] = this.response!.map((v) => v.toJson()).toList(); + } + data['errorResponses'] = this.errorResponses; + return data; + } +} + +class Response { + int? groupId; + String? groupName; + Null? groupIcon; + bool? isDeleted; + bool? isAdmin; + bool? canVideoC; + bool? canAudioC; + bool? canShareS; + bool? canAttach; + bool? canArchive; + bool? isMeeting; + Null? meetingTime; + Null? extUserLink; + int? callStatus; + int? groupUnreadMessageCount; + AdminUser? adminUser; + List? groupUserList; + + Response( + {this.groupId, + this.groupName, + this.groupIcon, + this.isDeleted, + this.isAdmin, + this.canVideoC, + this.canAudioC, + this.canShareS, + this.canAttach, + this.canArchive, + this.isMeeting, + this.meetingTime, + this.extUserLink, + this.callStatus, + this.groupUnreadMessageCount, + this.adminUser, + this.groupUserList}); + + Response.fromJson(Map json) { + groupId = json['groupId']; + groupName = json['groupName']; + groupIcon = json['groupIcon']; + isDeleted = json['isDeleted']; + isAdmin = json['isAdmin']; + canVideoC = json['canVideoC']; + canAudioC = json['canAudioC']; + canShareS = json['canShareS']; + canAttach = json['canAttach']; + canArchive = json['canArchive']; + isMeeting = json['isMeeting']; + meetingTime = json['meetingTime']; + extUserLink = json['extUserLink']; + callStatus = json['callStatus']; + groupUnreadMessageCount = json['groupUnreadMessageCount']; + adminUser = json['adminUser'] != null + ? new AdminUser.fromJson(json['adminUser']) + : null; + if (json['groupUserList'] != null) { + groupUserList = []; + json['groupUserList'].forEach((v) { + groupUserList!.add(new GroupUserList.fromJson(v)); + }); + } + } + + Map toJson() { + Map data = new Map(); + data['groupId'] = this.groupId; + data['groupName'] = this.groupName; + data['groupIcon'] = this.groupIcon; + data['isDeleted'] = this.isDeleted; + data['isAdmin'] = this.isAdmin; + data['canVideoC'] = this.canVideoC; + data['canAudioC'] = this.canAudioC; + data['canShareS'] = this.canShareS; + data['canAttach'] = this.canAttach; + data['canArchive'] = this.canArchive; + data['isMeeting'] = this.isMeeting; + data['meetingTime'] = this.meetingTime; + data['extUserLink'] = this.extUserLink; + data['callStatus'] = this.callStatus; + data['groupUnreadMessageCount'] = this.groupUnreadMessageCount; + if (this.adminUser != null) { + data['adminUser'] = this.adminUser!.toJson(); + } + if (this.groupUserList != null) { + data['groupUserList'] = + this.groupUserList!.map((v) => v.toJson()).toList(); + } + return data; + } +} + +class AdminUser { + int? id; + String? userName; + String? email; + Null? phone; + String? title; + int? userStatus; + Null? image; + int? unreadMessageCount; + Null? userAction; + bool? isPin; + bool? isFav; + bool? isAdmin; + Null? rKey; + int? totalCount; + + AdminUser( + {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.rKey, + this.totalCount}); + + AdminUser.fromJson(Map json) { + id = json['id']; + userName = json['userName']; + email = json['email']; + phone = json['phone']; + title = json['title']; + userStatus = json['userStatus']; + image = json['image']; + unreadMessageCount = json['unreadMessageCount']; + userAction = json['userAction']; + isPin = json['isPin']; + isFav = json['isFav']; + isAdmin = json['isAdmin']; + rKey = json['rKey']; + totalCount = json['totalCount']; + } + + Map toJson() { + Map data = new Map(); + data['id'] = this.id; + data['userName'] = this.userName; + data['email'] = this.email; + data['phone'] = this.phone; + data['title'] = this.title; + data['userStatus'] = this.userStatus; + data['image'] = this.image; + data['unreadMessageCount'] = this.unreadMessageCount; + data['userAction'] = this.userAction; + data['isPin'] = this.isPin; + data['isFav'] = this.isFav; + data['isAdmin'] = this.isAdmin; + data['rKey'] = this.rKey; + data['totalCount'] = this.totalCount; + return data; + } +} + +class GroupUserList { + int? id; + String? userName; + String? email; + Null? phone; + String? title; + int? userStatus; + Null? image; + int? unreadMessageCount; + int? userAction; + bool? isPin; + bool? isFav; + bool? isAdmin; + Null? rKey; + int? totalCount; + + GroupUserList( + {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.rKey, + this.totalCount}); + + GroupUserList.fromJson(Map json) { + id = json['id']; + userName = json['userName']; + email = json['email']; + phone = json['phone']; + title = json['title']; + userStatus = json['userStatus']; + image = json['image']; + unreadMessageCount = json['unreadMessageCount']; + userAction = json['userAction']; + isPin = json['isPin']; + isFav = json['isFav']; + isAdmin = json['isAdmin']; + rKey = json['rKey']; + totalCount = json['totalCount']; + } + + Map toJson() { + Map data = new Map(); + data['id'] = this.id; + data['userName'] = this.userName; + data['email'] = this.email; + data['phone'] = this.phone; + data['title'] = this.title; + data['userStatus'] = this.userStatus; + data['image'] = this.image; + data['unreadMessageCount'] = this.unreadMessageCount; + data['userAction'] = this.userAction; + data['isPin'] = this.isPin; + data['isFav'] = this.isFav; + data['isAdmin'] = this.isAdmin; + data['rKey'] = this.rKey; + data['totalCount'] = this.totalCount; + return data; + } +} diff --git a/lib/provider/chat_provider_model.dart b/lib/provider/chat_provider_model.dart index 08b148d..dc6350f 100644 --- a/lib/provider/chat_provider_model.dart +++ b/lib/provider/chat_provider_model.dart @@ -21,6 +21,7 @@ import 'package:mohem_flutter_app/main.dart'; import 'package:mohem_flutter_app/models/chat/chat_user_image_model.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_user_groups_by_id.dart' as groups; import 'package:mohem_flutter_app/models/chat/get_user_login_token_model.dart' as userLoginToken; import 'package:mohem_flutter_app/models/chat/make_user_favotire_unfavorite_chat_model.dart' as fav; import 'package:mohem_flutter_app/models/my_team/get_employee_subordinates_list.dart'; @@ -40,6 +41,8 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { TextEditingController message = TextEditingController(); TextEditingController search = TextEditingController(); + TextEditingController searchGroup = TextEditingController(); + List userChatHistory = [], repliedMsg = []; List? pChatHistory, searchedChats; String chatCID = ''; @@ -67,7 +70,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { late PlayerController playerController; List getEmployeeSubordinatesList = []; List teamMembersList = []; - + groups.GetUserGroups userGroups =groups.GetUserGroups(); Material.TextDirection textDirection = Material.TextDirection.ltr; //Chat Home Page Counter @@ -125,6 +128,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { Future getUserRecentChats() async { ChatUserModel recentChat = await ChatApiClient().getRecentChats(); ChatUserModel favUList = await ChatApiClient().getFavUsers(); + userGroups = await ChatApiClient().getGroupsByUserId(); if (favUList.response != null && recentChat.response != null) { favUsersList = favUList.response!; favUsersList.sort((ChatUser a, ChatUser b) => a.userName!.toLowerCase().compareTo(b.userName!.toLowerCase())); @@ -1466,4 +1470,11 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { } } } + + //group chat functions added here + + void filterGroups(String value) async { + // filter function added here. + notifyListeners(); + } } diff --git a/lib/ui/chat/chat_home.dart b/lib/ui/chat/chat_home.dart index 2e23254..75d916a 100644 --- a/lib/ui/chat/chat_home.dart +++ b/lib/ui/chat/chat_home.dart @@ -10,6 +10,7 @@ import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/provider/chat_provider_model.dart'; import 'package:mohem_flutter_app/ui/chat/chat_home_screen.dart'; import 'package:mohem_flutter_app/ui/chat/favorite_users_screen.dart'; +import 'package:mohem_flutter_app/ui/chat/group_chat.dart'; import 'package:mohem_flutter_app/ui/chat/my_team_screen.dart'; import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart'; import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; @@ -46,6 +47,7 @@ class _ChatHomeState extends State { data.getUserAutoLoginToken().whenComplete(() async { await data.buildHubConnection(); data.getUserRecentChats(); + }); return; } @@ -89,8 +91,9 @@ class _ChatHomeState extends State { child: Row( children: [ myTab(LocaleKeys.mychats.tr(), 0), - myTab(LocaleKeys.favorite.tr(), 1), - AppState().getempStatusIsManager ? myTab(LocaleKeys.myTeam.tr(), 2) : const SizedBox(), + myTab(LocaleKeys.group.tr(), 1), + myTab(LocaleKeys.favorite.tr(), 2), + AppState().getempStatusIsManager ? myTab(LocaleKeys.myTeam.tr(), 3) : const SizedBox(), ], ), ), @@ -104,6 +107,7 @@ class _ChatHomeState extends State { }, children: [ ChatHomeScreen(), + GropChatHomeScreen(), ChatFavoriteUsersScreen(), AppState().getempStatusIsManager ? const MyTeamScreen() : const SizedBox(), ], diff --git a/lib/ui/chat/group_chat.dart b/lib/ui/chat/group_chat.dart new file mode 100644 index 0000000..b3d7301 --- /dev/null +++ b/lib/ui/chat/group_chat.dart @@ -0,0 +1,182 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_svg/flutter_svg.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/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/provider/chat_provider_model.dart'; +import 'package:mohem_flutter_app/ui/chat/chat_detailed_screen.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:pull_to_refresh/pull_to_refresh.dart'; + +class GropChatHomeScreen extends StatefulWidget { + const GropChatHomeScreen({Key? key}) : super(key: key); + + @override + State createState() => _GropChatHomeScreenState(); +} + +class _GropChatHomeScreenState extends State { + TextEditingController search = TextEditingController(); + + @override + void initState() { + + super.initState(); + } + + @override + void dispose() { + super.dispose(); + search.clear(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: MyColors.white, + body: Consumer( + builder: (BuildContext context, ChatProviderModel m, Widget? child) { + return m.isLoading + ? ChatHomeShimmer( + isDetailedScreen: false, + ) + : Column( + children: [ + TextField( + controller: m.searchGroup, + style: const TextStyle(color: MyColors.darkTextColor, fontWeight: FontWeight.w500, fontSize: 12), + onChanged: (String val) { + m.filter(val); + }, + decoration: InputDecoration( + border: fieldBorder(radius: 5, color: 0xFFE5E5E5), + focusedBorder: fieldBorder(radius: 5, color: 0xFFE5E5E5), + enabledBorder: fieldBorder(radius: 5, color: 0xFFE5E5E5), + contentPadding: const EdgeInsets.all(11), + hintText: LocaleKeys.searchGroup.tr(), + hintStyle: const TextStyle(color: MyColors.lightTextColor, fontStyle: FontStyle.italic, fontWeight: FontWeight.w500, fontSize: 12), + filled: true, + fillColor: MyColors.greyF7Color, + suffixIconConstraints: const BoxConstraints(), + suffixIcon: m.search.text.isNotEmpty + ? IconButton( + constraints: const BoxConstraints(), + onPressed: () { + m.clearSelections(); + }, + icon: const Icon(Icons.clear, size: 22), + color: MyColors.redA3Color, + ) + : null, + ), + ).paddingOnly(top: 20, bottom: 14), + if (m.userGroups.response != null) + ListView.separated( + itemCount: m.userGroups.response!.length, + shrinkWrap: true, + physics: const ClampingScrollPhysics(), + padding: const EdgeInsets.only(bottom: 80.0), + itemBuilder: (BuildContext context, int index) { + return SizedBox( + height: 55, + child: Row( + children: [ + + Container( + alignment: Alignment.center, + width: 48, + height: 48, + padding: const EdgeInsets.all(10.0), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(24.0), + border: Border.all(width: 1, color: Colors.black), + + ), + child: SvgPicture.asset( + "assets/images/chat-group.svg", + + )), + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + (m.userGroups?.response![index].groupName!.toText14(color: MyColors.darkTextColor).paddingOnly(left: 11, top: 16) + )!.expanded, + ]) + + ], + ), + ).onPress(() { + Navigator.pushNamed( + context, + AppRoutes.chatDetailed, + arguments: ChatDetailedScreenParams(m.searchedChats![index], false), + ).then((Object? value) { + m.clearSelections(); + m.notifyListeners(); + }); + }); + }, + separatorBuilder: (BuildContext context, int index) => const Divider(color: MyColors.black).paddingOnly(left: 59), + ).expanded, + ], + ).paddingOnly(left: 21, right: 21); + }, + ), + 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 { + print(AppState().chatDetails!.response!.token); + showMyBottomSheet( + context, + callBackFunc: () {}, + child: SearchEmployeeBottomSheet( + title: LocaleKeys.searchForEmployee.tr(), + apiMode: LocaleKeys.delegate.tr(), + fromChat: true, + onSelectEmployee: (_selectedEmployee) {}, + ), + ); + }, + ), + ); + } + + OutlineInputBorder fieldBorder({required double radius, required int color}) { + return OutlineInputBorder( + borderRadius: BorderRadius.circular(radius), + borderSide: BorderSide( + color: Color(color), + ), + ); + } +}