Mazyod / PhoenixSharp

C# Phoenix Channels client. Unity Compatible.
MIT License
163 stars 27 forks source link

How to correctly handle Presence data? #27

Closed phollyer closed 1 year ago

phollyer commented 1 year ago

Hi Mazyod,

Thanks for this library, I've got multiple channels set up and working fine, which was all nice and simple 👍

I just have one problem to resolve:

I'm trying to set up Presences and add additional data as follows:

presence.ex

  def fetch("devices:" <> user_id, presences) do
    for {key, %{metas: metas}} <- presences, into: %{} do
      case Accounts.find_device(%{id: key}) do
        :not_found ->
          {key, %{metas: metas}}

        device ->
          {key, %{metas: metas, device: device}}

      end
    end
  end

DeviceChannel.cs

    private static void OnPresenceJoin(string key, Phoenix.Presence.MetadataContainer currentPresence, Phoenix.Presence.MetadataContainer newPresence)
    {
        foreach (var meta in newPresence.Metas)
        {
            try
            {
                Console.WriteLine(meta["device"]);
            }
            catch(Exception e)
            {
                Console.WriteLine($"Key not found: {e}");
            }            
        }
    }

How would I retrieve the device information? (Can I even do this?)

(I'm fairly new to C#, so sorry if that's a dumb question, but newPresence only provides access to Metas so I'm stumped)

Mazyod commented 1 year ago

Hi there, thanks for using the library!

After looking into it, this seems to be a complete oversight from my side. My apologies!

The phoenix.js presence tests focus on metas key testing, so I kind of assumed that's the only meaningful part. However, after looking into the docs, the code you shared is the recommended way to sync state with Presence objects.

I'll take a look and hopefully find a better approach. If it's simple, I'll push the fix today, otherwise, I'll have some free time to fix it properly within ~2 weeks.

Thanks for the report!

phollyer commented 1 year ago

No worries, thanks for coming back so quick.

I wrote elm-phoenix-websocket for Elm, and for handling Presence metas I simply leave the received Json untouched, and leave it to the user of the package to decode the Json seeing as how I can't possibly know it's structure ahead of time.

Maybe you could do the same, so that we could write something like:

PresenceResponse response = reply.JsonResponse<PresenceResponse>();

public class PresenceResponse
{
  public Metas metas;
  public Device device;
}

public class Metas
{
  public int online_at;
}

public class Device
{
  public string id;
  public string name;
}

Which would be consistent with handling received Json when joining a channel etc.

If I then extend the data that is sent back from Phoenix Presence, I just need to update PresenceResponse with the additional fields.

Just a thought...

Mazyod commented 1 year ago

Today's the last day of the two week timeframe, barely made it 😅

I found some various wins throughout the Json handling logic, making the library more concise and using less memory due to less arbitrary mapping of objects back and forth.

Also, improved and standardized the API to allow typed access to the Json element, while still allowing folks who want to use a different Json library to replace a single file as usual.

Still need to go through the PR for extra polish, integrate it and test it in my Unity project, then it will be good to go!

Feedback is always welcome and appreciated 👍