Laravel 5.8 and React Native using pusher: error listening to private channel: No callbacks on conversations34 for pusher:subscription_succeeded - reactjs

The problem is when the app developer tries to listen to the event via pusher private channel. Here's the laravel code.
routes/api.php
Route::middleware('auth:api')->post('/broadcast/auth', 'Api\Auth\BroadcastAuthController#auth');
routs/channels.php:
// notifications channel
Broadcast::channel('users.{id}', function ($user, $id) {
return (int)$user->id === (int)$id;
});
// conversations channel
Broadcast::channel('conversations.{conversation}', function ($user, \App\Models\Conversation
$conversation) {
return $conversation->users->contains($user);
});
MessageSent event
class MessageSent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $message;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct($message)
{
$this->message = $message;
}
/**
* Get the channels the event should broadcast on.
*
* #return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('conversations.' . $this->message->conversation_id);
}
/**
* Load user relation.
*
* #return array
*/
public function broadcastWith()
{
return [
'message' => $this->message->load('user')
];
}
}
React receiving the message
var Echotest= new Echo({
broadcaster: 'pusher',
key: 'xxxxxxxxxxx',
forceTLS: true,
cluster:"eu",
authEndpoint: 'https://example.com/api/broadcast/auth' ,
auth: {
headers: {
Authorization: "Bearer " + token ,
}
}
});
console.log(Echotest);
Echotest.channel("conversations" + this.state.conversation_id)
.listen("App\Events\MessageSent", e => {
alert(e)
});
But the result is:
Pusher : Event sent : {"event":"pusher:subscribe","data":{"channel":"conversations34"}}
Pusher : Event recd :
{"event":"pusher_internal:subscription_succeeded","channel":"conversations34","data":{}}
Pusher : No callbacks on conversations34 for pusher:subscription_succeeded
Is there anything else need to be done in Laravel to make it work in react? or the issue in react and need to be fixed? Please advise and thanks in advance.

Just Change the "Echotest.channel" to "Echotest.private"

Related

How to send notifications to Laravel Users with Onesignal notification channel?

I'm trying to use laravel-notification-channels/onesignal with Laravel 9, Inertia React project.
For first I setup the client in this way:
useEffect(() => {
OneSignal.init({
appId: "PRIVATE-KEY"
});
}, []);
Testing from Onesignal panel the client is listening.
For the back-end I have created a Notification:
<?php
namespace App\Notifications;
use NotificationChannels\OneSignal\OneSignalChannel;
use NotificationChannels\OneSignal\OneSignalMessage;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\BroadcastMessage;
use App\Models\Order;
class OrderPlacedNotification extends Notification
{
use Queueable;
public $order;
/**
* Create a new notification instance.
*
* #return void
*/
public function __construct(Order $order)
{
$this->order = $order;
}
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via($notifiable)
{
return ['database', 'broadcast', OneSignalChannel::class];
}
/**
* Get the array representation of the notification.
*
* #param mixed $notifiable
* #return array
*/
public function toArray($notifiable)
{
return [
'order' => $this->order,
];
}
public function toBroadcast($notifiable)
{
return new BroadcastMessage([
'order' => $this->order
]);
}
public function toOneSignal($notifiable)
{
return OneSignalMessage::create()
->setSubject("Nuovo ordine!")
->setBody("Vedi l'ordine.")
->setUrl('http://onesignal.com');
}
}
and I send the notification via controller to all users.
All config setted but I can't listen to the user.
I found the solution. In my client I subscibe user to the specific interest and in backend I send notifictions at the users with that specific interest:
Frontend
useEffect(() => {
if(auth.user) {
window.Echo.private(`App.Models.User.${auth.user.id}`).notification(notification => {
console.log(notification);
setNotifications(() => [...notifications, notification]);
})
OneSignal.init(
{
appId: "KEY",
},
//Automatically subscribe to the new_app_version tag
OneSignal.sendTag("orders", "orders", tagsSent => {
// Callback called when tag has finished sending
console.log('TAG SENT', tagsSent);
})
);
}
}, [notifications]);
User Model:
public function routeNotificationForOneSignal()
{
return ['tags' => ['key' => 'orders', 'relation' => '=', 'value' => 'orders']];
}

Laravel Lighthouse, how can i get all array data when i do subscription?

I do subscription with laravel + Lighthouse + Laravel WebSockets + vue-apollo tech.
When i subscription, i wanna get all array data, but i only got changed data.
My schema.graphql is below.
type Mutation {
updateTest(id: ID!, name: String, result: Int): Test #update
#broadcast(subscription: "testSub")
}
type Subscription {
testSub(id: ID): Test
}
type Test {
id: ID!
name: String
result: Int
}
This is my vue-apollo code
const subQuery = gql`subscription testSub($id: ID) {
testSub(id:$id) {
id
name
}
}`
const observer = this.$apollo.subscribe({
query: subQuery,
variables () {
return {
id: 14,
}
},
})
observer.subscribe({
next (data) {
console.log('subscribe')
console.log(data)
},
error (error) {
console.log('err')
console.error(error)
},
})
When i do mutation like below.
mutation {
updateTest(id:14, name: "hahaha", result:1) {
id
name
}
}
vue-apollo get subscription like pic.
I recognized return is only changed value instead of all data.
So i change subscription schema like below.
type Subscription {
testSub: [Test] #all
}
I also changed vue-apollo code.
const subQuery = gql`subscription testSub { #delete argument
testSub { #delete argument
id
name
}
}`
const observer = this.$apollo.subscribe({
query: subQuery,
variables () {
return {
id: 14,
}
},
})
observer.subscribe({
next (data) {
console.log('subscribe')
console.log(data)
},
error (error) {
console.log('err')
console.error(error)
},
})
When i do mutation after npm run dev and websocket start, i got this error.
But i already made testSub.
php artisan lighthouse:subscription testSub
This is my testSub file.
<?php
namespace App\GraphQL\Subscriptions;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
use GraphQL\Type\Definition\ResolveInfo;
use Nuwave\Lighthouse\Subscriptions\Subscriber;
use Nuwave\Lighthouse\Schema\Types\GraphQLSubscription;
use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;
use App\Test as Test2;
use App\Events\test;
class TestSub extends GraphQLSubscription
{
/**
* Check if subscriber is allowed to listen to the subscription.
*
* #param \Nuwave\Lighthouse\Subscriptions\Subscriber $subscriber
* #param \Illuminate\Http\Request $request
* #return bool
*/
public function authorize(Subscriber $subscriber, Request $request): bool
{
// TODO implement authorize
return true;
}
/**
* Filter which subscribers should receive the subscription.
*
* #param \Nuwave\Lighthouse\Subscriptions\Subscriber $subscriber
* #param mixed $root
* #return bool
*/
public function filter(Subscriber $subscriber, $root): bool
{
return true;
// TODO implement filter
}
public function encodeTopic(Subscriber $subscriber, string $fieldName): string
{
// Optionally create a unique topic name based on the
// `author` argument.
//$args = $subscriber->args;
//return Str::snake($fieldName).':'.$args['author'];
//return Str::snake($fieldName).':1';
return 'testSub';
}
/**
* Decode topic name.
*
* #param string $fieldName
* #param \App\Post $root
* #return string
*/
public function decodeTopic(string $fieldName, $root): string
{
// Decode the topic name if the `encodeTopic` has been overwritten.
$author_id = $root->author_id;
//return Str::snake($fieldName).':'.$author_id;
return 'testSub';
}
public function resolve($root, array $args, GraphQLContext $context, ResolveInfo $resolveInfo): Test2
{
event(new test());
return $root;
}
}
How can i get all array data instead of changed data?
In your vue-apollo code you have this:
gql`subscription testSub($id: ID) {
testSub(id:$id) {
id
name
}
}`
So, think that it is like a query. Everytime the subscription is fired, you are querying the id and name. If you want to also query the result, just add it:
gql`subscription testSub($id: ID) {
testSub(id:$id) {
id
name
result # <--
}
}`
There is no way to tell to apollo that has to fetch "all" fields of the Test type. You have to explicit those fields.

ng:areq fn is not a function got objectService angularjs es6

I have 1 service which extends other. When I try to import it in controller got this error ng:areq fn is not a function got UserRepository. I think my problem is in right way injecting this service. Here is my Service:
import { USER_REPO_CONFIG } from '../repository.config';
import { AbstractRepository } from '../abstract.repository';
import { UserModel } from './user.model';
import 'rxjs/add/operator/map';
class UserRepository extends AbstractRepository {
constructor($http) {
'NgInject'
super($http, USER_REPO_CONFIG, UserModel);
}
/**
* Gets the user by ID
*
* #param {number} userId - ID of a user to find
* #returns {Observable<UserModel>} - User model instance
*/
getUser(userId) {
return this.getItem(userId);
}
/**
* Creates the user
*
* #param {UserModel} user - User model instance
* #returns {Observable<UserModel>} - User model instance observable
*/
createUser(user) {
return this.createItem(user);
}
/**
* Updates the user
*
* #param {number} userId - ID of a user to update
* #param {UserModel} user - User model instance
* #returns {Observable<UserModel>} - User model instance observable
*/
updateUser(userId, user) {
return this.updateItem(userId, user);
}
}
export { UserRepository };
here is my Module:
import { HelloComponent } from './hello/hello.component';
import { UserRepository } from '../core/repository/user/user.repository';
const greetingModule = angular
.module('app.greeting',[])
.component('helloComponent', new HelloComponent)
.service('UserRepository', new UserRepository)
.name;
export { greetingModule };
and my Controller:
import { UserRepository } from '../../core/repository/user/user.repository';
class HelloController {
constructor(UserRepository) {
this.hello = 'Hello World';
this.service = UserRepository;
}
getAll() {
console.log('user Repository', this.service)
}
}
HelloController.$inject = ['UserRepository'];
export { HelloController };
.service('UserRepository', new UserRepository)
.service() expects a function, you pass an object. You can pass the class directly:
.service('UserRepository', UserRepository)
Passing the class effectively passes the constructor function.

FOSUserBundle: Success target after password reset according to roles

After the user did reset his password using the password reset of FOSUserBundle, by default he is redirected to the FOSUserProfile. I want to redirect to a different route according to their role. Is this possible and if yes, how?
I do this code but it redirect all kind of users
namespace Acme\UserBundle\EventListener;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FormEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
/**
* Listener responsible to change the redirection at the end of the password resetting
*/
class PasswordResettingListener implements EventSubscriberInterface {
private $router;
public function __construct(UrlGeneratorInterface $router) {
$this->router = $router;
}
public static function getSubscribedEvents() {
return [
FOSUserEvents::RESETTING_RESET_SUCCESS => 'onPasswordResettingSuccess',
];
}
public function onPasswordResettingSuccess(FormEvent $event) {
$url = $this->router->generate('homepage');
$event->setResponse(new RedirectResponse($url));
}
}
And then I registering it as a service with
services:
acme_user.password_resetting:
class: Acme\UserBundle\EventListener\PasswordResettingListener
arguments: [ "#router" ]
tags:
- { name: kernel.event_subscriber }
Based on your version of Symfony you can choose one of the approaches described in: http://symfony.com/blog/new-in-symfony-2-6-security-component-improvements
For example you can use security.authorization_checker service:
Inject it into your service:
services:
acme_user.password_resetting:
class: Acme\UserBundle\EventListener\PasswordResettingListener
arguments: [ "#router", "#security.authorization_checker" ]
tags:
- { name: kernel.event_subscriber }
Then in your actual service:
use Symfony\Component\Security\Core\Authorization\AuthorizationChecker;
/**
* Listener responsible to change the redirection at the end of the password resetting
*/
class PasswordResettingListener implements EventSubscriberInterface {
private $router;
private $authorizationChecker;
public function __construct(UrlGeneratorInterface $router, AuthorizationChecker $authorizationChecker) {
$this->authorizationChecker = $authorizationChecker;
$this->router = $router;
}
public static function getSubscribedEvents() {
return [
FOSUserEvents::RESETTING_RESET_SUCCESS => 'onPasswordResettingSuccess',
];
}
public function onPasswordResettingSuccess(FormEvent $event) {
//$url = $this->router->generate('homepage');
//$event->setResponse(new RedirectResponse($url));
if (false === $this->authorizationChecker->isGranted('ROLE_ADMIN')) {
// redirect somewhere
} else {
// redirect elsewhere
}
}
}

Socket authentication with node.js & socket.io (using DaftMonk's generator-angular-fullstack)

I'm using DaftMonk's generator-angular-fullstack for a project with everything set as default,
I find myself needing socket authentication with it so I have enabled "socketio-jwt" on the socketio.js
and on the Angular service.
This is how my Angular service looks like:
/* global io */
'use strict';
angular.module('myApp')
.factory('socket', function(socketFactory, Auth) {
// socket.io now auto-configures its connection when we ommit a connection url
var ioSocket = io('', {
// Send auth token on connection, you will need to DI the Auth service above
query: 'token=' + Auth.getToken(),
path: '/socket.io-client'
});
var socket = socketFactory({
ioSocket: ioSocket
});
return {
socket: socket,
/**
* Register listeners to sync an array with updates on a model
*
* Takes the array we want to sync, the model name that socket updates are sent from,
* and an optional callback function after new items are updated.
*
* #param {String} modelName
* #param {Array} array
* #param {Function} cb
*/
syncUpdates: function (modelName, array, cb) {
cb = cb || angular.noop;
/**
* Syncs item creation/updates on 'model:save'
*/
socket.on(modelName + ':save', function (item) {
var oldItem = _.find(array, {_id: item._id});
var index = array.indexOf(oldItem);
var event = 'created';
// replace oldItem if it exists
// otherwise just add item to the collection
if (oldItem) {
array.splice(index, 1, item);
event = 'updated';
} else {
array.push(item);
}
cb(event, item, array);
});
/**
* Syncs removed items on 'model:remove'
*/
socket.on(modelName + ':remove', function (item) {
var event = 'deleted';
_.remove(array, {_id: item._id});
cb(event, item, array);
});
},
/**
* Removes listeners for a models updates on the socket
*
* #param modelName
*/
unsyncUpdates: function (modelName) {
socket.removeAllListeners(modelName + ':save');
socket.removeAllListeners(modelName + ':remove');
}
};
});
This is how my socketio file on the server looks like:
/**
* Socket.io configuration
*/
'use strict';
var config = require('./environment');
// When the user disconnects.. perform this
function onDisconnect(socket) {
}
// When the user connects.. perform this
function onConnect(socket) {
//I dont have any decoded_token here
console.log(socket.handshake.decoded_token._id, 'connected');
// When the client emits 'info', this listens and executes
socket.on('info', function (data) {
console.info('[%s] %s', socket.address, JSON.stringify(data, null, 2));
});
// Insert sockets below
require('../api/conversation/conversation.socket').register(socket);
}
module.exports = function (socketio) {
// socket.io (v1.x.x) is powered by debug.
// In order to see all the debug output, set DEBUG (in server/config/local.env.js) to including the desired scope.
//
// ex: DEBUG: "http*,socket.io:socket"
// We can authenticate socket.io users and access their token through socket.handshake.decoded_token
//
// 1. You will need to send the token in `client/components/socket/socket.service.js`
//
// 2. Require authentication here:
// socketio.use(require('socketio-jwt').authorize({
// secret: config.secrets.session,
// handshake: true
// }));
socketio.use(require('socketio-jwt').authorize({
secret: config.secrets.session,
handshake: true
}));
socketio.on('connection', function (socket) {
socket.address = socket.handshake.address !== null ?
socket.handshake.address.address + ':' + socket.handshake.address.port :
process.env.DOMAIN;
socket.connectedAt = new Date();
// Call onDisconnect.
socket.on('disconnect', function () {
onDisconnect(socket);
console.info('[%s] DISCONNECTED', socket.address);
});
// Call onConnect.
onConnect(socket);
console.info('[%s] CONNECTED', socket.address);
});
};
I have read this blog post about socket authentication, and expected my socket to have a decoded_token value but it does not, I verified that the jwt sign attaches the userId to the token
but I still don't see it...
This is my jwt sign:
/**
* Returns a jwt token signed by the app secret
*/
function signToken(id) {
return jwt.sign({ _id: id }, config.secrets.session, { expiresInMinutes: 60*5 });
}
/**
* Set token cookie directly for oAuth strategies
*/
function setTokenCookie(req, res) {
if (!req.user) return res.json(404, { message: 'Something went wrong, please try again.'});
var token = signToken(req.user._id, req.user.role);
res.cookie('token', JSON.stringify(token));
}
My question is this, how do I get the current user info attached to the socket? (just the id is fine).
Silly me, it seems like I was looking for the decoded token in the wrong place, it was here:
socket.decoded_token._id

Resources