added unread messages and unread chats counters

This commit is contained in:
Username1548
2025-02-20 16:41:07 +07:00
parent 120af45dd5
commit 93899e316f
45 changed files with 1784 additions and 1884 deletions

View File

@@ -0,0 +1,139 @@
import 'dart:developer';
import 'package:dart_pusher_channels/dart_pusher_channels.dart';
import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/websockets/ws_service.dart';
import 'package:eatvio_app/core/token_storage.dart';
class WsManager {
final WsService wsService;
WsManager({required this.wsService});
PusherChannelsClient? client;
int? userId;
PrivateChannel? _privateNotificationChannel;
PrivateChannel? _privateMessagesChannel;
PrivateChannel? _privateDirectChatChannel;
Stream<ChannelReadEvent>? get newMessagesStream => _privateMessagesChannel?.bind('App\\Events\\ChatMessageUpdated');
// TODO: change name of event to a right one
Stream<ChannelReadEvent>? get newNotificationsStream => _privateNotificationChannel?.bind('App\\Events\\ChatMessageUpdated');
Stream<ChannelReadEvent>? get directChatStream => _privateDirectChatChannel?.bind('App\\Events\\ChatMessageUpdated');
Stream<ChannelReadEvent>? get clientTypingStream => _privateDirectChatChannel?.bind('client-typing');
Future<void> initialize() async {
if (this.client == null) {
try {
await TokenStorage.haveTokenInStorage();
final wsUrl = await wsService.getWebsocket();
final options = PusherChannelsOptions.fromHost(
scheme: 'wss',
key: wsUrl.key,
host: wsUrl.host,
shouldSupplyMetadataQueries: true,
metadata: PusherChannelsOptionsMetadata.byDefault(),
port: 443,
);
client = PusherChannelsClient.websocket(
options: options,
connectionErrorHandler: (exception, trace, refresh) {
refresh();
},
minimumReconnectDelayDuration: const Duration(
seconds: 1,
),
defaultActivityDuration: const Duration(
seconds: 30,
),
waitForPongDuration: const Duration(
seconds: 30,
),
);
await client?.connect();
this.client = client;
userId = wsUrl.userId;
await _initPrivateNotificationsChannel();
await _initPrivateMessagesChannel();
client?.lifecycleStream.listen((data) {
if (data == PusherChannelsClientLifeCycleState.establishedConnection) {
_unsubscribe();
_subscribe();
}
});
client?.eventStream.listen((event) {
log(event.rootObject.toString());
});
_subscribe();
} catch (e, st) {
log(e.toString(), stackTrace: st);
}
}
}
Future<void> _initPrivateNotificationsChannel() async {
final token = await TokenStorage.getTokenFromStorage();
_privateNotificationChannel = await client?.privateChannel(
'$newNotificationsChannelName.$userId',
authorizationDelegate: EndpointAuthorizableChannelTokenAuthorizationDelegate.forPrivateChannel(
authorizationEndpoint: Uri.parse(baseUrl + '/broadcasting/auth'),
headers: {
'Authorization': 'Bearer $token',
},
),
);
}
Future<void> _initPrivateMessagesChannel() async {
final token = await TokenStorage.getTokenFromStorage();
_privateMessagesChannel = await client?.privateChannel(
'$newMessagesChannelName.$userId',
authorizationDelegate: EndpointAuthorizableChannelTokenAuthorizationDelegate.forPrivateChannel(
authorizationEndpoint: Uri.parse(baseUrl + '/broadcasting/auth'),
headers: {
'Authorization': 'Bearer $token',
},
),
);
}
Future<void> initPrivateDirectChatChannel(String chatId) async {
final token = await TokenStorage.getTokenFromStorage();
_privateDirectChatChannel = await client?.privateChannel(
'$directChatChannelName.$chatId',
authorizationDelegate: EndpointAuthorizableChannelTokenAuthorizationDelegate.forPrivateChannel(
authorizationEndpoint: Uri.parse(baseUrl + '/broadcasting/auth'),
headers: {
'Authorization': 'Bearer $token',
},
),
);
}
void _subscribe() {
_privateMessagesChannel?.subscribe();
_privateNotificationChannel?.subscribe();
}
void _unsubscribe() {
_privateMessagesChannel?.unsubscribe();
_privateNotificationChannel?.unsubscribe();
}
void subscribeDirectChatChannel() {
_privateDirectChatChannel?.subscribe();
}
void unsubscribeDirectChatChannel() {
_privateDirectChatChannel?.unsubscribe();
}
}

View File

@@ -0,0 +1,15 @@
import 'package:dio/dio.dart';
import 'package:eatvio_app/features/chat/models/websocket_info_model.dart';
import 'package:retrofit/retrofit.dart';
part 'ws_service.g.dart';
@RestApi()
abstract class WsService {
factory WsService(Dio dio, {String baseUrl}) = _WsService;
@GET('/api/profile/ws/params')
Future<WebsocketInfoModel> getWebsocket();
}

View File

@@ -0,0 +1,73 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'ws_service.dart';
// **************************************************************************
// RetrofitGenerator
// **************************************************************************
// ignore_for_file: unnecessary_brace_in_string_interps,no_leading_underscores_for_local_identifiers,unused_element,unnecessary_string_interpolations
class _WsService implements WsService {
_WsService(this._dio, {this.baseUrl, this.errorLogger});
final Dio _dio;
String? baseUrl;
final ParseErrorLogger? errorLogger;
@override
Future<WebsocketInfoModel> getWebsocket() async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _headers = <String, dynamic>{};
const Map<String, dynamic>? _data = null;
final _options = _setStreamType<WebsocketInfoModel>(
Options(method: 'GET', headers: _headers, extra: _extra)
.compose(
_dio.options,
'/api/profile/ws/params',
queryParameters: queryParameters,
data: _data,
)
.copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
);
final _result = await _dio.fetch<Map<String, dynamic>>(_options);
late WebsocketInfoModel _value;
try {
_value = WebsocketInfoModel.fromJson(_result.data!);
} on Object catch (e, s) {
errorLogger?.logError(e, s, _options);
rethrow;
}
return _value;
}
RequestOptions _setStreamType<T>(RequestOptions requestOptions) {
if (T != dynamic &&
!(requestOptions.responseType == ResponseType.bytes ||
requestOptions.responseType == ResponseType.stream)) {
if (T == String) {
requestOptions.responseType = ResponseType.plain;
} else {
requestOptions.responseType = ResponseType.json;
}
}
return requestOptions;
}
String _combineBaseUrls(String dioBaseUrl, String? baseUrl) {
if (baseUrl == null || baseUrl.trim().isEmpty) {
return dioBaseUrl;
}
final url = Uri.parse(baseUrl);
if (url.isAbsolute) {
return url.toString();
}
return Uri.parse(dioBaseUrl).resolveUri(url).toString();
}
}