Skip to content

Commit cc63f2f

Browse files
Add methods for using a DownloadHandler and deprecate StreamResource (#95)
* Add methods for using a DownloadHandler and deprecate StreamResource * Use correct version number * Update to TestBench 9.4.1 * Improve tests * Correct example code
1 parent d6800f8 commit cc63f2f

File tree

4 files changed

+310
-6
lines changed

4 files changed

+310
-6
lines changed

collaboration-engine/src/main/java/com/vaadin/collaborationengine/CollaborationAvatarGroup.java

Lines changed: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import com.vaadin.flow.internal.UsageStatistics;
3636
import com.vaadin.flow.server.AbstractStreamResource;
3737
import com.vaadin.flow.server.StreamResource;
38+
import com.vaadin.flow.server.streams.DownloadHandler;
3839

3940
/**
4041
* Extension of the {@link AvatarGroup} component which integrates with the
@@ -55,8 +56,10 @@ public class CollaborationAvatarGroup extends Composite<AvatarGroup>
5556
* @see StreamResource
5657
* @see CollaborationAvatarGroup#setImageProvider(ImageProvider)
5758
* @since 1.0
59+
* @deprecated Use {@link #setImageHandler(ImageHandler)} instead.
5860
*/
5961
@FunctionalInterface
62+
@Deprecated(since = "6.5", forRemoval = true)
6063
public interface ImageProvider {
6164
/**
6265
* Gets a stream resource that provides the avatar image for the given
@@ -72,6 +75,30 @@ public interface ImageProvider {
7275
AbstractStreamResource getImageResource(UserInfo user);
7376
}
7477

78+
/**
79+
* Callback for creating a download handler for the avatar image for a
80+
* specific user. This allows loading the user image from a dynamic location
81+
* such as a database.
82+
*
83+
* @see DownloadHandler
84+
* @see CollaborationAvatarGroup#setImageHandler(ImageHandler)
85+
* @since 6.5
86+
*/
87+
@FunctionalInterface
88+
public interface ImageHandler {
89+
/**
90+
* Gets a download handler for the avatar image for the given user.
91+
*
92+
* @param user
93+
* the user for which to get a download handler with the
94+
* image, not <code>null</code>
95+
* @return the download handler to use for the image, or
96+
* <code>null</code> to not show use any avatar image for the
97+
* given user
98+
*/
99+
DownloadHandler getDownloadHandler(UserInfo user);
100+
}
101+
75102
private final SerializableSupplier<CollaborationEngine> ceSupplier;
76103

77104
private final UserInfo localUser;
@@ -84,6 +111,8 @@ public interface ImageProvider {
84111

85112
private ImageProvider imageProvider;
86113

114+
private ImageHandler imageHandler;
115+
87116
private boolean ownAvatarVisible;
88117

89118
static {
@@ -261,10 +290,12 @@ private AvatarGroupItem userToAvatarGroupItem(UserInfo user) {
261290
item.setName(user.getName());
262291
item.setAbbreviation(user.getAbbreviation());
263292

264-
if (imageProvider == null) {
265-
item.setImage(user.getImage());
266-
} else {
293+
if (imageHandler != null) {
294+
item.setImageHandler(imageHandler.getDownloadHandler(user));
295+
} else if (imageProvider != null) {
267296
item.setImageResource(imageProvider.getImageResource(user));
297+
} else {
298+
item.setImage(user.getImage());
268299
}
269300

270301
item.setColorIndex(getCollaborationEngine().getUserColorIndex(user));
@@ -310,7 +341,9 @@ private boolean isNotLocalUser(UserInfo user) {
310341
* the image provider to use, or <code>null</code> to use image
311342
* URLs directly from the user info object
312343
* @since 1.0
344+
* @deprecated Use {@link #setImageHandler(ImageHandler)} instead.
313345
*/
346+
@Deprecated(since = "6.5", forRemoval = true)
314347
public void setImageProvider(ImageProvider imageProvider) {
315348
this.imageProvider = imageProvider;
316349
refreshItems();
@@ -324,11 +357,63 @@ public void setImageProvider(ImageProvider imageProvider) {
324357
* @return the current image provider callback, or <code>null</code> if no
325358
* callback is set
326359
* @since 1.0
360+
* @deprecated Use {@link #setImageHandler(ImageHandler)} instead.
327361
*/
362+
@Deprecated(since = "6.5", forRemoval = true)
328363
public ImageProvider getImageProvider() {
329364
return imageProvider;
330365
}
331366

367+
/**
368+
* Sets an image handler callback for dynamically loading avatar images for
369+
* a given user. The image can be loaded on-demand from a database or using
370+
* any other source of IO streams.
371+
* <p>
372+
* If no image handler callback is defined, then the image URL defined by
373+
* {@link UserInfo#getImage()} is directly passed to the browser. This means
374+
* that avatar images need to be available as static files or served
375+
* dynamically from a custom servlet. This is the default.
376+
*
377+
* Usage example:
378+
*
379+
* <pre>
380+
* collaborationAvatarGroup.setImageHandler(userInfo -> {
381+
* DownloadHandler downloadHandler = DownloadHandler
382+
* .fromInputStream(context -> {
383+
* User userEntity = userRepository
384+
* .findById(userInfo.getId());
385+
* byte[] profilePicture = userEntity.getProfilePicture();
386+
* return new DownloadResponse(
387+
* new ByteArrayInputStream(profilePicture),
388+
* "avatar_" + userInfo.getId(), "image/png", -1);
389+
* });
390+
* return downloadHandler;
391+
* });
392+
* </pre>
393+
*
394+
* @param imageHandler
395+
* the image handler to use, or <code>null</code> to use image
396+
* URLs directly from the user info object
397+
* @since 6.5
398+
*/
399+
public void setImageHandler(ImageHandler imageHandler) {
400+
this.imageHandler = imageHandler;
401+
refreshItems();
402+
}
403+
404+
/**
405+
* Gets the currently used image handler callback.
406+
*
407+
* @see #setImageHandler(ImageHandler)
408+
*
409+
* @return the current image handler callback, or <code>null</code> if no
410+
* callback is set
411+
* @since 6.5
412+
*/
413+
public ImageHandler getImageHandler() {
414+
return imageHandler;
415+
}
416+
332417
/**
333418
* Gets whether the user's own avatar is displayed in the avatar group or
334419
* not.

collaboration-engine/src/main/java/com/vaadin/collaborationengine/CollaborationMessageList.java

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.Objects;
2121
import java.util.stream.Collectors;
2222

23+
import com.vaadin.collaborationengine.CollaborationAvatarGroup.ImageHandler;
2324
import com.vaadin.collaborationengine.CollaborationAvatarGroup.ImageProvider;
2425
import com.vaadin.flow.component.Composite;
2526
import com.vaadin.flow.component.HasSize;
@@ -70,6 +71,8 @@ public interface MessageConfigurator {
7071

7172
private ImageProvider imageProvider;
7273

74+
private ImageHandler imageHandler;
75+
7376
private final UserInfo localUser;
7477

7578
static {
@@ -278,7 +281,9 @@ void appendMessage(String text) {
278281
* @param imageProvider
279282
* the image provider to use, or <code>null</code> to use image
280283
* URLs directly from the user info object
284+
* @deprecated Use {@link #setImageHandler(ImageHandler)} instead.
281285
*/
286+
@Deprecated(since = "6.5", forRemoval = true)
282287
public void setImageProvider(ImageProvider imageProvider) {
283288
this.imageProvider = imageProvider;
284289
refreshMessages();
@@ -291,11 +296,63 @@ public void setImageProvider(ImageProvider imageProvider) {
291296
*
292297
* @return the current image provider callback, or <code>null</code> if no
293298
* callback is set
299+
* @deprecated Use {@link #setImageHandler(ImageHandler)} instead.
294300
*/
301+
@Deprecated(since = "6.5", forRemoval = true)
295302
public ImageProvider getImageProvider() {
296303
return imageProvider;
297304
}
298305

306+
/**
307+
* Sets an image handler callback for dynamically loading avatar images for
308+
* a given user. The image can be loaded on-demand from a database or using
309+
* any other source of IO streams.
310+
* <p>
311+
* If no image handler is defined, then the image URL defined by
312+
* {@link UserInfo#getImage()} is directly passed to the browser. This means
313+
* that avatar images need to be available as static files or served
314+
* dynamically from a custom servlet. This is the default.
315+
*
316+
* Usage example:
317+
*
318+
* <pre>
319+
* collaborationMessageList.setImageHandler(userInfo -> {
320+
* DownloadHandler downloadHandler = DownloadHandler
321+
* .fromInputStream(context -> {
322+
* User userEntity = userRepository
323+
* .findById(userInfo.getId());
324+
* byte[] profilePicture = userEntity.getProfilePicture();
325+
* return new DownloadResponse(
326+
* new ByteArrayInputStream(profilePicture),
327+
* "avatar_" + userInfo.getId(), "image/png", -1);
328+
* });
329+
* return downloadHandler;
330+
* });
331+
* </pre>
332+
*
333+
* @param imageHandler
334+
* the image handler to use, or <code>null</code> to use image
335+
* URLs directly from the user info object
336+
* @since 6.5
337+
*/
338+
public void setImageHandler(ImageHandler imageHandler) {
339+
this.imageHandler = imageHandler;
340+
refreshMessages();
341+
}
342+
343+
/**
344+
* Gets the currently used image handler callback.
345+
*
346+
* @see #setImageHandler(ImageHandler)
347+
*
348+
* @return the current image handler callback, or <code>null</code> if no
349+
* callback is set
350+
* @since 6.5
351+
*/
352+
public ImageHandler getImageHandler() {
353+
return imageHandler;
354+
}
355+
299356
/**
300357
* Sets a configurator callback for the messages. It can be used for
301358
* customizing the properties of the {@link MessageListItem} objects after
@@ -390,11 +447,14 @@ private MessageListItem convertToMessageListItem(
390447
MessageListItem messageListItem = new MessageListItem(message.getText(),
391448
message.getTime(), message.getUser().getName());
392449

393-
if (imageProvider == null) {
394-
messageListItem.setUserImage(message.getUser().getImage());
395-
} else {
450+
if (imageHandler != null) {
451+
messageListItem.setUserImageHandler(
452+
imageHandler.getDownloadHandler(message.getUser()));
453+
} else if (imageProvider != null) {
396454
messageListItem.setUserImageResource(
397455
imageProvider.getImageResource(message.getUser()));
456+
} else {
457+
messageListItem.setUserImage(message.getUser().getImage());
398458
}
399459
messageListItem
400460
.setUserAbbreviation(message.getUser().getAbbreviation());

collaboration-engine/src/test/java/com/vaadin/collaborationengine/CollaborationAvatarGroupTest.java

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import com.vaadin.flow.component.avatar.AvatarGroup.AvatarGroupItem;
4242
import com.vaadin.flow.function.SerializableSupplier;
4343
import com.vaadin.flow.server.VaadinService;
44+
import com.vaadin.flow.server.streams.DownloadHandler;
4445

4546
public class CollaborationAvatarGroupTest {
4647

@@ -428,6 +429,75 @@ public void imageProvider_clearProvider_imageIsSetFromUserInfo() {
428429
assertEquals("image2", item.getImage());
429430
}
430431

432+
@Test
433+
public void imageHandler_beforeAttach_downloadHandlerIsUsed() {
434+
UI.setCurrent(client1.ui);
435+
client1.group.setImageHandler(user -> DownloadHandler
436+
.forClassResource(getClass(), user.getImage(), user.getName()));
437+
client1.attach();
438+
client2.attach();
439+
440+
List<AvatarGroupItem> items = client1.getItems();
441+
assertEquals(2, items.size());
442+
AvatarGroupItem item = items.get(1);
443+
444+
Assert.assertThat(item.getImage(),
445+
CoreMatchers.startsWith("VAADIN/dynamic"));
446+
assertEquals("name2", item.getImageResource().getName());
447+
}
448+
449+
@Test
450+
public void imageHandler_afterAttach_downloadHandlerIsUsed() {
451+
UI.setCurrent(client1.ui);
452+
client1.attach();
453+
client2.attach();
454+
455+
client1.group.setImageHandler(user -> DownloadHandler
456+
.forClassResource(getClass(), user.getImage(), user.getName()));
457+
458+
List<AvatarGroupItem> items = client1.getItems();
459+
assertEquals(2, items.size());
460+
AvatarGroupItem item = items.get(1);
461+
462+
Assert.assertThat(item.getImage(),
463+
CoreMatchers.startsWith("VAADIN/dynamic"));
464+
assertEquals("name2", item.getImageResource().getName());
465+
}
466+
467+
@Test
468+
public void imageHandler_nullHandler_noImage() {
469+
UI.setCurrent(client1.ui);
470+
client1.attach();
471+
client2.attach();
472+
473+
client1.group.setImageHandler(user -> null);
474+
475+
List<AvatarGroupItem> items = client1.getItems();
476+
assertEquals(2, items.size());
477+
AvatarGroupItem item = items.get(1);
478+
479+
Assert.assertNull(item.getImage());
480+
Assert.assertNull(item.getImageResource());
481+
}
482+
483+
@Test
484+
public void imageHandler_clearHandler_imageIsSetFromUserInfo() {
485+
UI.setCurrent(client1.ui);
486+
client1.group.setImageHandler(user -> DownloadHandler
487+
.forClassResource(getClass(), user.getImage(), user.getName()));
488+
client1.attach();
489+
client2.attach();
490+
491+
client1.group.setImageHandler(null);
492+
493+
List<AvatarGroupItem> items = client1.getItems();
494+
assertEquals(2, items.size());
495+
AvatarGroupItem item = items.get(1);
496+
497+
Assert.assertNull(item.getImageResource());
498+
assertEquals("image2", item.getImage());
499+
}
500+
431501
@Test
432502
public void setOwnAvatarVisibleFalse_ownAvatarNotIncluded() {
433503
client1.group.setOwnAvatarVisible(false);

0 commit comments

Comments
 (0)