Better status/presence handling (T2109)
ClosedPublic

Authored by machniak on Jan 16 2017, 10:19 AM.

Details

Summary

Use client context to distinguish user presence "session".

TODO: The context string should probably be created on logon, so

using the same client in private browsing window will be
handled as a separate client

TODO: We still do not store the last used status, so when user connects

he becomes online.

Diff Detail

Repository
rKC kolab-chat
Lint
Automatic diff as part of commit; lint not applicable.
Unit
Automatic diff as part of commit; unit tests not applicable.
machniak retitled this revision from to Better status/presence handling (T2109).Jan 16 2017, 10:19 AM
machniak updated this object.
machniak edited the test plan for this revision. (Show Details)
machniak added a reviewer: Kolab Chat Developers.
machniak updated this revision to Diff 834.Jan 16 2017, 12:30 PM

Implement more status values (T2124)

One small comment above. Otherwise looks good. Will you merge the changes in my branch into this and push it all together into develop? I would +1 that.

web/channels/system_channel.ex
42

Should this return the existing status rather than online? What if the user is busy and then tries to switch to sth invalid.. this would make them online rather than keep busy, no?

seigo added inline comments.Jan 17 2017, 11:15 AM
web/channels/system_channel.ex
27–28

Just to add to my comment from yesterday... to keep this simple and performant, this could perhaps be:

update_presence_status(check_status(status, socket))

with:

defp update_presence_status(:invalid_status, _socket), do: :ok
defp update_presence_status(new_status, socket) do:
    {:ok, _} = Presence.update(socket, socket.assigns.user.username, 
                                                  %{
                                                      status: new_status,
                                                      context: socket.assigns.context
                                                    })
end

and then check_status can return :invalid_status when the status is .. well .. not valid :)

machniak updated this revision to Diff 840.Jan 17 2017, 11:43 AM

Don't update presence on invalid status

Presence maybe is nice chunk of code, but still we need to implement:

  1. last status persistence. I'm not even sure how to implement that, what's the "last status" of the user if he uses multiple clients? But I think it would be reasonable to not force "online" status when user connects to the service. Or should we only store status in a session? So only connection with expired session will set the status to "online".
  2. the context handling and best availability status setting should be done server-side, for privacy and performance reasons.
  3. if some user is not connected no one will see him as an existing user and will not be able to invite him - we need a user list on the server that is not based on Presence.

Perhaps I'm missing something, but this simply shows the effective status of the user in the UserStatusWidget on the client, so if I have two connections in the same context with statuses as:

{ C(1) , available }
{ C(2), away }

both will show "busy" if C(1) changes to:

{ C(1), busy }

however, on the server side, the presence of C(2) is still away? this would explain why i am ocassionally able to de-sync two connections in different web browser tabs .. it also has the negative side-effect that if we have:

{ C(1) , available }
{ C(2), away }

which becomes:

{ C(1) , invisible }
{ C(2), away }

both client connections will show invisible as the status UNTIL C(1) is closed and suddenly C(2) will show as away again!

I expect there will be similar odd behavior triggered in the UserList ...

This was the reason I used a user:sync channel specifically for communicating these changes between user's sessions: it provides a way to serialize those changes to all connections, which can then respond by setting their own Presence status as well. This ensured the status was syncronized on the server-side, so no matter what status or connection changes were made, the status remains consistent.

web/channels/system_channel.ex
18

this fails if no context is provided. imho this should be an optional parameter. see attached patch:

web/static/js/widgets/userstatus.js
13 ↗(On Diff #840)

yes, this will eventually need to be done. i have added T2193 for this.

Two connections with the same user and context should "share" the status, i.e. the status should always be the same for them. If you change status in one window it should change in other windows for the same user+context.
I don't see how a separate channel can help here. Maybe using system:<context> as a channel name would help. I don't know, Presence will still see these two connections as separate. No? Feel free to fix this.

last status persistence.

Some thoughts:

  • it would be nice to be able to set a desired initial status on connection; this would be ignored in the case of a user/context connection being established when other connections are already live in that same user/context. this would allow clients to store this, or go online with a specific status immediately (avoiding showing briefly as online only to then go invisible)
  • Persisting status information on the server would allows tracking the status for a user/context when new connections are made as well as between sessions (so web apps wouldn't need to store this themselves, just set a context and let the server handle it). A module that allows setting this data, notifying relevant user connections of those changes, and querying of the current stored status would need to be written to wrap that. Using something like mnesia would make this pretty clean and make it clusterable out of the box. If we go that route, something like https://github.com/meh/amnesia (an Elixir wrapper around Erlang's mnesia distributed key/value store) may be a good way forward.
we need a user list on the server that is not based on Presence

I can imagine a few approaches to the user list:

  • just show every user in a Kolab domain (won't scale if you have 50k users :)
  • only show connected users, with the ability to also send msgs to users who are offline (or invisible) for later delivery ... so they wouldn't show in the buddy list, but could be searched for / sent to, even if they aren't shown in the user list; this is what some/most? of the social media platforms seem to do
  • a custom list of users where the user would need to add them (possibly triggering an invite request to the other user for their approval/denial), which is what Jabber does

Personally, I *hate* setting up buddy lists manually. However, it may be nice to be able to "pin" users specifically so that the behavior might be a mix of the last two: by default show all users who are online in your Kolab domain, but make it possible to search/autocomplete offline users and pin them to your list so they are shown even when offline.

Eventually if/when we get to federation, allowing you to add buddies from other Kolab instances (or even other chat systems, with Kolab Chat as a bridge service ala oneroom), we'll need to add a add-with-permission system so that the server knows who else to track. But ... that shouldn't really impact how the buddy list works; it could still just show online users by default, and allow you to pin contacts to be shown even if offline.

I don't think we should bother tackling the offline display of contacts until we also have offline (delayed, stored on server) message delivery, since those features will go hand-in-hand.

But in the case of having presence+pinning for a user list, the user would then have a dynamic user list based on online status, and a separate "static" (user managed) list that would need to be stored / managed via the server. Or ... it could be stored on the client exclusively with a syncronization mechanism .. but that seems more work than necessary.

Another question is that of grouping users and filtering OUT users you don't care about. That could be entirely client-side ... but then that sucks to set up for each client (web browser tab? ugh..), and so having a server-side solution would be nice. What would be extra nice would be to not have a hierarchical listing, but something more tag-like. Google+'s circles are quite nice in that regard imho: you can add a contact to 0..N groups and then filter on 0..N groups. This would be nice for use in projects (add everyone for a given project to a group), but could also be used to model a more traditional hierarchical structure as well. However, that feels more like an address book feature set rather than a chat feature, where that information is stored/retrieved in the address books of the user and the chat system simply follows along with it. So there is probably a bigger architectural question here for Kolab, and so I'd like to punt on this particular feature set for the time-being and let us continue to focus on the "one linear list of contacts" for now.

This revision was automatically updated to reflect the committed changes.