From 116bacf428b8de76810e6cdbfa77d3155f936fb6 Mon Sep 17 00:00:00 2001 From: Alzalia Date: Fri, 8 Aug 2025 14:23:23 +0200 Subject: [PATCH] feature: update owners by websocket --- .vscode/launch.json | 26 +++++++ lib/data/repositories/owner_repository.dart | 31 ++++---- lib/data/services/websocket_client.dart | 78 +++++++++++++++++-- .../add_page/view_model/add_view_model.dart | 18 ++++- pubspec.lock | 8 ++ pubspec.yaml | 1 + 6 files changed, 139 insertions(+), 23 deletions(-) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..366c255 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,26 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "seshat", + "request": "launch", + "type": "dart", + "flutterMode": "debug", + }, + { + "name": "seshat (profile mode)", + "request": "launch", + "type": "dart", + "flutterMode": "profile" + }, + { + "name": "seshat (release mode)", + "request": "launch", + "type": "dart", + "flutterMode": "release" + } + ] +} \ No newline at end of file diff --git a/lib/data/repositories/owner_repository.dart b/lib/data/repositories/owner_repository.dart index f00ea5d..dd89d64 100644 --- a/lib/data/repositories/owner_repository.dart +++ b/lib/data/repositories/owner_repository.dart @@ -1,5 +1,8 @@ +import 'dart:async'; import 'dart:convert'; +import 'package:flutter/foundation.dart'; +import 'package:rxdart/rxdart.dart'; import 'package:seshat/data/services/api_client.dart'; import 'package:seshat/data/services/websocket_client.dart'; import 'package:seshat/domain/models/owner.dart'; @@ -14,6 +17,10 @@ class OwnerRepository { final ApiClient _apiClient; final WebsocketClient _wsClient; + final BehaviorSubject _ownersController = BehaviorSubject( + sync: true, + ); + late final StreamSubscription sub; List? _cachedOwners; Future> postOwner( @@ -29,29 +36,27 @@ class OwnerRepository { Future>> getOwners() async { if (_cachedOwners == null) { final result = await _apiClient.getOwners(); + _wsClient.connect(); if (result is Ok>) { _cachedOwners = result.value; } + sub = _wsClient.owners.listen((owner) { + debugPrint("\n\n\n\n[3] Added : $owner\n\n\n\n"); + _cachedOwners!.add(owner); + _ownersController.add(owner); + }); + return result; } else { return Result.ok(_cachedOwners!); } } - Stream liveOwners() async* { - await for (String data in await _wsClient.connect()) { - Map decodedData = jsonDecode( - data, - ).cast>(); - Owner owner = Owner.fromJSON(decodedData); - if (_cachedOwners == null) { - await getOwners(); - } else { - _cachedOwners!.add(owner); - yield* Stream.value(owner); - } - } + Stream get liveOwners => _ownersController.stream; + + dispose() { + sub.cancel(); } } diff --git a/lib/data/services/websocket_client.dart b/lib/data/services/websocket_client.dart index e2b87ba..80d7f80 100644 --- a/lib/data/services/websocket_client.dart +++ b/lib/data/services/websocket_client.dart @@ -1,16 +1,82 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:rxdart/rxdart.dart'; import 'package:seshat/config/constants.dart'; +import 'package:seshat/domain/models/owner.dart'; import 'package:web_socket_channel/web_socket_channel.dart'; class WebsocketClient { - Future> connect() async { - final channel = WebSocketChannel.connect( - Uri.parse("wss://$apiBasePath/ws"), + WebSocketChannel? _channel; + FlutterSecureStorage? _secureStorage; + final BehaviorSubject _baseController = BehaviorSubject(); + final BehaviorSubject _ownersController = BehaviorSubject( + sync: true, + ); + late final StreamSubscription sub; + + Stream get owners => _ownersController.stream; + + Future _initStore() async { + _secureStorage ??= const FlutterSecureStorage( + aOptions: AndroidOptions(encryptedSharedPreferences: true), ); + } - await channel.ready; + Future connect() async { + await _initStore(); + debugPrint("\n\n\n\nWEBSOCKET STORE IS READY\n\n\n\n"); + if (_channel != null) return; - channel.sink.add("json-token: "); + debugPrint("\n\n\n\nWEBSOCKET WILL CONNECT\n\n\n\n"); + _channel = WebSocketChannel.connect(Uri.parse("wss://$apiBasePath/ws")); + debugPrint("\n\n\n\nWEBSOCKET IS CONNECTING\n\n\n\n"); - return channel.stream; + await _channel!.ready; + debugPrint("\n\n\n\nWEBSOCKET IS READY\n\n\n\n"); + var token = await _secureStorage!.read(key: "token"); + + _channel!.sink.add(jsonEncode({"token": "$token"})); + _channel!.stream.listen((message) { + debugPrint("\n\n\n\n[1] Received : $message\n\n\n\n"); + _baseController.add(message); + }); + var data = await _baseController.stream.first; + debugPrint("\n\n\n\n$data\n\n\n\n"); + var result = jsonDecode(data); + + if (result["type"] == "auth_success") { + debugPrint("\n\n\n\nSUCCESS !\n\n\n\n"); + sub = _baseController.stream.listen( + (message) { + final Map data = jsonDecode(message); + debugPrint("\n\n\n\n[2] Transfered : $message\n\n\n\n"); + switch (data["type"]) { + case "new_owner": + final owner = Owner.fromJSON(data["data"]); + _ownersController.add(owner); + break; + default: + } + }, + onDone: _handleDisconnect, + onError: (error) { + _handleDisconnect(); + }, + ); + } + } + + void _handleDisconnect() { + sub.cancel(); + _channel = null; + } + + void dispose() { + sub.cancel(); + _channel?.sink.close(); + _ownersController.close(); } } diff --git a/lib/ui/add_page/view_model/add_view_model.dart b/lib/ui/add_page/view_model/add_view_model.dart index d62e374..269b908 100644 --- a/lib/ui/add_page/view_model/add_view_model.dart +++ b/lib/ui/add_page/view_model/add_view_model.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:mobile_scanner/mobile_scanner.dart'; @@ -14,6 +16,7 @@ class AddViewModel extends ChangeNotifier { } final OwnerRepository _ownerRepository; + late final StreamSubscription sub; /* * ==================== @@ -122,10 +125,17 @@ class AddViewModel extends ChangeNotifier { debugPrint("Oupsie daysie, ${result.error}"); } notifyListeners(); - // _ownerRepository.liveOwners().listen((Owner owner) { - // _owners.add(owner); - // notifyListeners(); - // }); + sub = _ownerRepository.liveOwners.listen((Owner owner) { + debugPrint("\n\n\n\n[5] Updated UI : $owner\n\n\n\n"); + _owners.add(owner); + notifyListeners(); + }); return result; } + + @override + void dispose() { + sub.cancel(); + super.dispose(); + } } diff --git a/pubspec.lock b/pubspec.lock index 1ca61e1..3f86d44 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -368,6 +368,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.1.5" + rxdart: + dependency: "direct main" + description: + name: rxdart + sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" + url: "https://pub.dev" + source: hosted + version: "0.28.0" sky_engine: dependency: transitive description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index 2e08d74..adee4a1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -43,6 +43,7 @@ dependencies: web_socket_channel: ^3.0.3 nested: ^1.0.0 flutter_secure_storage: ^9.2.4 + rxdart: ^0.28.0 dev_dependencies: flutter_test: