Tutorial: Habilitar o suporte de imagem embutida em seu aplicativo de Chat
O SDK de Chat foi projetado para funcionar perfeitamente com o Microsoft Teams. Especificamente, o SDK do Chat fornece uma solução para receber imagens em linha e enviar imagens em linha para os usuários do Microsoft Teams.
Neste tutorial, você aprenderá a habilitar o suporte a imagens embutidas usando o SDK dos Serviços de Comunicação do Azure de Chat para JavaScript.
Imagens embutidas são imagens copiadas e coladas diretamente na caixa de envio do cliente do Teams. Quanto às imagens carregadas pelo menu Carregar deste dispositivo ou ao arrastar e soltar, como imagens arrastadas diretamente para a caixa de envio no Teams, você precisa se referir a este tutorial como parte do recurso de compartilhamento de arquivos. (Consulte a seção "Manipular anexos de imagem".)
Os usuários do Teams têm duas opções para copiar uma imagem:
- Usar o menu de contexto do sistema operacional para copiar o arquivo de imagem e colar na caixa de envio do cliente do Teams.
- Usar atalhos do teclado.
Neste tutorial, você aprenderá o que precisa fazer ao:
Observação
A capacidade de enviar imagens em linha está disponível atualmente em visualização pública. Isso só está disponível para JavaScript. O recebimento de imagens em linha, está em disponibilidade geral no momento. Isso está disponível para JavaScript e C# em um chat de interoperabilidade do Teams.
Pré-requisitos
- Revisar o início rápido Ingressar seu aplicativo de chat em uma reunião do Teams.
- Criar um recurso dos Serviços de Comunicação do Azure. Para obter mais informações, confira Criar um recurso dos Serviços de Comunicação do Azure. Você precisará registrar a cadeia de conexão para este tutorial.
- Configure uma reunião do Teams usando sua conta comercial e tenha a URL da reunião pronta.
- Use o SDK de Chat para JavaScript (@azure/communication-chat) versão 1.4.0 ou mais recente. Para obter mais informações, consulte a Biblioteca de clientes do chat de comunicação do Azure para JavaScript.
Código de exemplo
Encontre o código finalizado desse tutorial no GitHub.
Manipular imagens em linha recebidas em um evento de mensagem nova
Nesta seção, você aprenderá como renderizar imagens em linha inseridas no conteúdo da mensagem de um evento recebido de mensagem nova.
No início rápido, você criou um manipulador de eventos para o evento chatMessageReceived
, que é disparado quando você recebe uma mensagem nova do usuário do Teams. Você também acrescentou o conteúdo da mensagem de entrada diretamente a messageContainer
ao receber o evento chatMessageReceived
do chatClient
, desta maneira:
chatClient.on("chatMessageReceived", (e) => {
console.log("Notification chatMessageReceived!");
// Check whether the notification is intended for the current thread
if (threadIdInput.value != e.threadId) {
return;
}
if (e.sender.communicationUserId != userId) {
renderReceivedMessage(e.message);
}
else {
renderSentMessage(e.message);
}
});
async function renderReceivedMessage(message) {
messages += '<div class="container lighter">' + message + '</div>';
messagesContainer.innerHTML = messages;
}
No evento de entrada do tipo ChatMessageReceivedEvent
, uma propriedade nomeada attachments
contém informações sobre a imagem em linha. Isto é tudo o que você precisa para renderizar imagens em linha na interface do usuário:
export interface ChatMessageReceivedEvent extends BaseChatMessageEvent {
/**
* Content of the message.
*/
message: string;
/**
* Metadata of the message.
*/
metadata: Record<string, string>;
/**
* Chat message attachment.
*/
attachments?: ChatAttachment[];
}
export interface ChatAttachment {
/** Id of the attachment */
id: string;
/** The type of attachment. */
attachmentType: ChatAttachmentType;
/** The name of the attachment content. */
name?: string;
/** The URL where the attachment can be downloaded */
url?: string;
/** The URL where the preview of attachment can be downloaded */
previewUrl?: string;
}
export type ChatAttachmentType = "image" | "unknown";
Agora, volte ao código anterior para adicionar uma lógica extra, como os seguintes snippets de código:
chatClient.on("chatMessageReceived", (e) => {
console.log("Notification chatMessageReceived!");
// Check whether the notification is intended for the current thread
if (threadIdInput.value != e.threadId) {
return;
}
const isMyMessage = e.sender.communicationUserId === userId;
renderReceivedMessage(e, isMyMessage);
});
function renderReceivedMessage(e, isMyMessage) {
const messageContent = e.message;
const card = document.createElement('div');
card.className = isMyMessage ? "container darker" : "container lighter";
card.innerHTML = messageContent;
messagesContainer.appendChild(card);
// Filter out inline images from attachments
const imageAttachments = e.attachments.filter((e) =>
e.attachmentType.toLowerCase() === 'image');
// Fetch and render preview images
fetchPreviewImages(imageAttachments);
// Set up onclick event handler to fetch full-scale image
setImgHandler(card, imageAttachments);
}
function setImgHandler(element, imageAttachments) {
// Do nothing if there are no image attachments
if (!imageAttachments.length > 0) {
return;
}
const imgs = element.getElementsByTagName('img');
for (const img of imgs) {
img.addEventListener('click', (e) => {
// Fetch full-scale image upon click
fetchFullScaleImage(e, imageAttachments);
});
}
}
async function fetchPreviewImages(attachments) {
if (!attachments.length > 0) {
return;
}
// Since each message could contain more than one inline image
// we need to fetch them individually
const result = await Promise.all(
attachments.map(async (attachment) => {
// Fetch preview image from its 'previewURL'
const response = await fetch(attachment.previewUrl, {
method: 'GET',
headers: {
// The token here should be the same one from chat initialization
'Authorization': 'Bearer ' + tokenString,
},
});
// The response would be in an image blob, so we can render it directly
return {
id: attachment.id,
content: await response.blob(),
};
}),
);
result.forEach((imageResult) => {
const urlCreator = window.URL || window.webkitURL;
const url = urlCreator.createObjectURL(imageResult.content);
// Look up the image ID and replace its 'src' with object URL
document.getElementById(imageResult.id).src = url;
});
}
Neste exemplo, você criou duas funções auxiliares fetchPreviewImages
e setImgHandler
. A primeira busca a imagem preliminar diretamente da previewURL
fornecida em cada objeto ChatAttachment
com um cabeçalho de autenticação. Da mesma forma, você configura um evento onclick
para cada imagem na função setImgHandler
. No manipulador de eventos, você busca uma imagem em grande escala da propriedade url
a partir do objeto ChatAttachment
com um cabeçalho de autenticação.
Agora você precisa expor o token no nível global porque precisa construir um cabeçalho de autenticação com isso. É necessário modificar o seguinte código:
// New variable for token string
var tokenString = '';
async function init() {
....
let tokenResponse = await identityClient.getToken(identityResponse, [
"voip",
"chat"
]);
const { token, expiresOn } = tokenResponse;
// Save to token string
tokenString = token;
...
}
Para mostrar a imagem em grande escala em uma sobreposição, você também precisa adicionar um novo componente:
<div class="overlay" id="overlay-container">
<div class="content">
<img id="full-scale-image" src="" alt="" />
</div>
</div>
Com alguns CSS:
/* let's make chat popup scrollable */
.chat-popup {
...
max-height: 650px;
overflow-y: scroll;
}
.overlay {
position: fixed;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, .7);
top: 0;
left: 0;
z-index: 100;
}
.overlay .content {
position: fixed;
width: 100%;
height: 100%;
text-align: center;
overflow: hidden;
z-index: 100;
margin: auto;
background-color: rgba(0, 0, 0, .7);
}
.overlay img {
position: absolute;
display: block;
max-height: 90%;
max-width: 90%;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
#overlay-container {
display: none
}
Agora que você configurou uma sobreposição, é hora de trabalhar na lógica para renderizar imagens em grande escala. Lembre-se de que criou um manipulador de eventos onClick
para chamar uma função fetchFullScaleImage
:
const overlayContainer = document.getElementById('overlay-container');
const loadingImageOverlay = document.getElementById('full-scale-image');
function fetchFullScaleImage(e, imageAttachments) {
// Get the image ID from the clicked image element
const link = imageAttachments.filter((attachment) =>
attachment.id === e.target.id)[0].url;
loadingImageOverlay.src = '';
// Fetch the image
fetch(link, {
method: 'GET',
headers: {'Authorization': 'Bearer ' + tokenString},
}).then(async (result) => {
// Now we set image blob to our overlay element
const content = await result.blob();
const urlCreator = window.URL || window.webkitURL;
const url = urlCreator.createObjectURL(content);
loadingImageOverlay.src = url;
});
// Show overlay
overlayContainer.style.display = 'block';
}
É aconselhável adicionar mais uma coisa, a capacidade de ignorar a sobreposição ao clicar na imagem:
loadingImageOverlay.addEventListener('click', () => {
overlayContainer.style.display = 'none';
});
Você já fez todas as alterações necessárias para renderizar imagens em linha de mensagens provenientes de notificações em tempo real.
Executar o código
Os usuários do Webpack podem usar webpack-dev-server
para compilar e executar seu aplicativo. Execute o comando a seguir para empacotar o host de aplicativos em um servidor Web local:
npx webpack-dev-server --entry ./client.js --output bundle.js --debug --devtool inline-source-map
Demonstração
Abra o navegador e vá parahttp://localhost:8080/
. Insira a URL da reunião e a ID do thread. Enviar algumas imagens em linha a partir do cliente do Teams.
Em seguida, você verá a nova mensagem renderizada junto com imagens de pré-visualização.
Depois que o usuário dos Serviços de Comunicação do Azure selecionar a imagem de pré-visualização, uma sobreposição aparecerá com a imagem em grande escala enviada pelo usuário do Teams.
Manipular o envio de imagens em linha em uma nova solicitação de mensagem
Importante
Este recurso dos Serviços de Comunicação do Azure estão atualmente em versão prévia.
Versões prévias das APIs e dos SDKs são fornecidas sem um contrato de nível de serviço. É recomendável que você não as use para cargas de trabalho de produção. Alguns recursos podem não ter suporte ou talvez ter restrição de recursos.
Para saber mais, consulte os Termos de Uso Complementares das Versões Prévias do Microsoft Azure.
Além de manipular mensagens com imagens em linha, o SDK de Chat para JavaScript também fornece uma solução para permitir que o usuário de comunicação envie imagens em linha para o usuário do Microsoft Teams em um chat de interoperabilidade.
Dê uma olhada na nova API de ChatThreadClient
:
var imageAttachment = await chatThreadClient.uploadImage(blob, file.name, {
"onUploadProgress": reportProgressCallback
});
A API usa um blob de imagem, uma cadeia de caracteres de nome de arquivo e um retorno de chamada de função que relata o progresso do carregamento.
Para enviar uma imagem para outro participante do chat, é necessário:
- Carregar a imagem por meio da API
uploadImage
doChatThreadClient
e salvar o objeto retornado. - Redigir o conteúdo da mensagem e definir um anexo para o objeto retornado salvo na etapa anterior.
- Enviar a nova mensagem pela API
sendMessage
doChatThreadClient
.
Criar um novo seletor de arquivos que aceite imagens:
<label for="myfile">Attach images:</label>
<input id="upload" type="file" id="myfile" name="myfile" accept="image/*" multiple>
<input style="display: none;" id="upload-result"></input>
Agora, configure um ouvinte de eventos para quando houver uma alteração de estado:
document.getElementById("upload").addEventListener("change", uploadImages);
É necessário criar uma nova função para quando o estado for alterado:
var uploadedImageModels = [];
async function uploadImages(e) {
const files = e.target.files;
if (files.length === 0) {
return;
}
for (let key in files) {
if (files.hasOwnProperty(key)) {
await uploadImage(files[key]);
}
}
}
async function uploadImage(file) {
const buffer = await file.arrayBuffer();
const blob = new Blob([new Uint8Array(buffer)], {type: file.type });
const url = window.URL.createObjectURL(blob);
document.getElementById("upload-result").innerHTML += `<img src="${url}" height="auto" width="100" />`;
let uploadedImageModel = await chatThreadClient.uploadImage(blob, file.name, {
imageBytesLength: file.size
});
uploadedImageModels.push(uploadedImageModel);
}
Neste exemplo, você criou um FileReader
para ler as imagens como imagens codificadas em base64
e criou um Blob
antes de chamar a API do ChatSDK para carregá-las. Você criou um uploadedImageModels
global para salvar os modelos de dados de imagens carregadas do ChatSDK.
Por fim, é necessário modificar o ouvinte de eventos sendMessageButton
criado anteriormente para anexar as imagens carregadas.
sendMessageButton.addEventListener("click", async () => {
let message = messagebox.value;
let attachments = uploadedImageModels;
// Inject image tags for images we have selected
// so they can be treated as inline images
// Alternatively, we can use some third-party libraries
// to have a rich text editor with inline image support
message += attachments.map((attachment) => `<img id="${attachment.id}" />`).join("");
let sendMessageRequest = {
content: message,
attachments: attachments,
};
let sendMessageOptions = {
senderDisplayName: "Jack",
type: "html"
};
let sendChatMessageResult = await chatThreadClient.sendMessage(
sendMessageRequest,
sendMessageOptions
);
let messageId = sendChatMessageResult.id;
uploadedImageModels = [];
messagebox.value = "";
document.getElementById("upload").value = "";
console.log(`Message sent!, message id:${messageId}`);
});
É isso. Agora, execute o código para vê-lo em ação.
Executar o código
Os usuários do Webpack podem usar webpack-dev-server
para compilar e executar seu aplicativo. Execute o comando a seguir para empacotar o host de aplicativos em um servidor Web local:
npx webpack-dev-server --entry ./client.js --output bundle.js --debug --devtool inline-source-map
Demonstração
Abra o navegador e vá parahttp://localhost:8080/
. Você tem uma nova seção na caixa de envio para anexar imagens.
Em seguida, é possível selecionar as imagens que deseja anexar.
Agora, o usuário do Teams deve receber a imagem que você acabou de enviar assim que selecionar Enviar.
Este tutoria mostra como habilitar o suporte a imagens em linha usando o SDK dos Serviços de Comunicação do Azure de Chat para C#.
Neste tutorial, você aprenderá a:
- Manipular imagens em linha para novas mensagens.
Pré-requisitos
- Revisar o início rápido Ingressar seu aplicativo de chat em uma reunião do Teams.
- Criar um recurso dos Serviços de Comunicação do Azure. Para obter mais informações, confira Criar um recurso dos Serviços de Comunicação do Azure. Você precisará registrar a cadeia de conexão para este tutorial.
- Configure uma reunião do Teams usando sua conta comercial e tenha a URL da reunião pronta.
- Use o SDK de Chat para C# (Azure.Communication.Chat) versão 1.3.0 ou mais recente. Para obter mais informações, consulte a Biblioteca de clientes do Chat de Comunicação do Azure para .NET.
Goal
- Levar a propriedade
previewUri
para anexos de imagem em linha.
Manipular imagens embutidas para novas mensagens
No início rápido, é possível verificar mensagens e adicionar novas mensagens à propriedade messageList
. Depois, use essa funcionalidade para incluir a análise e busca de imagens em linha.
CommunicationUserIdentifier currentUser = new(user_Id_);
AsyncPageable<ChatMessage> allMessages = chatThreadClient.GetMessagesAsync();
SortedDictionary<long, string> messageList = [];
int textMessages = 0;
await foreach (ChatMessage message in allMessages)
{
if (message.Type == ChatMessageType.Html || message.Type == ChatMessageType.Text)
{
textMessages++;
var userPrefix = message.Sender.Equals(currentUser) ? "[you]:" : "";
var strippedMessage = StripHtml(message.Content.Message);
messageList.Add(long.Parse(message.SequenceId), $"{userPrefix}{strippedMessage}");
}
}
No evento de entrada de tipo ChatMessageReceivedEvent
, a propriedade nomeada attachments
contém informações sobre a imagem em linha. Isso é tudo o que você precisa para renderizar imagens em linha na interface do usuário.
public class ChatAttachment
{
public ChatAttachment(string id, ChatAttachmentType attachmentType)
public ChatAttachmentType AttachmentType { get }
public string Id { get }
public string Name { get }
public System.Uri PreviewUrl { get }
public System.Uri Url { get }
}
public struct ChatAttachmentType : System.IEquatable<AttachmentType>
{
public ChatAttachmentType(string value)
public static File { get }
public static Image { get }
}
O JSON a seguir é um exemplo de como um ChatAttachment
pode parecer para um anexo de imagem:
"attachments": [
{
"id": "9d89acb2-c4e4-4cab-b94a-7c12a61afe30",
"attachmentType": "image",
"name": "Screenshot.png",
"url": "https://contoso.communication.azure.com/chat/threads/19:9d89acb29d89acb2@thread.v2/images/9d89acb2-c4e4-4cab-b94a-7c12a61afe30/views/original?api-version=2023-11-03",
"previewUrl": "https://contoso.communication.azure.com/chat/threads/19:9d89acb29d89acb2@thread.v2/images/9d89acb2-c4e4-4cab-b94a-7c12a61afe30/views/small?api-version=2023-11-03"
}
]
Agora, volte e substitua o código para adicionar lógica extra a fim de analisar e buscar os anexos de imagem:
CommunicationUserIdentifier currentUser = new(user_Id_);
AsyncPageable<ChatMessage> allMessages = chatThreadClient.GetMessagesAsync();
SortedDictionary<long, string> messageList = [];
int textMessages = 0;
await foreach (ChatMessage message in allMessages)
{
// Get message attachments that are of type 'image'
IEnumerable<ChatAttachment> imageAttachments = message.Content.Attachments.Where(x => x.AttachmentType == ChatAttachmentType.Image);
// Fetch image and render
var chatAttachmentImageUris = new List<Uri>();
foreach (ChatAttachment imageAttachment in imageAttachments)
{
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", communicationTokenCredential.GetToken().Token);
var response = await client.GetAsync(imageAttachment.PreviewUri);
var randomAccessStream = await response.Content.ReadAsStreamAsync();
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
var bitmapImage = new BitmapImage();
await bitmapImage.SetSourceAsync(randomAccessStream.AsRandomAccessStream());
InlineImage.Source = bitmapImage;
});
chatAttachmentImageUris.Add(imageAttachment.PreviewUri);
}
// Build message list
if (message.Type == ChatMessageType.Html || message.Type == ChatMessageType.Text)
{
textMessages++;
var userPrefix = message.Sender.Equals(currentUser) ? "[you]:" : "";
var strippedMessage = StripHtml(message.Content.Message);
var chatAttachments = chatAttachmentImageUris.Count > 0 ? "[Attachments]:\n" + string.Join(",\n", chatAttachmentImageUris) : "";
messageList.Add(long.Parse(message.SequenceId), $"{userPrefix}{strippedMessage}\n{chatAttachments}");
}
Neste exemplo, você pega todos os anexos da mensagem de tipo Image
e, em seguida, busca cada uma das imagens. Você deve usar o Token
na parte Bearer
do cabeçalho da solicitação para fins de autorização. Depois de baixar a imagem, você poderá atribuí-la ao elemento InlineImage
da exibição.
Você também inclui uma lista dos URIs de anexo para serem exibidos junto com a mensagem na lista de mensagens de texto.
Demonstração
- Execute o aplicativo a partir do IDE (ambiente de desenvolvimento integrado).
- Insira um link de reunião do Teams.
- Ingresse na reunião.
- Admitir o usuário no Teams.
- Envie uma mensagem no Teams com uma imagem.
A URL incluída na mensagem aparece na lista de mensagens. A última imagem recebida é renderizada na parte inferior da janela.
Próximas etapas
- Saiba mais sobre outros recursos de interoperabilidade com suporte.
- Confira nosso Exemplo de chat em destaque.
- Saiba mais sobre como o chat funciona.