Closed nicolasvahidzein closed 1 month ago
Hello @nicolasvahidzein !
Please, provide version of dart_pusher_channels
, a portion of a code and a full log of an error
I believe the issue is not related to the package since all the classes provided (including PusherChannelsClient
) are pure Dart classes, no annotations, no dependency injection.
I believe you are trying to run some codegen upon mobx which uses build_runner
as a transitive dependency. Anyways, I need some piece of a code along with the pubspec to help you on that.
Hello, Kerim, I am using v 1.2.1. Not sure how i can send you the code the app is massive. I always have this problem on GitHub and for the life of me i never know how to share code.
You have just sent me an error without a context. I don't even know whether it's a static error or runtime error. I need way more things to help you. Logs, screenshots of a code, a piece of a code, logs and e.t.c. So share some insights about the error, please in the ways I described. Try to look up markdown tips to paste a code. For example, wrap the code inside markdown code blocks.
@nicolasvahidzein Refer to this for markdown: https://www.freecodecamp.org/news/markdown-cheat-sheet/
I will read this article today Kerim @kerimamansaryyev and revert back. I think more than anything that my code is not well structured. I was trying to recycle the pusher connection between channel subscription but it seems wrong.
Do you have an example of the proper way to connect to different channel on the same server from within different parts of your app?
I will read this article today Kerim @kerimamansaryyev and revert back. I think more than anything that my code is not well structured. I was trying to recycle the pusher connection between channel subscription but it seems wrong.
Do you have an example of the proper way to connect to different channel on the same server from within different parts of your app?
Sure, check this project: https://github.com/kerimamansaryyev/pusher_channels_test_app
It’s implemented on Feature Driven Design with detailed docs. Here is the brief general idea: you must make your client as a singleton so you will have 1 instance of your client and later you can create instances of channels upon it, don’t worry about channel instances, their state is managed by client internally so their states shall stay up-to-date along with the event bindings - event binding streams won’t be interrupted even on client’s connection life cycle change (unless client was disposed)
So it’s a general and brief idea. You now have the reference and idea, of course, you may need adjust to your own case along with testing. Feel free to reach me, I will be monitoring the issue
That was my mistake. Not a singleton. Thank you so much.
@nicolasvahidzein Should I close the issue?
Hey Kerim. No please. Give me another day. Im rewriting everything and will share my code.
Here is my singleton, i'm quite happy. Did i miss something you think?
//flutter packages
import 'dart:async';
import 'package:dart_pusher_channels/dart_pusher_channels.dart';
//project packages
import 'package:zandu_mobile_admin/constants/main.dart';
import 'package:zandu_mobile_admin/services/user_repository.dart';
class WebsocketsSingleton {
//This creates the single instance by calling the `_internal` constructor specified below
static final WebsocketsSingleton _singleton = WebsocketsSingleton._internal();
WebsocketsSingleton._internal();
//This is what's used to retrieve the instance through the app
static WebsocketsSingleton getInstance() => _singleton;
//variables
final String _fileName = 'websockets_singleton.dart';
final _userRepository = UserRepository();
List _channelList = [];//the array that will hold all our lists
late PusherChannelsClient? pusher;
bool alreadyRun = false;
//first thing we need to do when we launch the app and log in successfully
Future<void> initialize() async {
/* trace */ if (showFunctionPrintStatements) { print('initialize function inside $_fileName'); }
if (alreadyRun == false) {
//never run yet
alreadyRun = true;
} else {
//already ran, do nothing
//the only reason we will allow this function to run again is if pusher is null
if (pusher == null) {
//pusher is null, we have a problem
} else {
//pusher is not null, we are good to go
//abort
return;
}
}
//create the base variables
//String websocketsAppKey = '';//never used
String websocketsAppId = '';
String websocketsDomain = '';
int websocketsPort = 0;
//String websocketsCluster = '';//never used
String websocketsScheme = 'ws';
//check which mode we are in
if (appMode == 'dev') {
//we are in "dev" mode
//websocketsAppKey = websocketsAppKeyDev;
websocketsAppId = websocketsAppIdDev;
websocketsDomain = websocketsDomainDev;
websocketsPort = websocketsPortDev;
//websocketsCluster = websocketsClusterDev;
if (websocketsEncryptedDev == true) {
websocketsScheme = 'wss';
}
} else {
//we are in "prod" mode
//websocketsAppKey = websocketsAppKeyProd;
websocketsAppId = websocketsAppIdProd;
websocketsDomain = websocketsDomainProd;
websocketsPort = websocketsPortProd;
//websocketsCluster = websocketsClusterProd;
if (websocketsEncryptedProd == true) {
websocketsScheme = 'wss';
}
}
//THIS WILL BE RE ADDED AS A FEATURE IN THE COMING VERSIONS
//PusherChannelsPackageConfigs.enableLogs();
//PusherChannelsPackageConfigs.disableLogs();
PusherChannelsOptions options = PusherChannelsOptions.fromHost(
scheme: websocketsScheme,
key: websocketsAppId,
host: websocketsDomain,
port: websocketsPort,
);
pusher = PusherChannelsClient.websocket(
minimumReconnectDelayDuration: const Duration(seconds: 3),
options: options,
connectionErrorHandler: (error, trace, refresh) {
print('websocket error: $error');
},
);
//Ensure you implement your logic after successfull connection to your server
//Channel? channel;//TURNED INTO A CLASS VARIABLE
//This stream will receive events and notify subscribers whenever the client is connected or reconnected after potential error
//StreamSubscription? eventSubscription;//TURNED INTO A CLASS VARIABLE
pusher!.onConnectionEstablished.listen((_) async {
});
print('will attempt to connect the pusher service');
//connect the service
pusher!.connect();
print('pusher service connection attempt done');
}
void dispose() {
for (var i = 0; i < _channelList.length; i++) {
StreamSubscription? eventSubscription = _channelList[i]['event'];
eventSubscription?.cancel();
}
//now disconnect from everywhere
disconnectFromAllChannels();
}
Future<void> connectToChannel(
String channelName,
String eventName,
Function(dynamic data) eventTriggered,
) async {
//create the final channel name because in some instances it is a concatenated value and needs to be assembled
String? defaultChannelName;
//determine which channel if any we will be calling
switch (channelName) {
case 'test_channel':
defaultChannelName = 'private-user.mike@hola.com';
break;
default:
//do nothing
}
if (pusher == null) {
//pusher is null, we have a problem
await initialize();
if (pusher == null) {
//stop on second attempt
}
} else {
//pusher is not null, we are good to go
}
//check if the defaultChannelName variable is null or not
if (defaultChannelName == null) {
//not good, it's null
//abort
return;
} else {
//it's not null, we can keep going
//make sure the channel is not already created
for (var i = 0; i < _channelList.length; i++) {
if (_channelList[i]['channel'] == channelName) {
//found an identical channel, abort
//abort
return;
}
}
Map tokenData = await _userRepository.retrieveToken();
String accessToken = tokenData['accessToken'];
//create the base variables
String websocketsAuthEndpointDomain = '';
String websocketsAuthEndpointRelativeUrl = '';
//check which mode we are in
if (appMode == 'dev') {
//we are in "dev" mode
websocketsAuthEndpointDomain = websocketsAuthEndpointDomainDev;
websocketsAuthEndpointRelativeUrl = websocketsAuthEndpointRelativeUrlDev;
} else {
//we are in "prod" mode
websocketsAuthEndpointDomain = websocketsAuthEndpointDomainProd;
websocketsAuthEndpointRelativeUrl = websocketsAuthEndpointRelativeUrlProd;
}
//PUBLIC CHANNEL EXAMPLE
//channel ??= pusher.publicChannel('my_public_channel_name');
//PRIVATE CHANNEL EXAMPLE
//connect to a private channel
Channel channel = pusher!.privateChannel(
defaultChannelName,
authorizationDelegate: EndpointAuthorizableChannelTokenAuthorizationDelegate.forPrivateChannel(
authorizationEndpoint: Uri.parse(websocketsAuthEndpointDomain + websocketsAuthEndpointRelativeUrl),
headers: {
'Authorization': 'Bearer ' + accessToken,
//'Content-Type': 'application/json',//supposedly this header was causing trouble but even after removing it I could not access the private-user.useremail channel
'Accept': 'application/json'
},
),
);
//await _eventSubscription?.cancel();
// Ensure you bind to the channel before subscribing, otherwise - you will miss some events
StreamSubscription eventSubscription = channel.bind(eventName).listen((event) {
//listen for events form the channel here
eventTriggered('hola');
});
channel.subscribe();
Map newChannel = {
'channel': channel,
'event': eventSubscription,
};
_channelList.add(newChannel);
}
}
//you can only disconnect from a channel that this class listened to, meaning you can call this from the same class that triggered the listening event on that partical channel.
Future<void> disconnectFromChannel(String channelName) async {
/* trace */ if (showFunctionPrintStatements) { print('disconnectFromChannel function inside $_fileName'); }
for (var i = 0; i < _channelList.length; i++) {
if (_channelList[i]['channel'] == channelName) {
//found the correct channel
Channel? channel = _channelList[i]['channel'];
channel?.unsubscribe();
_channelList.removeAt(i);
}
}
}
Future<void> disconnectFromAllChannels() async {
/* trace */ if (showFunctionPrintStatements) { print('disconnectFromAllChannels function inside $_fileName'); }
for (var i = 0; i < _channelList.length; i++) {
Channel? channel = _channelList[i]['channel'];
channel?.unsubscribe();
}
_channelList = [];
}
}
And this is how i import it:
`` //singletons import 'package:zandu_mobile_admin/singletons/websockets_singleton.dart';
final _websockets = WebsocketsSingleton.getInstance();
await _websockets.connectToChannel(
'test_channel',
'App\\Events\\UserStatusChange',
(dynamic data){
//data was received
print('event was received');
print(data);
}
);
``
@nicolasvahidzein how did it go? Should I close the issue?
Yes i believe so its been a while.
Hello. Thank you so much for a wonderful package, it was a lifesaver after many months of searching.
I have a question about using your package with MobX state management. It is telling me that PusherChannelsClient is an invalid type
Undefined class 'InvalidType'. Try changing the name to the name of an existing class, or creating a class with the name 'InvalidType'.dartundefined_class
It was working before i believe but it might have changed after an update perhaps.
Any pointers?
I am using websockets from many different parts of my app and connecting to different channels. Thanks.