Skip to content

Commit 8656b85

Browse files
committed
feat: private messages implemented
1 parent e31ee1b commit 8656b85

File tree

16 files changed

+462
-30
lines changed

16 files changed

+462
-30
lines changed

assets/tones/message_tone.mp3

34.7 KB
Binary file not shown.

ios/Podfile.lock

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ PODS:
44
- AppAuth/ExternalUserAgent (= 1.4.0)
55
- AppAuth/Core (1.4.0)
66
- AppAuth/ExternalUserAgent (1.4.0)
7+
- audioplayers (0.0.1):
8+
- Flutter
79
- FBAEMKit (11.2.1):
810
- FBAEMKit/AEM (= 11.2.1)
911
- FBAEMKit/AEM (11.2.1):
@@ -119,16 +121,20 @@ PODS:
119121
- nanopb/encode (= 2.30908.0)
120122
- nanopb/decode (2.30908.0)
121123
- nanopb/encode (2.30908.0)
124+
- path_provider (0.0.1):
125+
- Flutter
122126
- PromisesObjC (2.0.0)
123127
- sign_in_with_apple (0.0.1):
124128
- Flutter
125129

126130
DEPENDENCIES:
131+
- audioplayers (from `.symlinks/plugins/audioplayers/ios`)
127132
- Firebase/Analytics
128133
- Flutter (from `Flutter`)
129134
- flutter_facebook_auth (from `.symlinks/plugins/flutter_facebook_auth/ios`)
130135
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
131136
- google_sign_in (from `.symlinks/plugins/google_sign_in/ios`)
137+
- path_provider (from `.symlinks/plugins/path_provider/ios`)
132138
- sign_in_with_apple (from `.symlinks/plugins/sign_in_with_apple/ios`)
133139

134140
SPEC REPOS:
@@ -153,6 +159,8 @@ SPEC REPOS:
153159
- PromisesObjC
154160

155161
EXTERNAL SOURCES:
162+
audioplayers:
163+
:path: ".symlinks/plugins/audioplayers/ios"
156164
Flutter:
157165
:path: Flutter
158166
flutter_facebook_auth:
@@ -161,11 +169,14 @@ EXTERNAL SOURCES:
161169
:path: ".symlinks/plugins/flutter_secure_storage/ios"
162170
google_sign_in:
163171
:path: ".symlinks/plugins/google_sign_in/ios"
172+
path_provider:
173+
:path: ".symlinks/plugins/path_provider/ios"
164174
sign_in_with_apple:
165175
:path: ".symlinks/plugins/sign_in_with_apple/ios"
166176

167177
SPEC CHECKSUMS:
168178
AppAuth: 31bcec809a638d7bd2f86ea8a52bd45f6e81e7c7
179+
audioplayers: 455322b54050b30ea4b1af7cd9e9d105f74efa8c
169180
FBAEMKit: 5de0a7aaa854eec69bb5be20795952a63d38a5f6
170181
FBSDKCoreKit: bf655f808b040ed66a72b9922911b39d703e64f4
171182
FBSDKCoreKit_Basics: 73ebe3a27eb688ac5b5aa7e99f68992993042115
@@ -186,6 +197,7 @@ SPEC CHECKSUMS:
186197
GTMAppAuth: ad5c2b70b9a8689e1a04033c9369c4915bfcbe89
187198
GTMSessionFetcher: 43748f93435c2aa068b1cbe39655aaf600652e91
188199
nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96
200+
path_provider: d1e9807085df1f9cc9318206cd649dc0b76be3de
189201
PromisesObjC: 68159ce6952d93e17b2dfe273b8c40907db5ba58
190202
sign_in_with_apple: f3bf75217ea4c2c8b91823f225d70230119b8440
191203

lib/src/app_router.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'package:auth/src/features/auth/views/screens/login_screen.dart';
22
import 'package:auth/src/features/auth/views/screens/recover_screen.dart';
33
import 'package:auth/src/features/auth/views/screens/register_screen.dart';
44
import 'package:auth/src/features/home/views/screens/home_screen.dart';
5+
import 'package:auth/src/features/messages/views/screens/direct_message_screen.dart';
56
import 'package:auth/src/features/room/views/screens/room_screen.dart';
67
import 'package:auth/src/features/room/views/screens/rooms_screen.dart';
78
import 'package:flutter/material.dart';
@@ -21,6 +22,8 @@ class AppRouter {
2122
return RoomsScreen.route();
2223
case RoomScreen.routeName:
2324
return RoomScreen.route(settings);
25+
case DirectMessageScreen.routeName:
26+
return DirectMessageScreen.route(settings);
2427
default:
2528
return HomeScreen.route();
2629
}

lib/src/features/auth/logic/models/user.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,5 +45,5 @@ class User extends Equatable implements Comparable {
4545
}
4646

4747
@override
48-
List<Object?> get props => [id, username, email];
48+
List<Object?> get props => [id, username, email, online];
4949
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import 'dart:async';
2+
3+
import 'package:auth/src/core/socket.dart';
4+
import 'package:auth/src/features/auth/logic/models/user.dart';
5+
import 'package:auth/src/features/user/logic/repository/user_repository.dart';
6+
import 'package:bloc/bloc.dart';
7+
import 'package:equatable/equatable.dart';
8+
import 'package:socket_io_client/socket_io_client.dart';
9+
10+
part 'direct_message_event.dart';
11+
part 'direct_message_state.dart';
12+
13+
class DirectMessageBloc extends Bloc<DirectMessageEvent, DirectMessageState> {
14+
final String username;
15+
16+
final _userRepository = UserRepository();
17+
18+
final Socket socket = socketManager.socket;
19+
20+
Timer? updateTimer;
21+
22+
final bool fromMessages;
23+
24+
DirectMessageBloc(this.username, {this.fromMessages = false})
25+
: super(DirectMessageInitialState()) {
26+
initEvents();
27+
}
28+
29+
void initSockets() {
30+
socket.onConnect((_) => add(SocketConnectedEvent()));
31+
32+
if (socket.connected) {
33+
add(SocketConnectedEvent());
34+
}
35+
36+
socket.onDisconnect((_) => add(SocketDisconnectedEvent()));
37+
38+
socket.connect();
39+
}
40+
41+
void initEvents() {
42+
on<UserLoadedEvent>(_onLoaded);
43+
on<SocketConnectedEvent>(_onConnected);
44+
on<SocketDisconnectedEvent>(_onDisconnected);
45+
46+
updateTimer = Timer.periodic(
47+
Duration(seconds: 5),
48+
(_) => add(UserLoadedEvent()),
49+
);
50+
}
51+
52+
FutureOr<void> _onLoaded(
53+
UserLoadedEvent event,
54+
Emitter<DirectMessageState> emit,
55+
) async {
56+
if (state is UserLoadInProgressState) {
57+
return;
58+
}
59+
60+
final isInitial = state is DirectMessageInitialState;
61+
62+
if (isInitial) {
63+
emit.call(UserLoadInProgressState());
64+
}
65+
66+
try {
67+
final user = await _userRepository.getUser(username);
68+
69+
if (state is SocketConnectState) {
70+
emit.call(SocketConnectState(user));
71+
} else {
72+
emit.call(UserLoadSuccessState(user));
73+
74+
if (isInitial) {
75+
initSockets();
76+
}
77+
}
78+
} catch (_) {
79+
emit.call(UserLoadFailureState());
80+
}
81+
}
82+
83+
@override
84+
Future<void> close() {
85+
updateTimer?.cancel();
86+
87+
if (!fromMessages) {
88+
socketManager.dispose();
89+
}
90+
91+
return super.close();
92+
}
93+
94+
FutureOr<void> _onConnected(
95+
SocketConnectedEvent event,
96+
Emitter<DirectMessageState> emit,
97+
) {
98+
final data = state as DirectUserState;
99+
100+
emit.call(SocketConnectState(data.user));
101+
}
102+
103+
FutureOr<void> _onDisconnected(
104+
SocketDisconnectedEvent event,
105+
Emitter<DirectMessageState> emit,
106+
) {
107+
final data = state as DirectUserState;
108+
109+
emit.call(SocketDisconnectState(data.user));
110+
}
111+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
part of 'direct_message_bloc.dart';
2+
3+
abstract class DirectMessageEvent extends Equatable {
4+
const DirectMessageEvent();
5+
6+
@override
7+
List<Object> get props => [];
8+
}
9+
10+
class UserLoadedEvent extends DirectMessageEvent {}
11+
12+
class SocketDisconnectedEvent extends DirectMessageEvent {}
13+
14+
class SocketConnectedEvent extends DirectMessageEvent {}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
part of 'direct_message_bloc.dart';
2+
3+
abstract class DirectMessageState extends Equatable {
4+
const DirectMessageState();
5+
6+
@override
7+
List<Object> get props => [];
8+
}
9+
10+
class DirectMessageInitialState extends DirectMessageState {}
11+
12+
class UserLoadInProgressState extends DirectMessageState {}
13+
14+
class UserLoadFailureState extends DirectMessageState {}
15+
16+
abstract class DirectUserState extends DirectMessageState {
17+
final User user;
18+
19+
DirectUserState(this.user) : super();
20+
21+
@override
22+
List<Object> get props => [user];
23+
}
24+
25+
class UserLoadSuccessState extends DirectUserState {
26+
UserLoadSuccessState(User user) : super(user);
27+
}
28+
29+
class SocketConnectState extends DirectUserState {
30+
SocketConnectState(User user) : super(user);
31+
}
32+
33+
class SocketDisconnectState extends DirectUserState {
34+
SocketDisconnectState(User user) : super(user);
35+
}

lib/src/features/messages/logic/bloc/message_bloc.dart

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'dart:async';
22

3+
import 'package:audioplayers/audioplayers.dart';
34
import 'package:auth/src/app.dart';
45
import 'package:auth/src/core/socket.dart';
56
import 'package:auth/src/features/auth/logic/cubit/auth_cubit.dart';
@@ -10,6 +11,7 @@ import 'package:auth/src/features/messages/logic/models/typing.dart';
1011
import 'package:auth/src/features/messages/logic/repository/message_repository.dart';
1112
import 'package:bloc/bloc.dart';
1213
import 'package:equatable/equatable.dart';
14+
import 'package:flutter/material.dart';
1315
import 'package:flutter_bloc/flutter_bloc.dart';
1416

1517
part 'message_event.dart';
@@ -21,6 +23,8 @@ class MessageBloc extends Bloc<MessageEvent, MessageState> {
2123
final socket = socketManager.socket;
2224
final repository = MessageRepository();
2325

26+
final audioCache = AudioCache();
27+
2428
final limit = 30;
2529
final typingTimeout = 5000;
2630

@@ -29,10 +33,20 @@ class MessageBloc extends Bloc<MessageEvent, MessageState> {
2933

3034
Map<String, Timer> typingTimers = {};
3135

36+
final BuildContext context;
37+
38+
User? currentUser;
39+
40+
final bool fromMessages;
41+
3242
MessageBloc({
3343
required this.partnerId,
3444
required this.type,
45+
required this.context,
46+
this.fromMessages = false,
3547
}) : super(MessageInitial()) {
48+
currentUser = context.read<AuthCubit>().state;
49+
3650
initEvents();
3751

3852
initSockets();
@@ -56,7 +70,10 @@ class MessageBloc extends Bloc<MessageEvent, MessageState> {
5670
(data) {
5771
final message = Message.fromJson(data);
5872

59-
if (!_isCurrentPartner([message.from.id, message.room])) {
73+
if (!_isCurrentPartner([
74+
message.to != currentUser?.id ? message.to : message.from.id,
75+
message.room
76+
])) {
6077
return;
6178
}
6279

@@ -90,7 +107,7 @@ class MessageBloc extends Bloc<MessageEvent, MessageState> {
90107

91108
bool _isCurrentPartner([List<String?> partners = const []]) {
92109
for (final partner in partners) {
93-
if (partner == partnerId) {
110+
if (partner == partnerId || currentUser?.id == partner) {
94111
return true;
95112
}
96113
}
@@ -100,7 +117,9 @@ class MessageBloc extends Bloc<MessageEvent, MessageState> {
100117

101118
@override
102119
Future<void> close() {
103-
socketManager.dispose();
120+
if (!fromMessages) {
121+
socketManager.dispose();
122+
}
104123

105124
return super.close();
106125
}
@@ -231,6 +250,12 @@ class MessageBloc extends Bloc<MessageEvent, MessageState> {
231250

232251
cancelTypingTimer(event.message.from);
233252

253+
final currentUser = context.read<AuthCubit>().state;
254+
255+
if (event.message.from.id != currentUser?.id) {
256+
audioCache.play('tones/message_tone.mp3', isNotification: true);
257+
}
258+
234259
emit.call(MessageReceiveState(
235260
[
236261
...data.messages,

0 commit comments

Comments
 (0)