Design an API and internal database structure for creating channels/rooms (make a decision about which word to use), invitations and room/channel events
- Ticket Type
- id - unique room identifier
- creator - user that created the room (and is an admin for it)
- is_public - is the room for everyone or requires an invitation
- alias - human readable room name (especially for public rooms) - should be unique
- room_id - room identifier
- user_id - invited user identifier
- updated - timestamp of when the invitation was created or last updated
- status - one of: invited, accepted, declined
- comment - optional comment added by invitor, to be displayed for invitee
- ttl - should invitations have a ttl?
Ok, let's focus on this for now. In our simples case of 1-to-1 chat, we'll have:
- UserA opens a chat window with UserB
- room entry is created
- invitation entry is created
- UserB receives the invitation and accepts (or declines) the invitation
- invitation entry is updated
- Both can now talk in the room
That looks pretty simple, but we'd need to consider some room data retention. E.g. what happens with room and invitation entries when users stop using the room, or if the invited user declined the invitation. Or when UserB starts a new chat with UserA - should we create a new room or re-use the one created by UserA?
This should be modeled so that we can reasonably expect to bridge XMPP to it, and perhaps even matrix.org federation.
For rooms, I think that is a good starting list. I would add:
- image / avatar (optional; by default rooms would use a stock icon, but this would allow it to be modified)
- topic (optional; empty string by default)
- encryption_status (cleartext, ...)
- media_types (set of flags: text, audio, video, screenshare, ..)
Invitations also look good; I would recommend including the room metadata and not just provide the room_id; this will prevent another server roundtrip for the client to fetch that data. Same for user_id: send the user information (which would naturally include the user_id) to prevent a round-trip for that. ttl -> yes, but optional; similarly it should probably have the possibility to be scheduled in the future. This could even take the form of an iTip invitation if we wanted to get extra special, so one could easily pop it into a calendar.
what happens with room and invitation entries when users stop using the room, or if the invited user declined the invitation.
IMO: Invites should remain valid until the invited person accepts/declines, the ttl is reached, or the invite is canceled by the user who did the inviting (or a room admin?). That gives us a simple model: make an invite, wait for response, or delete it later by the invitor. This way we don't have to worry about what to do on channel creation / deletion, user online/offline status, etc.
On declining the invite, the user who issued the invited should be optionally informed (it should also be possible to silently decline, or decline with a message); once that has been delivered to the person who did the invite (a message to their clientsync:username channel?) then the invite does not need to be held onto, yes?
Or when UserB starts a new chat with UserA - should we create a new room or re-use the one created by UserA?
As a user, I would expect the same chat to be picked up, so that implies the same room. So 1:1 chats should probably have a simple name layout such as private:usera:userb where the username are sorted in some stable fashion (simple byte-comparison sorting would be enough). The question is what happens when a third person is invited to such a chat. IMO that should actually open a new chat with all three people in it, with the recent-chat history inherited into the new chat. That way, usera and userb can always re-open a private chat, while userc is in the new chat room. This would require some client-side support for upgrading chat from 1:1 to many:many, but that should not be a big deal (one hopes! :)
@seigo, I started working on a 1:1 chat. Creating and using a chat is simple. Problem is with invitations/notifications. I suppose we'd need some kind of event system. Consider scenario:
- UserA first time wants to talk with UserB
- UserB is notified about "incoming chat". He joins the conversation.
- UserB disconnects, closes the chat window. Then connects again.
- UserA want's still to talk with UserB again.
How to get to know to UserB there's a "requested conversation"? He already accepted "an invitation" once for this room. If and when the room should be destroyed?
How to get "pending invitations" if user is disconnected when someone tries to talk with him? E.g.
- UserA first time wants to talk with UserB, UserB is disconnected.
- UserB connects, but now UserA is disconnected.
Should UserB be notified? Should this depend on the fact that UserA left some messages in the chat or not? Etc.
If I'm understanding you correctly, this is really about persistence: dealing with disconnections / reconnections etc. how do we manage notifications, invitations, permissions, etc.
Having looked at other systems (esp ones that are not XMPP :), perhaps we want to take an approach where are all notifications and permissions (invitations) are persisted on the server UNTIL the user does something with the notification (closes/rejects it, accepts, etc.), and on connect the client will get the list of existing notifications (or at least the first N). This can happen over a channel (the system channel? a user-specific channel?), and the data can be persisted in amnesia, but then these issues become much easier to deal with. This would also give us pattern to follow for keeping track of the chats a user is in between connections: when they join a channel, persist that information on the server, and when they connect again that persisted information can be sent to the client again.
But for the immediate, notifications and invitations are simpler: they are single pieces of information (a notification, a permission to join a given channel, ..) and they have an easy-to-define lifespan (when the user does something to the notification/permission, remove it from the persisted set of notifications/invitations on the server).
Being able to look these up by user is obviously the most important thing, so in the storage the keys should probably be of a shape similar to: <<userid>>::notifications and <<userid>>::invitations ... a list/map of active notifications or invitations can then be stored as the values. Ensuring that those do not become INSANELY LARGE due to e.g. notification spamming or a user not logging in for a while ... that something we can address later as long as the access to notifications, invitations, etc is wrapped in a function that hides the exact implementation. (e.g. I can imagine splitting notifications and invitations up into chunks, limiting the number of notifications/invitations stored, expiring old ones after a certain period, support for paged fetching of notifications/invitations to avoid overloading clients, etc. That can all come later though)
On the client side, sounds like we need a notifications widget :)
I have started a feature branch in feature/room_metadata
1:1 chats now trigger invites to the other side and currently the web app automatically opens the chat on invite. the chats are now held in separate channels with a UUID for the room name. this makes it usable for group chats as well as 1:1 chats. a UserChannel has been added to allow for delivery of notifications, though currently the only notification being used in invites
what still needs implementing:
- a notifications UI where these invites can appear
- persistence of invitations (though now that they are being sent, that is one big step in the right direction)
- ability to match existing chats to users (if user A closes the chat window and clicks on Open Chat again, they should return to the same chat)
- a way to invite other people to the chat
- adding of topic/title/etc metadata to RoomChannels