Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "patch",
"area": "fix",
"workstream": "Inline Image",
"comment": "Fixed the issue where image placeholder being shown when fetching failed",
"packageName": "@azure/communication-react",
"email": "109105353+jpeng-ms@users.noreply.github.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "patch",
"area": "fix",
"workstream": "Inline Image",
"comment": "Fixed the issue where image placeholder being shown when fetching failed",
"packageName": "@azure/communication-react",
"email": "109105353+jpeng-ms@users.noreply.github.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ describe('ResourceDownloadQueue api functions', () => {

/* @conditional-compile-remove(teams-inline-images-and-file-sharing) */
describe('ResourceDownloadQueue api functions', () => {
// URL.createObjectURL is not available in jest-dom
// so we need to mock it in tests
if (typeof URL.createObjectURL === 'undefined') {
Object.defineProperty(window.URL, 'createObjectURL', {
value: () => {
return 'http://mocked-url';
}
});
}
test('should add a message to the queue and contains message', () => {
const context = new ChatContext();
const tokenCredential = stubCommunicationTokenCredential();
Expand Down
52 changes: 37 additions & 15 deletions packages/chat-stateful-client/src/ResourceDownloadQueue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,19 +58,14 @@ export class ResourceDownloadQueue {
continue;
}

try {
if (options) {
const singleUrl = options.singleUrl;
message = await this.downloadSingleUrl(message, singleUrl, operation);
} else {
message = await this.downloadAllPreviewUrls(message, operation);
}
this._context.setChatMessage(threadId, message);
} catch (error) {
console.log('Downloading Resource error: ', error);
} finally {
this.isActive = false;
if (options) {
Comment thread
emlynmac marked this conversation as resolved.
const singleUrl = options.singleUrl;
message = await this.downloadSingleUrl(message, singleUrl, operation);
} else {
message = await this.downloadAllPreviewUrls(message, operation);
}
this._context.setChatMessage(threadId, message);
this.isActive = false;
Comment thread
emlynmac marked this conversation as resolved.
}
}

Expand All @@ -79,7 +74,7 @@ export class ResourceDownloadQueue {
resourceUrl: string,
operation: ImageRequest
): Promise<ChatMessageWithStatus> {
const blobUrl = await operation(resourceUrl, this._credential);
const blobUrl = await this.downloadResource(operation, resourceUrl);
message = { ...message, resourceCache: { ...message.resourceCache, [resourceUrl]: blobUrl } };
return message;
}
Expand All @@ -95,14 +90,24 @@ export class ResourceDownloadQueue {
}
for (const attachment of attachments) {
if (attachment.previewUrl && attachment.attachmentType === 'image') {
const blobUrl = await operation(attachment.previewUrl, this._credential);
const blobUrl = await this.downloadResource(operation, attachment.previewUrl);
message.resourceCache[attachment.previewUrl] = blobUrl;
}
}
}

return message;
}

private async downloadResource(operation: ImageRequest, url: string): Promise<string> {
let blobUrl = URL.createObjectURL(new Blob());
try {
blobUrl = await operation(url, this._credential);
} catch (error) {
console.log('Downloading Resource error: ', error);
Comment thread
emlynmac marked this conversation as resolved.
}
return blobUrl;
}
}
/* @conditional-compile-remove(teams-inline-images-and-file-sharing) */
/**
Expand Down Expand Up @@ -141,11 +146,28 @@ export const fetchImageSource = async (src: string, credential: CommunicationTok
const headers = new Headers();
headers.append('Authorization', `Bearer ${token}`);
try {
return await fetch(url, { headers });
return await fetchWithTimeout(url, { headers });
} catch (err) {
throw new ChatError('ChatThreadClient.getMessage', err as Error);
}
}
async function fetchWithTimeout(
resource: string | URL | Request,
options: { timeout?: number; headers?: Headers }
): Promise<Response> {
// default timeout is 4 seconds
Comment thread
jpeng-ms marked this conversation as resolved.
Outdated
const { timeout = 4000 } = options;

const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);

const response = await fetch(resource, {
...options,
signal: controller.signal
});
clearTimeout(id);
return response;
}
const accessToken = await credential.getToken();
const response = await fetchWithAuthentication(src, accessToken.token);
const blob = await response.blob();
Expand Down