Bagikan melalui


Tutorial: Mengaktifkan dukungan gambar sebaris di aplikasi Obrolan Anda

SDK Obrolan dirancang untuk bekerja dengan Microsoft Teams dengan mulus. Secara khusus, Chat SDK menyediakan solusi untuk menerima gambar sebaris dan mengirim gambar sebaris kepada pengguna dari Microsoft Teams.

Dalam tutorial ini, Anda mempelajari cara mengaktifkan dukungan gambar sebaris dengan menggunakan Azure Communication Services Chat SDK untuk JavaScript.

Gambar sebaris adalah gambar yang disalin dan ditempelkan langsung ke dalam kotak kirim klien Teams. Untuk gambar yang diunggah melalui menu Unggah dari perangkat ini atau melalui seret dan letakkan, seperti gambar yang diseret langsung ke kotak kirim di Teams, Anda perlu merujuk ke tutorial ini sebagai bagian dari fitur berbagi file. (Lihat bagian "Menangani lampiran gambar.")

Untuk menyalin gambar, pengguna Teams memiliki dua opsi:

  • Gunakan menu konteks sistem operasi mereka untuk menyalin file gambar lalu tempelkan ke dalam kotak kirim klien Teams mereka.
  • Gunakan pintasan keyboard.

Dalam tutorial ini, Anda mempelajari apa yang perlu Anda lakukan ketika Anda:

Nota

Kemampuan untuk mengirim gambar sebaris saat ini tersedia dalam pratinjau publik. Ini hanya tersedia untuk JavaScript. Untuk menerima gambar sebaris, saat ini sudah tersedia secara umum. Ini tersedia untuk JavaScript dan C# dalam obrolan interoperabilitas Teams.

Prasyarat

Contoh kode

Temukan kode akhir tutorial ini di GitHub.

Menangani gambar sebaris yang diterima dalam peristiwa pesan baru

Di bagian ini, Anda mempelajari cara merender gambar sebaris yang disematkan dalam konten pesan dari peristiwa pesan baru yang diterima.

Dalam mulai cepat, Anda membuat penanganan aktivitas untuk peristiwa tersebut chatMessageReceived , yang dipicu saat Anda menerima pesan baru dari pengguna Teams. Anda juga menambahkan konten pesan masuk ke messageContainer langsung saat menerima event chatMessageReceived dari chatClient, seperti ini:

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;
}

Dari peristiwa masuk jenis ChatMessageReceivedEvent, properti bernama attachments berisi informasi tentang gambar sebaris. Ini semua yang Anda butuhkan untuk merender gambar sebaris di UI Anda:

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";

Sekarang kembali ke kode sebelumnya untuk menambahkan beberapa logika tambahan, seperti cuplikan kode berikut:

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;
  });
}

Dalam contoh ini, Anda membuat dua fungsi pembantu, fetchPreviewImages dan setImgHandler. Yang pertama mengambil gambar pratinjau langsung dari previewURL yang disediakan di setiap objek ChatAttachment dengan header otentikasi. Demikian pula, Anda menyiapkan onclick peristiwa untuk setiap gambar dalam fungsi setImgHandler. Di penanganan aktivitas, Anda mengambil gambar skala penuh dari properti url dari ChatAttachment objek dengan header autentikasi.

Sekarang Anda perlu mengekspos token ke tingkat global karena Anda perlu membuat header autentikasi dengannya. Anda perlu mengubah kode berikut:

// 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;
   
   ...
}

Untuk menampilkan gambar skala penuh dalam overlay, Anda juga perlu menambahkan komponen baru:


<div class="overlay" id="overlay-container">
   <div class="content">
      <img id="full-scale-image" src="" alt="" />
   </div>
</div>

Menggunakan sedikit 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
}

Sekarang setelah Anda memiliki overlay yang disiapkan, saatnya untuk mengerjakan logika untuk merender gambar skala penuh. Ingat bahwa Anda membuat onClick penanganan aktivitas untuk memanggil fungsi 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';
}

Satu hal terakhir yang ingin Anda tambahkan adalah kemampuan untuk menutup overlay saat gambar diklik:

loadingImageOverlay.addEventListener('click', () => {
  overlayContainer.style.display = 'none';
});

Sekarang Anda telah membuat semua perubahan yang Anda butuhkan untuk merender gambar sebaris untuk pesan yang berasal dari pemberitahuan real-time.

Menjalankan kode

Pengguna webpack dapat menggunakan webpack-dev-server untuk membuat dan menjalankan aplikasi Anda. Jalankan perintah berikut untuk menggabungkan host aplikasi Anda di server web lokal:

npx webpack-dev-server --entry ./client.js --output bundle.js --debug --devtool inline-source-map

Demo

Buka browser Anda dan buka http://localhost:8080/. Masukkan URL rapat dan ID utas. Kirim beberapa gambar sebaris dari klien Teams.

Cuplikan layar yang memperlihatkan klien Teams dengan pesan terkirim yang berbunyi: Berikut adalah beberapa ide, beri tahu saya apa yang Anda pikirkan! Pesan ini juga berisi dua gambar sebaris mockup interior ruangan.

Kemudian Anda akan melihat pesan baru yang dirender bersama dengan gambar pratinjau.

Cuplikan layar yang memperlihatkan aplikasi sampel dengan pesan masuk dengan gambar sebaris.

Setelah pengguna Azure Communication Services memilih gambar pratinjau, overlay muncul dengan gambar skala penuh yang dikirim oleh pengguna Teams.

Cuplikan layar yang memperlihatkan aplikasi sampel dengan overlay gambar berskala penuh.

Menangani pengiriman gambar sebaris dalam permintaan pesan baru

Penting

Fitur Azure Communication Services ini saat ini dalam pratinjau. Fitur dalam pratinjau tersedia untuk umum dan dapat digunakan oleh semua pelanggan Microsoft baru dan yang sudah ada.

API pratinjau dan SDK disediakan tanpa perjanjian tingkat layanan. Kami menyarankan agar Anda tidak menggunakannya untuk beban kerja produksi. Fitur tertentu mungkin tidak didukung atau kemampuan mungkin dibatasi.

Untuk informasi lebih lanjut, lihat Supplemental Terms of Use for Microsoft Azure Previews.

Selain menangani pesan dengan gambar sebaris, Chat SDK untuk JavaScript juga menyediakan solusi untuk memungkinkan pengguna komunikasi mengirim gambar sebaris ke pengguna Microsoft Teams dalam obrolan interoperabilitas.

Lihat API baru dari ChatThreadClient:

var imageAttachment = await chatThreadClient.uploadImage(blob, file.name, {
  "onUploadProgress": reportProgressCallback
});

API ini menerima blob gambar, string nama file, dan fungsi callback yang melaporkan kemajuan pengunggahan.

Untuk mengirim gambar ke peserta obrolan lain, Anda perlu:

  1. Unggah gambar melalui uploadImage API dari ChatThreadClient, dan simpan objek yang dikembalikan.
  2. Buat konten pesan dan atur lampiran ke objek yang dikembalikan yang Anda simpan di langkah sebelumnya.
  3. Kirim pesan baru melalui sendMessage API dari ChatThreadClient.

Buat pemilih file baru yang menerima gambar:

<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>

Sekarang siapkan pendengar peristiwa saat ada perubahan status:

document.getElementById("upload").addEventListener("change", uploadImages);

Anda perlu membuat fungsi baru saat status berubah:

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);
}

Dalam contoh ini, Anda membuat FileReader untuk membaca setiap gambar sebagai base64gambar yang dikodekan, lalu membuat Blob sebelum memanggil ChatSDK API untuk mengunggahnya. Anda membuat variabel global uploadedImageModels untuk menyimpan model data dari gambar yang diunggah melalui Chat SDK.

Terakhir, Anda perlu memodifikasi pendengar acara yang sendMessageButton Anda buat sebelumnya untuk melampirkan gambar yang Anda unggah.

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}`);
});

Itu saja. Sekarang jalankan kode untuk melihatnya beraksi.

Menjalankan kode

Pengguna webpack dapat menggunakan webpack-dev-server untuk membuat dan menjalankan aplikasi Anda. Jalankan perintah berikut untuk menggabungkan host aplikasi Anda di server web lokal:

npx webpack-dev-server --entry ./client.js --output bundle.js --debug --devtool inline-source-map

Demo

Buka browser Anda dan buka http://localhost:8080/. Anda memiliki bagian baru dalam kotak kirim untuk melampirkan gambar.

Cuplikan layar yang memperlihatkan aplikasi sampel dengan bagian yang baru ditambahkan untuk melampirkan gambar.

Selanjutnya, Anda dapat memilih gambar yang ingin Anda lampirkan.

Cuplikan layar yang memperlihatkan pemilih file dengan daftar gambar yang dapat dilampirkan pengguna ke pesan mereka.

Cuplikan layar yang memperlihatkan aplikasi sampel dengan dua gambar terlampir.

Pengguna Teams sekarang akan menerima gambar yang baru saja Anda kirim ketika mereka memilih Kirim.

Cuplikan layar yang memperlihatkan aplikasi sampel dengan pesan terkirim dengan dua gambar yang disematkan.

Cuplikan layar yang memperlihatkan klien Teams dengan pesan yang diterima dengan dua gambar yang disematkan.

Tutorial ini menunjukkan kepada Anda cara mengaktifkan dukungan gambar sebaris dengan menggunakan Azure Communication Services Chat SDK untuk C#.

Dalam tutorial ini, Anda akan belajar cara:

  • Menangani gambar dalam baris untuk pesan baru.

Prasyarat

Maksud

  • Ambil properti previewUri untuk lampiran gambar yang disusun sebaris.

Menangani gambar dalam baris untuk pesan baru

Dalam mulai cepat, Anda melakukan polling untuk pesan dan menambahkan pesan baru ke messageList properti . Anda membangun fungsionalitas ini nanti untuk menyertakan penguraian dan pengambilan gambar sebaris.

  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}");
      }
  }

Dari peristiwa masuk jenis ChatMessageReceivedEvent, properti bernama attachments berisi informasi tentang gambar sebaris. Hanya itu yang Anda butuhkan untuk merender gambar sebaris di UI Anda.

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 }
}

JSON berikut adalah contoh seperti apa yang ChatAttachment mungkin terlihat untuk lampiran gambar:

"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"
      }
]

Sekarang kembali dan ganti kode untuk menambahkan logika tambahan untuk mengurai dan mengambil lampiran gambar:

  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}");
      }

Dalam contoh ini, Anda mengambil semua lampiran dari pesan jenis Image lalu mengambil masing-masing gambar. Anda harus menggunakan Anda Token di Bearer bagian header permintaan untuk tujuan otorisasi. Setelah gambar diunduh, Anda dapat menetapkannya ke InlineImage elemen tampilan.

Anda juga menyertakan daftar URI lampiran yang akan ditampilkan bersama dengan pesan dalam daftar pesan teks.

Demo

  • Jalankan aplikasi dari lingkungan pengembangan terintegrasi (IDE).
  • Masukkan tautan rapat Teams.
  • Bergabung dalam rapat.
  • Akui pengguna di sisi Teams.
  • Kirim pesan dari aplikasi Teams dengan gambar.

URL yang disertakan dengan pesan muncul di daftar pesan. Gambar terakhir yang diterima dirender di bagian bawah jendela.

Langkah selanjutnya