Collaboration Engine does currently have three different use cases that are supported by high-level APIs:
Sharing presence through CollaborationAvatarGroup
Editing a form together through CollaborationBinder
Viewing and posting messages through CollaborationMessageList and CollaborationMessageInput
Each use case is backed by low-level primitive mechanisms provided through TopicConnection and it is in theory possible to implement custom integrations by manually modifying topic contents or listening to changes according to the conventions used by the high-level components. At the same time, this requires using quite low-level APIs and also relying on implementation details on the exact structure used for storing different types of data in the topics.
Some examples of use cases where it would be useful to create custom integrations:
Implementing bots for our own demos.
Showing simple color dots in a list of items to show who are currently editing which item.
Submitting system messages to a message list.
To deal with this, I propose the concept of collaboration data access object classes for various use cases. Each instance would be managing its own TopicConnection which in practice means that it would need a CollaborationEngine reference, a ConnectionContext, a topic id, and a UserInfo object. There could also be a shorthand constructor that uses CollaborationEngine.getInstance() and creates a ComponentConnectionContext based on a provided Component instance.
The exact functionality of each class would be tailored to the use cases as follows
Presence
Key concepts:
Add and remove active UserInfo instances. This could be done through simple add and remove methods that return CompletableFuture instances to signal when the operation is completed.
React when a UserInfo instance is added or removed. Many use cases would need to keep track of e.g. a component created for an added user to remove the same component when the user is removed. This can be facilitated by allowing the add handler to return a Registration that is triggered for removals.
Using those mechanisms, a simple list of active users could be shown in a VerticalLayout using in this way:
VerticalLayout users = new VerticalLayout();
PresenceCollaborationDao dao = new PresenceCollaborationDao(users, topicId, ownUserInfo);
dao.setPresence(ownUserInfo, true);
dao.setNewUserHandler(newUserInfo -> {
Component card = createUserCard(newUserInfo);
users.add(card);
return () -> avatarGroup.remove(card);
});
Similarly to CollaborationAvatarGroup, any presence registered using setPresence through the DAO would be automatically removed when the connection context of the DAO is deactivated and added back if it's reactivated. Similarly to other CE concepts, there would be initial add events for all current items users connecting and also corresponding remove events when disconnecting.
Related tickets:
[ ] #37 Enable rendering AvatarGroup with a custom visual representation
[ ] #38 Grid avatar indicators to visualize which rows are currently being edited
Form editing
Key concepts:
Set the current value for a given property name
Toggle highlight for a UserInfo for a given property name
React when a value or highlight status is changed
Usage would follow the same pattern as for presence. Values and highlights can be set directly using void setValue(String property, JsonNode value) and void setHighlight(String property, UserInfo user, boolean highlight). Listening to value changes is through void setValueChangeHandler(SerializableBiConsumer<String, JsonValue> handler) while highlight handling is again structured to associate removal as a Registration returned from the add handler void setHighlightHandler(SerializableBiFunction<String, UserInfo, Registration> handler).
Edit: Field highlighting also needs to shift around a fieldIndex value for some cases. This means that the API might have to be slightly more complex.
Messages
Message handling is slightly more complex since the DAO would also have to deal with persistence. This is handled through additional constructors that receive a CollaborationMessagePersister instance in addition to the general parameters.
Key concepts:
Submit a message
React when a message is submitted
The unread count for the messages in a specific topic could then be implemented like this
public class UnreadCount extends Span {
private int count;
public UnreadCount(String topicId, UserInfo localUser, CollaborationMessagePersister persister) {
MessageCollaborationDao dao = new MessageCollaborationDao(this, topicId, localUser, persister);
dao.setActivationHandler(topic -> {
reset();
return null; // Registration triggered when deactivating
});
dao.setNewMessageHandler(message -> {
setCount(count + 1);
});
}
public void reset() {
setCount(0);
}
private void setCount(int count) {
this.count = count;
setText(Integer.toString(count));
}
}
This example uses a generic setActivationHandler method that is present in all the DAO classes. It is directly connected to the activation status of the underlying topic connection. All DAO classes would also have a setTopic method that behaves in the same way as in the high-level components.
Related tickets:
[ ] #35 Enable observing topics to see how many messages there is in a chat without joining it
Collaboration Engine does currently have three different use cases that are supported by high-level APIs:
CollaborationAvatarGroup
CollaborationBinder
CollaborationMessageList
andCollaborationMessageInput
Each use case is backed by low-level primitive mechanisms provided through
TopicConnection
and it is in theory possible to implement custom integrations by manually modifying topic contents or listening to changes according to the conventions used by the high-level components. At the same time, this requires using quite low-level APIs and also relying on implementation details on the exact structure used for storing different types of data in the topics.Some examples of use cases where it would be useful to create custom integrations:
To deal with this, I propose the concept of collaboration data access object classes for various use cases. Each instance would be managing its own
TopicConnection
which in practice means that it would need aCollaborationEngine
reference, aConnectionContext
, a topic id, and aUserInfo
object. There could also be a shorthand constructor that usesCollaborationEngine.getInstance()
and creates aComponentConnectionContext
based on a providedComponent
instance.The exact functionality of each class would be tailored to the use cases as follows
Presence
Key concepts:
UserInfo
instances. This could be done through simpleadd
andremove
methods that returnCompletableFuture
instances to signal when the operation is completed.UserInfo
instance is added or removed. Many use cases would need to keep track of e.g. a component created for an added user to remove the same component when the user is removed. This can be facilitated by allowing the add handler to return aRegistration
that is triggered for removals.Using those mechanisms, a simple list of active users could be shown in a
VerticalLayout
using in this way:Similarly to
CollaborationAvatarGroup
, any presence registered usingsetPresence
through the DAO would be automatically removed when the connection context of the DAO is deactivated and added back if it's reactivated. Similarly to other CE concepts, there would be initial add events for all current items users connecting and also corresponding remove events when disconnecting.Related tickets:
Form editing
Key concepts:
UserInfo
for a given property nameUsage would follow the same pattern as for presence. Values and highlights can be set directly using
void setValue(String property, JsonNode value)
andvoid setHighlight(String property, UserInfo user, boolean highlight)
. Listening to value changes is throughvoid setValueChangeHandler(SerializableBiConsumer<String, JsonValue> handler)
while highlight handling is again structured to associate removal as aRegistration
returned from the add handlervoid setHighlightHandler(SerializableBiFunction<String, UserInfo, Registration> handler)
.Edit: Field highlighting also needs to shift around a
fieldIndex
value for some cases. This means that the API might have to be slightly more complex.Messages
Message handling is slightly more complex since the DAO would also have to deal with persistence. This is handled through additional constructors that receive a
CollaborationMessagePersister
instance in addition to the general parameters.Key concepts:
The unread count for the messages in a specific topic could then be implemented like this
This example uses a generic
setActivationHandler
method that is present in all the DAO classes. It is directly connected to the activation status of the underlying topic connection. All DAO classes would also have asetTopic
method that behaves in the same way as in the high-level components.Related tickets: