Skip to content
This repository has been archived by the owner on Sep 26, 2023. It is now read-only.

Question: Channels (api/channels.md) - Go to a channel on request #950

Closed
Buom01 opened this issue Dec 17, 2017 · 6 comments
Closed

Question: Channels (api/channels.md) - Go to a channel on request #950

Buom01 opened this issue Dec 17, 2017 · 6 comments

Comments

@Buom01
Copy link

Buom01 commented Dec 17, 2017

Hi,

By using channels, I don't find out a working solution to add an user dynamically to a channel (by a call from the client)

For exemple, I've tried without success this code:

(Custom service)

class Service {
  constructor (options) {
    this.options = options || {};
  }
  setup(app) {
    this.app = app;
  }

  async create (data, params) {
    if (Array.isArray(data)) {
      return await Promise.all(data.map(current => this.create(current)));
    }

/*
data.action: "join" or "leave",
data.type: in this case "user",
data.identifier: the user ID or it's username
*/


    this.app.channel(`${data.type}/${data.identifier}`)[data.action](params);

    return data;
  }
}


(Publication)

  app.service('users').publish((data, hook) => {
    // return app.channel('authenticated');
    return [
      app.channel(`user/${data._id}`),
      app.channel(`user/${data.username}`)
    ];
  });

But after being correctly added to the connections list of the channel by calling the custom service, I don't get update from the publications
I have verified that the channel's name of publications and channel's name dynamically subscribed by the call are same, but nothing happed when the user got a mutation.
However it works with the channel authenticated.

I think that the connection is not exactly the same as params argument, and it's probably the probleme.

So, how to add a connection to a channel when the client request it ?

@daffl
Copy link
Member

daffl commented Dec 18, 2017

The trick with channels is that they should be represented by data that has been saved to the database because channels itself are not persistent and only local to the Feathers server.

I think what you are looking for is similar to keeping the users rooms updated when they join/leave one, which could look like this:

// Get a user to leave all channels
const leaveChannels = user => {
  app.channel(app.channels).leave(connection =>
    connection.user._id === user._id
  );
};

// Update channels for updated user information
const updateUserChannels = function(user) {
  // Find all connections for this user
  const { connections } = app.channel(app.channels).filter(connection =>
    connection.user._id === user._id
  );

  // Leave all channels
  leaveChannels(user);

  // Re-join all channels with the updated user information
  connections.forEach(connection => {
    app.channel('authenticated').join(connection);
    // Assuming that the chat room/user assignment is stored
    // on an array of the user
    user.rooms.forEach(room =>
      app.channel(`rooms/${roomId}`).join(connection)
    )
  });
}

app.service('users').on('updated', updateUser);
app.service('users').on('patched', updateUser);
app.service('users').on('removed', leaveChannels);

@jakobrosenberg
Copy link

So if you have 100k connections and one person joins a new channel, does feathers run this function for every single user?

@daffl
Copy link
Member

daffl commented Dec 18, 2017

It will only run once whenever a user is updated, patched or removed. The old (v2) filter functions ran for every connection which was the big reason why we moved to the channel system (which is a lot faster and more secure).

@Buom01
Copy link
Author

Buom01 commented Dec 26, 2017

If anybody want a sample code:

/* eslint-disable no-unused-vars */

const each = require('lodash.foreach');

class Service {
  constructor (options) {
    this.options = options || {};
  }
  setup(app) {
    this.app = app;
  }

  async create (data, params) {
    if (Array.isArray(data)) {
      return await Promise.all(data.map(current => this.create(current)));
    }

    const channel = `${data.type}/${data.identifier}`;
    const action = data.action;
    if(action === "join"){
      const { connections } = this.app.channel(this.app.channels).filter(connection => {
        return connection.connection_id === params.connection_id;
      });
      console.log('join', params.connection_id, channel);
      each(connections, connection => {
        this.app.channel(channel).join(connection);
      })
    }else if(action === "leave"){
      console.log('leave', params.connection_id, channel);
      this.app.channel(channel).leave(connection => {
        return connection.connection_id === params.connection_id;
      });
    }

    // this.app.channel()[data.action]();

    return data;
  }
}

module.exports = function (options) {
  return new Service(options);
};

module.exports.Service = Service;

And in app.js;

app.configure(socketio((io)=>{
  io.use(function(socket, next) {
    socket.feathers.connection_id = socket.id;
    next();
  });
}))

Because my probleme is solved, I close the issue

@annibuliful
Copy link

Thank you for your sample code

@dottodot
Copy link

@Buom01 Thanks for the code, been tearing my hair out for hours trying to work out a way to connect to rooms like this.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants