Skip to content

Commit

Permalink
feat: add new messages notifications
Browse files Browse the repository at this point in the history
  • Loading branch information
path-yu committed Apr 23, 2023
1 parent 9ac8158 commit 210e025
Show file tree
Hide file tree
Showing 14 changed files with 276 additions and 38 deletions.
54 changes: 50 additions & 4 deletions lib/common/firebase.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:math';

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_storage/firebase_storage.dart';
Expand Down Expand Up @@ -129,6 +131,52 @@ void updateUserChatsAndChatsId(String uid, String targetUid, String chatId) {
db.collection(ChatsKey).doc(chatId).update({'id': chatId});
}

addMessageNotification({
required String targetUid,
required String chatId,
String? id,
}) async {
var currentUser = getCurrentUser();
var uid = currentUser.uid;
var notificationDb = db.collection(NOTIFICATION);
var myNotificationData = await notificationDb
.where('uid', isEqualTo: uid)
.where('chatId', isEqualTo: chatId)
.get();
if (myNotificationData.docs.isEmpty) {
final random = Random();

// 生成一个 8 位随机数
final randomInt = random.nextInt((pow(10, 8) - 1).toInt());

// 生成一个随机的一位整数作为前导数字
final leadingDigit = random.nextInt(9) + 1;

// 将前导数字和随机数组合成一个 9 位整数类型的 ID
final localNotificationId = int.parse('$leadingDigit$randomInt');
notificationDb.add({
'type': 'newMessage',
'uid': currentUser.uid,
'targetUid': targetUid,
'count': 1,
'chatId': chatId,
'localNotificationId': localNotificationId,
'userName': currentUser.displayName
});
} else {
var target = myNotificationData.docs.first;
notificationDb.doc(target.id).update({'count': target.data()['count'] + 1});
}
}

updateMessageNotification(String id, int count) {
db.collection(NOTIFICATION).doc(id).update({'count': count});
}

// incrementMessageNotification(String id, int type, int count) {
// db.collection(NOTIFICATION).doc(id).update({'count': count + 1});
// }

Future<String> addNewChat(String targetUid) async {
var currentUser = getCurrentUser();
var result = await db.collection(ChatsKey).add({
Expand Down Expand Up @@ -281,10 +329,8 @@ Future<Map<String, dynamic>> handleChatData(
? '[voice]'
: lastMessage['content'];
}
data['showAvatar'] =
data['isMyRequest'] ? data['userPhotoURL'] : data['targetUserPhotoURL'];
data['showUserName'] =
data['isMyRequest'] ? data['userName'] : data['targetUserName'];
data['showAvatar'] = data['targetUserPhotoURL'];
data['showUserName'] = data['targetUserName'];
data['appbarTitle'] = data['targetUserName'];
return data;
}
Expand Down
5 changes: 2 additions & 3 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:flutter_chat/provider/current_primary_swatch.dart';
import 'package:flutter_chat/provider/current_switch.dart';
import 'package:flutter_chat/provider/current_user.dart';
import 'package:flutter_chat/router/index.dart';
import 'package:flutter_chat/utils/notification.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
Expand All @@ -25,6 +26,7 @@ void main() async {
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
await initNotification();
var currentUser = CurrentUser();
final SharedPreferences prefs = await SharedPreferences.getInstance();
String? brightnessValue = prefs.getString('currentBrightness');
Expand Down Expand Up @@ -70,11 +72,8 @@ void main() async {
class MyApp extends StatelessWidget {
const MyApp({super.key});

void init() async {}

@override
Widget build(BuildContext context) {
// init();
return ScreenUtilInit(
designSize: const Size(360, 690),
minTextAdapt: true,
Expand Down
36 changes: 29 additions & 7 deletions lib/pages/chat_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,12 @@ import 'package:record/record.dart';
class ChatPage extends StatefulWidget {
final Map<String, dynamic>? parentChatData;
final double? initialScrollOffset;

const ChatPage({super.key, this.parentChatData, this.initialScrollOffset});
final String? notificationId;
const ChatPage(
{super.key,
this.parentChatData,
this.initialScrollOffset,
this.notificationId});

@override
State<ChatPage> createState() => _ChatPageState();
Expand Down Expand Up @@ -62,6 +66,9 @@ class _ChatPageState extends State<ChatPage> with TickerProviderStateMixin {
bool isRecordEnd = false;
Record? record;
int startSeconds = 0;
int sendNewMessageCount = 0;
//
int? prevSendMessageTime;
final Duration _duration = const Duration(milliseconds: 150);
List<String> get pics => messageList
.where((element) => element['type'] == 'pic')
Expand All @@ -85,6 +92,9 @@ class _ChatPageState extends State<ChatPage> with TickerProviderStateMixin {
chatId = widget.parentChatData!['chatId'];
replyUid = widget.parentChatData!['replyUid'];
isMyRequest = widget.parentChatData!['isMyRequest'];
if (widget.notificationId != null) {
updateMessageNotification(widget.notificationId!, 0);
}
});
eventBus.on<ChatsChangeEvent>().listen((data) {
var index = data.value.indexWhere((element) => element['id'] == chatId);
Expand All @@ -106,6 +116,9 @@ class _ChatPageState extends State<ChatPage> with TickerProviderStateMixin {
} else {
messageList = handleVoiceMessageList(newMessageList);
}
if (widget.notificationId != null) {
updateMessageNotification(widget.notificationId!, 0);
}
});
}
});
Expand Down Expand Up @@ -289,16 +302,22 @@ class _ChatPageState extends State<ChatPage> with TickerProviderStateMixin {
return {'baseMessageData': baseMessageData, 'messageItem': messageItem};
}

handSendClick() {
handleSendClick() {
var data = createMessageData(content: _controller.text);
setState(() {
messageList.add(data['messageItem']);
_controller.text = '';
});
addMessage(chatId, data['baseMessageData']!);
sendMessage(chatId, data['baseMessageData']!);
scrollToBottom();
}

void sendMessage(String chatId, Map<dynamic, dynamic> message) {
addMessage(chatId, message);
addMessageNotification(
targetUid: replyUid, chatId: chatId, id: widget.notificationId);
}

void addImgMessage(List<String> urlList) {
var now = DateTime.now().millisecondsSinceEpoch;
var baseMessageData = urlList.map((url) {
Expand Down Expand Up @@ -398,7 +417,7 @@ class _ChatPageState extends State<ChatPage> with TickerProviderStateMixin {
setState(() {
messageList
.add({...data['messageItem'] as Map, ...createVoiceMessageData()});
addMessage(chatId, data['baseMessageData']!);
sendMessage(chatId, data['baseMessageData']!);
Future.delayed(const Duration(milliseconds: 300)).then((value) {
_scrollController.jumpTo(
_scrollController.position.maxScrollExtent,
Expand Down Expand Up @@ -842,8 +861,11 @@ class _ChatPageState extends State<ChatPage> with TickerProviderStateMixin {
border: InputBorder.none,
suffixIcon: showSendBtn
? buildIconButton(
Icons.send, handSendClick,
size: 20, iconColor: Colors.blue)
Icons.send, handleSendClick,
size: 20,
iconColor: context
.read<CurrentPrimarySwatch>()
.color)
: const SizedBox(),
isDense: true,
contentPadding: const EdgeInsets.all(8),
Expand Down
47 changes: 30 additions & 17 deletions lib/pages/components/home/home_messages.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';

class HomeMessages extends StatefulWidget {
const HomeMessages({super.key});

final Map messageNotificationMaps;
const HomeMessages({super.key, required this.messageNotificationMaps});
@override
State<HomeMessages> createState() => _HomeMessagesState();
}
Expand All @@ -31,12 +31,6 @@ class _HomeMessagesState extends State<HomeMessages> {

Map<String, StreamSubscription<QuerySnapshot<Map<String, dynamic>>>>?
unSubscribeMap = {};
@override
void setState(fn) {
if (mounted) {
super.setState(fn);
}
}

@override
void initState() {
Expand Down Expand Up @@ -103,6 +97,12 @@ class _HomeMessagesState extends State<HomeMessages> {
return true;
}

@override
void dispose() {
// TODO: implement dispose
super.dispose();
}

@override
Widget build(BuildContext context) {
return Scaffold(
Expand Down Expand Up @@ -156,6 +156,17 @@ class _HomeMessagesState extends State<HomeMessages> {
child: ListView.separated(
itemBuilder: (context, index) {
var item = chatList[index];
var messageNotificationItem =
widget.messageNotificationMaps[item['id']];
var avatarEle = ClipRRect(
borderRadius: BorderRadius.circular(5.0),
child: ClipOval(
child: buildBaseImage(
width: ScreenUtil().setWidth(40),
height: ScreenUtil().setHeight(40),
url: item['showAvatar']),
),
);
return ListTile(
onTap: () {
// Obtain shared preferences.
Expand All @@ -166,6 +177,10 @@ class _HomeMessagesState extends State<HomeMessages> {
MaterialPageRoute(
builder: (context) => ChatPage(
parentChatData: item,
notificationId:
messageNotificationItem != null
? messageNotificationItem['id']
: null,
initialScrollOffset: offset != null
? double.parse(offset)
: 0,
Expand All @@ -174,15 +189,13 @@ class _HomeMessagesState extends State<HomeMessages> {
);
});
},
leading: ClipRRect(
borderRadius: BorderRadius.circular(5.0),
child: ClipOval(
child: buildBaseImage(
width: ScreenUtil().setWidth(40),
height: ScreenUtil().setHeight(40),
url: item['showAvatar']),
),
),
leading: messageNotificationItem != null &&
messageNotificationItem['count'] != 0
? Badge.count(
count: messageNotificationItem['count'],
child: avatarEle,
)
: avatarEle,
title: buildOneLineText(item['showUserName']),
subtitle: item['lastMessage'] != null
? buildOneLineText(
Expand Down
54 changes: 48 additions & 6 deletions lib/pages/home_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:flutter_chat/eventBus/index.dart';
import 'package:flutter_chat/pages/components/home/home_contacts.dart';
import 'package:flutter_chat/pages/components/home/home_messages.dart';
import 'package:flutter_chat/provider/current_user.dart';
import 'package:flutter_chat/utils/notification.dart';
import 'package:provider/provider.dart';

class HomePage extends StatefulWidget {
Expand All @@ -16,7 +17,15 @@ class HomePage extends StatefulWidget {
class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
int currentIndex = 0;
int newFriendsBadgeCount = 0;
int newMessageCount = 0;
// int newMessageCount = 0;
Map messageNotificationMaps = {};

int get newMessageCount {
return messageNotificationMaps.values.fold(0, (previousValue, element) {
int count = element['count'];
return previousValue + count;
});
}

void handleOnTap(int? index) {
setState(() {
Expand All @@ -36,7 +45,13 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
context.read<CurrentUser>().setCurrentUser(data);
eventBus.fire(UserChangeEvent(data));
});
// listener add contact notification
listenAddContactNotification();
listenMessageNotification();
setNotificationListener();
print('init');
}

void listenAddContactNotification() {
db
.collection(NOTIFICATION)
.where('targetEmail', isEqualTo: getCurrentUser().email)
Expand All @@ -55,9 +70,28 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
});
}
});
// initUniLinks();
}

void listenMessageNotification() {
db
.collection(NOTIFICATION)
.where('targetUid', isEqualTo: getCurrentUser().uid)
.where('type', isEqualTo: 'newMessage')
.snapshots()
.listen((querySnapshot) {
setState(() {
for (var e in querySnapshot.docs) {
var data = e.data();
data['id'] = e.id;
messageNotificationMaps[e['chatId']] = data;
addNotification(
'${data['userName']}:${data['count']} new messages',
{'chatId': data['chatId'], 'notificationId': e.id},
e['localNotificationId']);
}
});
});
}
// Future<void> initUniLinks() async {
// print('init');
// // ... check initialLink
Expand All @@ -77,8 +111,14 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
@override
Widget build(BuildContext context) {
final List<BottomNavigationBarItem> bottomTabsList = [
const BottomNavigationBarItem(
icon: Icon(Icons.message), label: 'Messages'),
BottomNavigationBarItem(
label: 'Messages',
icon: newMessageCount != 0
? Badge.count(
count: newMessageCount,
child: const Icon(Icons.message),
)
: const Icon(Icons.message)),
BottomNavigationBarItem(
icon: newFriendsBadgeCount != 0
? Badge.count(
Expand All @@ -99,7 +139,9 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
body: IndexedStack(
index: currentIndex,
children: [
const HomeMessages(),
HomeMessages(
messageNotificationMaps: messageNotificationMaps,
),
HomeContacts(
hasNewFriends: newFriendsBadgeCount != 0,
),
Expand Down
1 change: 0 additions & 1 deletion lib/pages/login_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,6 @@ class _LoginPageState extends State<LoginPage>
Navigator.pushNamed(context, '/');
EasyLoading.dismiss();
} on FirebaseAuthException catch (e) {
print(e);
EasyLoading.dismiss();
showOkAlertDialog(context: context, message: e.message!);
}
Expand Down
Loading

0 comments on commit 210e025

Please sign in to comment.