home_cubit.dart 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. import 'dart:convert';
  2. import 'package:cloud_firestore/cloud_firestore.dart';
  3. import 'package:firebase_messaging/firebase_messaging.dart';
  4. import 'package:flutter/cupertino.dart';
  5. import 'package:flutter_bloc/flutter_bloc.dart';
  6. import 'package:youtube/data/api/call_api.dart';
  7. import 'package:youtube/data/api/home_api.dart';
  8. import 'package:youtube/data/models/user_model.dart';
  9. import 'package:youtube/shared/constats.dart';
  10. import '../../../data/models/call_model.dart';
  11. import '../../../data/models/fcm_payload_model.dart';
  12. import '../../../services/fcm/firebase_notification_handler.dart';
  13. import '../../../shared/dio_helper.dart';
  14. import '../../../shared/network/cache_helper.dart';
  15. import 'home_state.dart';
  16. enum HomeTypes { users, history }
  17. class HomeCubit extends Cubit<HomeState> {
  18. HomeCubit() : super(HomeInitial());
  19. static HomeCubit get(context) => BlocProvider.of(context);
  20. final firebaseNotifications = FirebaseNotifications();
  21. void initFcm(context){
  22. firebaseNotifications.setUpFcm(context: context, onForegroundClickCallNotify: (String payload){
  23. debugPrint('Foreground Click Call Notify: $payload');
  24. });
  25. }
  26. void updateFcmToken({required String uId}) {
  27. FirebaseMessaging.instance.getToken().then((token) {
  28. UserFcmTokenModel tokenModel = UserFcmTokenModel(token: token!,uId: uId);
  29. FirebaseFirestore.instance
  30. .collection(tokensCollection)
  31. .doc(CacheHelper.getString(key: 'uId'))
  32. .set(tokenModel.toMap())
  33. .then((value) {
  34. debugPrint('User Fcm Token Updated $token');
  35. }).catchError((onError) {
  36. debugPrint(onError.toString());
  37. });
  38. });
  39. }
  40. List<UserModel> users = [];
  41. final _homeApi = HomeApi();
  42. void getUsersRealTime(){
  43. emit(LoadingGetUsersState());
  44. _homeApi.getUsersRealTime().onData((data) {
  45. if(data.size!=0){
  46. users = []; // for realtime update the list
  47. for (var element in data.docs) {
  48. if(!users.any((e) => e.id == element.id)){
  49. users.add(UserModel.fromJsonMap(map: element.data(),uId: element.id));
  50. }
  51. }
  52. emit(SuccessGetUsersState());
  53. }else{
  54. emit(ErrorGetUsersState('No users found'));
  55. }
  56. });
  57. }
  58. List<CallModel> calls = [];
  59. void getCallHistoryRealTime(){
  60. emit(LoadingGetCallHistoryState());
  61. _homeApi.getCallHistoryRealTime().onData((data) {
  62. if(data.size != 0){
  63. calls = []; // for realtime update the list
  64. for (var element in data.docs) {
  65. if(element.data()['callerId'] == CacheHelper.getString(key: 'uId')
  66. || element.data()['receiverId'] == CacheHelper.getString(key: 'uId')){ //As firebase not allow multi where query, so we get all calls and filter it
  67. if(!calls.any((e) => e.id == element.id)){
  68. var call = CallModel.fromJson(element.data());
  69. if(call.callerId == CacheHelper.getString(key: 'uId')){
  70. call.otherUser = UserModel(name: call.receiverName!, avatar: call.receiverAvatar!);
  71. }else{
  72. call.otherUser = UserModel(name: call.callerName!, avatar: call.callerAvatar!);
  73. }
  74. calls.add(call);
  75. }
  76. }
  77. }
  78. emit(SuccessGetCallHistoryState());
  79. }else{
  80. emit(ErrorGetCallHistoryState('No Call History'));
  81. }
  82. });
  83. }
  84. //#region Get Data by Normal Request
  85. void getUser(){
  86. FirebaseFirestore.instance
  87. .collection(userCollection)
  88. .get()
  89. .then((value) {
  90. if(value.size!=0){
  91. for (var element in value.docs) {
  92. if(!users.any((e) => e.id == element.id)){
  93. users.add(UserModel.fromJsonMap(map: element.data(),uId: element.id));
  94. }else{
  95. users[users.indexWhere((e) => e.id == element.id)] = UserModel.fromJsonMap(map: element.data(),uId: element.id);
  96. }
  97. }
  98. emit(SuccessGetUsersState());
  99. }else{
  100. emit(ErrorGetUsersState('No users found'));
  101. }
  102. });
  103. }
  104. void getCallHistory(){
  105. FirebaseFirestore.instance
  106. .collection(callsCollection)
  107. .get()
  108. .then((value) {
  109. debugPrint('sizeVal: ${value.size}');
  110. if(value.size != 0){
  111. for (var element in value.docs) {
  112. if(element.data()['callerId'] == CacheHelper.getString(key: 'uId') || element.data()['receiverId'] == CacheHelper.getString(key: 'uId')){
  113. calls.add(CallModel.fromJson(element.data()));
  114. }
  115. }
  116. emit(SuccessGetCallHistoryState());
  117. }else{
  118. emit(ErrorGetCallHistoryState('No Call History'));
  119. }
  120. }).catchError((onError){
  121. emit(ErrorGetCallHistoryState(onError.toString()));
  122. });
  123. }
  124. //#endregion
  125. //Call Logic ________________________________
  126. final _callApi = CallApi();
  127. bool fireCallLoading = false;
  128. Future<void> fireVideoCall({required CallModel callModel}) async {
  129. fireCallLoading = true;
  130. emit(LoadingFireVideoCallState());
  131. //1-generate call token
  132. Map<String,dynamic> queryMap = {
  133. 'channelName' : 'channel_${UniqueKey().hashCode.toString()}',
  134. 'uid' : callModel.callerId,
  135. };
  136. _callApi.generateCallToken(queryMap: queryMap).then((value){
  137. callModel.token = value['token'];
  138. callModel.channelName = value['channel_name'];
  139. //2-post call in Firebase
  140. postCallToFirestore(callModel: callModel);
  141. }).catchError((onError){
  142. fireCallLoading = false;
  143. //For test
  144. callModel.token = agoraTestToken;
  145. callModel.channelName = agoraTestChannelName;
  146. postCallToFirestore(callModel: callModel);
  147. emit(ErrorFireVideoCallState(onError.toString()));
  148. });
  149. }
  150. void postCallToFirestore({required CallModel callModel}) {
  151. _callApi.postCallToFirestore(callModel: callModel).then((value){
  152. //3-update user busy status in Firebase
  153. _callApi.updateUserBusyStatusFirestore(callModel: callModel, busy: true).then((value) {
  154. fireCallLoading = false;
  155. //4-send notification to receiver
  156. sendNotificationForIncomingCall(callModel: callModel);
  157. }).catchError((onError){
  158. fireCallLoading = false;
  159. emit(ErrorUpdateUserBusyStatus(onError.toString()));
  160. });
  161. }).catchError((onError){
  162. fireCallLoading = false;
  163. emit(ErrorPostCallToFirestoreState(onError.toString()));
  164. });
  165. }
  166. void sendNotificationForIncomingCall({required CallModel callModel}) {
  167. FirebaseFirestore.instance
  168. .collection(tokensCollection)
  169. .doc(callModel.receiverId)
  170. .get()
  171. .then((value) {
  172. if(value.exists){
  173. Map<String, dynamic> bodyMap = {
  174. 'type': 'call',
  175. 'title': 'New call',
  176. 'body': jsonEncode(callModel.toMap())
  177. };
  178. FcmPayloadModel fcmSendData = FcmPayloadModel(to: value.data()!['token'],data: bodyMap);
  179. DioHelper.postData(
  180. data: fcmSendData.toMap(), baseUrl: 'https://fcm.googleapis.com/', endPoint: 'fcm/send',
  181. ).then((value) {
  182. debugPrint('SendNotifySuccess ${value.data.toString()}');
  183. emit(SuccessFireVideoCallState(callModel: callModel));
  184. }).catchError((onError){
  185. debugPrint('Error when send Notify: $onError');
  186. fireCallLoading = false;
  187. emit(ErrorSendNotification(onError.toString()));
  188. });
  189. }
  190. }).catchError((onError){
  191. debugPrint('Error when get user token: $onError');
  192. fireCallLoading = false;
  193. emit(ErrorSendNotification(onError.toString()));
  194. });
  195. }
  196. // CallModel inComingCall;
  197. CallStatus? currentCallStatus;
  198. void listenToInComingCalls() {
  199. _callApi.listenToInComingCall().onData((data) {
  200. if(data.size!=0){
  201. for (var element in data.docs) {
  202. if(element.data()['current'] == true){
  203. String status = element.data()['status'];
  204. if(status == CallStatus.ringing.name){
  205. currentCallStatus = CallStatus.ringing;
  206. debugPrint('ringingStatus');
  207. emit(SuccessInComingCallState(callModel: CallModel.fromJson(element.data())));
  208. }
  209. }
  210. }
  211. }
  212. });
  213. }
  214. }