Skip to content

Commit 392bcb3

Browse files
authored
Merge pull request #272995 from jpeng-ms/patch-12
[ACS][Chat] Updated File Sharing API
2 parents 2822c67 + 4c4f1c2 commit 392bcb3

File tree

2 files changed

+93
-53
lines changed

2 files changed

+93
-53
lines changed

articles/communication-services/tutorials/file-sharing-tutorial-acs-chat.md

Lines changed: 93 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ The diagram shows a typical flow of a file sharing scenario for both upload and
5858

5959
You can follow the tutorial [Upload file to Azure Blob Storage with an Azure Function](/azure/developer/javascript/how-to/with-web-app/azure-function-file-upload) to write the backend code required for file sharing.
6060

61-
Once implemented, you can call this Azure Function inside the `uploadHandler` function to upload files to Azure Blob Storage. For the remaining of the tutorial, we assume you have generated the function using the tutorial for Azure Blob Storage linked previously.
61+
Once implemented, you can call this Azure Function inside the `handleAttachmentSelection` function to upload files to Azure Blob Storage. For the remaining of the tutorial, we assume you have generated the function using the tutorial for Azure Blob Storage linked previously.
6262

6363
### Securing your Azure Blob storage container
6464

@@ -91,7 +91,7 @@ Use the `npm install` command to install the beta Azure Communication Services U
9191

9292
```bash
9393

94-
npm install @azure/communication-react@1.13.0-beta.1
94+
npm install @azure/communication-react@1.16.0-beta.1
9595

9696
```
9797

@@ -100,8 +100,8 @@ you can most consistently use the API from the core libraries in your applicatio
100100

101101
```bash
102102

103-
npm install @azure/communication-calling@1.21.1-beta.4
104-
npm install @azure/communication-chat@1.5.0-beta.1
103+
npm install @azure/communication-calling@1.24.1-beta.2
104+
npm install @azure/communication-chat@1.6.0-beta.1
105105

106106
```
107107

@@ -121,11 +121,13 @@ You need to replace the variable values for both common variable required to ini
121121

122122
`App.tsx`
123123

124-
```javascript
125-
import { FileUploadHandler, FileUploadManager } from '@azure/communication-react';
124+
```typescript
126125
import { initializeFileTypeIcons } from '@fluentui/react-file-type-icons';
127126
import {
128127
ChatComposite,
128+
AttachmentUploadTask,
129+
AttachmentUploadOptions,
130+
AttachmentSelectionHandler,
129131
fromFlatCommunicationIdentifier,
130132
useAzureCommunicationChatAdapter
131133
} from '@azure/communication-react';
@@ -172,12 +174,9 @@ function App(): JSX.Element {
172174
<ChatComposite
173175
adapter={chatAdapter}
174176
options={{
175-
fileSharing: {
176-
uploadHandler: fileUploadHandler,
177-
// If `fileDownloadHandler` is not provided. The file URL is opened in a new tab.
178-
downloadHandler: fileDownloadHandler,
179-
accept: 'image/png, image/jpeg, text/plain, .docx',
180-
multiple: true
177+
attachmentOptions: {
178+
uploadOptions: uploadOptions,
179+
downloadOptions: downloadOptions,
181180
}
182181
}} />
183182
</div>
@@ -190,36 +189,37 @@ function App(): JSX.Element {
190189
return <h3>Initializing...</h3>;
191190
}
192191

193-
const fileUploadHandler: FileUploadHandler = async (userId, fileUploads) => {
194-
for (const fileUpload of fileUploads) {
192+
const uploadOptions: AttachmentUploadOptions = {
193+
// default is false
194+
disableMultipleUploads: false,
195+
// define mime types
196+
supportedMediaTypes: ["image/jpg", "image/jpeg"]
197+
handleAttachmentSelection: attachmentSelectionHandler,
198+
}
199+
200+
const attachmentSelectionHandler: AttachmentSelectionHandler = async (uploadTasks) => {
201+
for (const task of uploadTasks) {
195202
try {
196-
const { name, url, extension } = await uploadFileToAzureBlob(fileUpload);
197-
fileUpload.notifyUploadCompleted({ name, extension, url });
203+
const uniqueFileName = `${v4()}-${task.file?.name}`;
204+
const url = await uploadFileToAzureBlob(task);
205+
task.notifyUploadCompleted(uniqueFileName, url);
198206
} catch (error) {
199207
if (error instanceof Error) {
200-
fileUpload.notifyUploadFailed(error.message);
208+
task.notifyUploadFailed(error.message);
201209
}
202210
}
203211
}
204212
}
205213

206-
const uploadFileToAzureBlob = async (fileUpload: FileUploadManager) => {
214+
const uploadFileToAzureBlob = async (uploadTask: AttachmentUploadTask) => {
207215
// You need to handle the file upload here and upload it to Azure Blob Storage.
208216
// This is how you can configure the upload
209217
// Optionally, you can also update the file upload progress.
210-
fileUpload.notifyUploadProgressChanged(0.2);
218+
uploadTask.notifyUploadProgressChanged(0.2);
211219
return {
212-
name: 'SampleFile.jpg', // File name displayed during download
213220
url: 'https://sample.com/sample.jpg', // Download URL of the file.
214-
extension: 'jpeg' // File extension used for file icon during download.
215221
};
216222

217-
const fileDownloadHandler: FileDownloadHandler = async (userId, fileData) => {
218-
return new URL(fileData.url);
219-
}
220-
};
221-
}
222-
223223
```
224224
225225
## Configure upload method to use Azure Blob storage
@@ -229,10 +229,10 @@ To enable Azure Blob Storage upload, we modify the `uploadFileToAzureBlob` metho
229229
`App.tsx`
230230
231231
```javascript
232-
const uploadFileToAzureBlob = async (fileUpload: FileUploadManager) => {
233-
const file = fileUpload.file;
232+
const uploadFileToAzureBlob = async (uploadTask: AttachmentUploadTask) => {
233+
const file = uploadTask.file;
234234
if (!file) {
235-
throw new Error("fileUpload.file is undefined");
235+
throw new Error("uploadTask.file is undefined");
236236
}
237237

238238
const filename = file.name;
@@ -258,16 +258,14 @@ const uploadFileToAzureBlob = async (fileUpload: FileUploadManager) => {
258258
data: formData,
259259
onUploadProgress: (p) => {
260260
// Optionally, you can update the file upload progess.
261-
fileUpload.notifyUploadProgressChanged(p.loaded / p.total);
261+
uploadTask.notifyUploadProgressChanged(p.loaded / p.total);
262262
},
263263
});
264264

265265
const storageBaseUrl = "https://<YOUR_STORAGE_ACCOUNT>.blob.core.windows.net";
266266

267267
return {
268-
name: filename,
269268
url: `${storageBaseUrl}/${username}/${filename}`,
270-
extension: fileExtension,
271269
};
272270
};
273271
```
@@ -278,50 +276,92 @@ When an upload fails, the UI Library Chat Composite displays an error message.
278276
279277
![File Upload Error Bar](./media/file-too-big.png "Screenshot that shows the File Upload Error Bar.")
280278
281-
Here's sample code showcasing how you can fail an upload due to a size validation error by changing the `fileUploadHandler`:
279+
Here's sample code showcasing how you can fail an upload due to a size validation error:
282280
283281
`App.tsx`
284282
285283
```javascript
286-
import { FileUploadHandler } from from '@azure/communication-react';
284+
import { AttachmentSelectionHandler } from from '@azure/communication-react';
287285

288-
const fileUploadHandler: FileUploadHandler = async (userId, fileUploads) => {
289-
for (const fileUpload of fileUploads) {
290-
if (fileUpload.file && fileUpload.file.size > 99 * 1024 * 1024) {
286+
const attachmentSelectionHandler: AttachmentSelectionHandler = async (uploadTasks) => {
287+
for (const task of uploadTasks) {
288+
if (task.file && task.file.size > 99 * 1024 * 1024) {
291289
// Notify ChatComposite about upload failure.
292290
// Allows you to provide a custom error message.
293-
fileUpload.notifyUploadFailed('File too big. Select a file under 99 MB.');
291+
task.notifyUploadFailed('File too big. Select a file under 99 MB.');
294292
}
295293
}
296294
}
295+
296+
export const attachmentUploadOptions: AttachmentUploadOptions = {
297+
handleAttachmentSelection: attachmentSelectionHandler
298+
};
297299
```
298300
299301
## File downloads - advanced usage
300302
301-
By default, the file `url` provided through `notifyUploadCompleted` method is used to trigger a file download. However, if you need to handle a download in a different way, you can provide a custom `downloadHandler` to ChatComposite. Next, we modify the `fileDownloadHandler` that we declared previously to check for an authorized user before allowing to download the file.
303+
By default, the UI library will open a new tab pointing to the URL you have set when you `notifyUploadCompleted`. Alternatively, you can have a custom logic to handle attachment downloads via `actionsForAttachment`. Let's take a look of an example.
302304
303305
`App.tsx`
304306
305307
```javascript
306-
import { FileDownloadHandler } from "communication-react";
308+
import { AttachmentDownloadOptions } from "communication-react";
309+
310+
const downloadOptions: AttachmentDownloadOptions = {
311+
actionsForAttachment: handler
312+
}
307313

308-
const isUnauthorizedUser = (userId: string): boolean => {
309-
// You need to write your own logic here for this example.
314+
const handler = async (attachment: AttachmentMetadata, message?: ChatMessage) => {
315+
// here we are returning a static action for all attachments and all messages
316+
// alternately, you can provide custom menu actions based on properties in `attachment` or `message`
317+
return [defaultAttachmentMenuAction];
310318
};
311319

312-
const fileDownloadHandler: FileDownloadHandler = async (userId, fileData) => {
313-
if (isUnauthorizedUser(userId)) {
314-
// Error message is displayed to the user.
315-
return { errorMessage: "You don’t have permission to download this file." };
316-
} else {
317-
// If this function returns a Promise that resolves a URL string,
318-
// the URL is opened in a new tab.
319-
return new URL(fileData.url);
320+
const customHandler = = async (attachment: AttachmentMetadata, message?: ChatMessage) => {
321+
if (attachment.extension === "pdf") {
322+
return [
323+
{
324+
title: "Custom button",
325+
icon: (<i className="custom-icon"></i>),
326+
onClick: () => {
327+
return new Promise((resolve, reject) => {
328+
// custom logic here
329+
window.alert("custom button clicked");
330+
resolve();
331+
// or to reject("xxxxx") with a custom message
332+
})
333+
}
334+
},
335+
defaultAttachmentMenuAction
336+
];
337+
} else if (message?.senderId === "user1") {
338+
return [
339+
{
340+
title: "Custom button 2",
341+
icon: (<i className="custom-icon-2"></i>),
342+
onClick: () => {
343+
return new Promise((resolve, reject) => {
344+
window.alert("custom button 2 clicked");
345+
resolve();
346+
})
347+
}
348+
},
349+
// you can also override the default action partially
350+
{
351+
...defaultAttachmentMenuAction,
352+
onClick: () => {
353+
return new Promise((resolve, reject) => {
354+
window.alert("default button clicked");
355+
resolve();
356+
})
357+
}
358+
}
359+
];
320360
}
321-
};
361+
}
322362
```
323363
324-
Download errors are displayed to users in an error bar on top of the Chat Composite.
364+
If there were any issues during the download and the user needs to be notified, we can just `throw` an error with a message in the `onClick` function then the message would be shown in the error bar on top of the Chat Composite.
325365
326366
![File Download Error](./media/download-error.png "Screenshot that shows the File Download Error.")
327367
66.5 KB
Loading

0 commit comments

Comments
 (0)