MessageListAttachments.java
package com.vaadin.demo.component.messages;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Arrays;
import java.util.Base64;
import javax.imageio.ImageIO;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.messages.MessageList;
import com.vaadin.flow.component.messages.MessageListItem;
import com.vaadin.flow.router.Route;
@Route("message-list-attachments")
public class MessageListAttachments extends Div {
public MessageListAttachments() {
MessageList list = new MessageList();
Instant yesterday = LocalDateTime.now().minusDays(1)
.toInstant(ZoneOffset.UTC);
Instant fiftyMinsAgo = LocalDateTime.now().minusMinutes(50)
.toInstant(ZoneOffset.UTC);
// tag::snippet[]
MessageListItem message1 = new MessageListItem(
"Here are the documents for the project.", yesterday,
"Matt Mambo");
message1.setUserColorIndex(1);
message1.addAttachment(new MessageListItem.Attachment(
"project-proposal.pdf",
"https://example.com/files/proposal.pdf", "application/pdf"));
// end::snippet[]
message1.addAttachment(new MessageListItem.Attachment(
"budget-overview.xlsx", "https://example.com/files/budget.xlsx",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"));
MessageListItem message2 = new MessageListItem(
"Thanks! Here's a photo from the offsite.", fiftyMinsAgo,
"Linsey Listy");
message2.setUserColorIndex(2);
String imageDataUrl = toThumbnailDataUrl(
getClass().getResourceAsStream("/images/reindeer.jpg"));
message2.addAttachment(new MessageListItem.Attachment("landscape.jpg",
imageDataUrl, "image/jpeg"));
// tag::snippet[]
list.setItems(Arrays.asList(message1, message2));
var status = new Span("Click an attachment to see its name here.");
list.addAttachmentClickListener(event -> {
status.setText("Clicked: " + event.getAttachment().name());
});
// end::snippet[]
add(list, status);
}
private static final int THUMBNAIL_MAX_SIZE = 200;
private static String toThumbnailDataUrl(InputStream imageStream) {
try {
var originalImage = ImageIO.read(imageStream);
var originalWidth = originalImage.getWidth();
var originalHeight = originalImage.getHeight();
var scale = Math.min((double) THUMBNAIL_MAX_SIZE / originalWidth,
(double) THUMBNAIL_MAX_SIZE / originalHeight);
var scaledWidth = (int) (originalWidth * scale);
var scaledHeight = (int) (originalHeight * scale);
var scaledImage = new BufferedImage(scaledWidth, scaledHeight,
BufferedImage.TYPE_INT_RGB);
var g2d = scaledImage.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.drawImage(originalImage, 0, 0, scaledWidth, scaledHeight, null);
g2d.dispose();
var outputStream = new ByteArrayOutputStream();
ImageIO.write(scaledImage, "jpg", outputStream);
return "data:image/jpeg;base64," + Base64.getEncoder()
.encodeToString(outputStream.toByteArray());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}
message-list-attachments.tsx
import React from 'react';
import { format, subDays, subMinutes } from 'date-fns';
import { useSignal } from '@vaadin/hilla-react-signals';
import { MessageList } from '@vaadin/react-components/MessageList.js';
import landscapeImage from '../../../../../src/main/resources/images/reindeer.jpg?url';
function Example() {
// tag::snippet[]
const isoMinutes = 'yyyy-MM-dd HH:mm';
const yesterday = format(subDays(new Date(), 1), isoMinutes);
const fiftyMinutesAgo = format(subMinutes(new Date(), 50), isoMinutes);
const items = [
{
text: 'Here are the documents for the project.',
time: yesterday,
userName: 'Matt Mambo',
userColorIndex: 1,
attachments: [
{
name: 'project-proposal.pdf',
url: 'https://example.com/files/proposal.pdf',
type: 'application/pdf',
},
{
name: 'budget-overview.xlsx',
url: 'https://example.com/files/budget.xlsx',
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
},
],
},
{
text: "Thanks! Here's a photo from the offsite.",
time: fiftyMinutesAgo,
userName: 'Linsey Listy',
userColorIndex: 2,
attachments: [
{
name: 'landscape.jpg',
url: landscapeImage,
type: 'image/jpeg',
},
],
},
];
const statusText = useSignal('Click an attachment to see its name here.');
return (
<>
<MessageList
items={items}
ref={(messageList) => {
if (messageList) {
messageList.addEventListener('attachment-click', (e: CustomEvent) => {
statusText.value = 'Clicked: ' + e.detail.attachment.name;
});
}
}}
// Switch to using onAttachmentClick once https://github.com/vaadin/web-components/pull/11189 is available in a release.
// onAttachmentClick={(e) => {
// statusText.value = 'Clicked: ' + e.detail.attachment.name;
// }}
/>
<span>{statusText.value}</span>
</>
);
// end::snippet[]
}
message-list-attachments.ts
import '@vaadin/message-list';
import { format, subDays, subMinutes } from 'date-fns';
import { html, LitElement } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import { applyTheme } from 'Frontend/demo/theme';
import landscapeImage from '../../../../src/main/resources/images/reindeer.jpg?url';
@customElement('message-list-attachments')
export class Example extends LitElement {
private readonly yesterday = format(subDays(new Date(), 1), 'yyyy-MM-dd HH:mm');
private readonly fiftyMinutesAgo = format(subMinutes(new Date(), 50), 'yyyy-MM-dd HH:mm');
@state()
private statusText = 'Click an attachment to see its name here.';
protected override createRenderRoot() {
const root = super.createRenderRoot();
applyTheme(root);
return root;
}
protected override render() {
return html`
<!-- tag::snippet[] -->
<vaadin-message-list
@attachment-click="${(e: CustomEvent) => {
this.statusText = 'Clicked: ' + e.detail.attachment.name;
}}"
.items="${[
{
text: 'Here are the documents for the project.',
time: this.yesterday,
userName: 'Matt Mambo',
userColorIndex: 1,
attachments: [
{
name: 'project-proposal.pdf',
url: 'https://example.com/files/proposal.pdf',
type: 'application/pdf',
},
{
name: 'budget-overview.xlsx',
url: 'https://example.com/files/budget.xlsx',
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
},
],
},
{
text: 'Thanks! Here\u0027s a photo from the offsite.',
time: this.fiftyMinutesAgo,
userName: 'Linsey Listy',
userColorIndex: 2,
attachments: [
{
name: 'landscape.jpg',
url: landscapeImage,
type: 'image/jpeg',
},
],
},
]}"
></vaadin-message-list>
<span>${this.statusText}</span>
<!-- end::snippet[] -->
`;
}
}